import { describe, it, expect, beforeAll, afterEach, vi } from "vitest"; import config from "@payload-config"; import { getPayload, type Payload } from "payload"; import type { Map as MapDoc, Resource } from "@/payload-types"; import { checkResourcesInRange } from "@/lib/map/placement"; import { nodeIconOption } from "@/lib/map/nodeIcons"; import { saveMapNode } from "@/app/(frontend)/logistics/map/actions"; vi.mock("next/headers", () => ({ headers: async () => new Headers(), })); describe("generic marker nodes (no resources)", () => { let payload: Payload; let map: MapDoc; let copper: Resource; let authSpy: ReturnType; let mapper: { id: number }; beforeAll(async () => { payload = await getPayload({ config }); map = (await payload.create({ collection: "maps", data: { name: `Generic Node Map ${Date.now()}`, worldSizeWidth: 8192, worldSizeHeight: 8192, basemapMode: "tiles", tileUrlTemplate: "http://localhost/{z}/{x}/{y}.png", isActive: true, worldMap: false, }, overrideAccess: true, depth: 0, })) as unknown as MapDoc; const mapperRole = (await payload.create({ collection: "roles", data: { name: `Generic Node Mapper ${Date.now()}`, slug: `generic-mapper-${Date.now()}`, permissions: ["resource-nodes:author"], }, overrideAccess: true, depth: 0, })) as unknown as { id: number }; mapper = (await payload.create({ collection: "users", data: { username: `generic-node-mapper-${Date.now()}`, discordUsername: `generic-node-mapper-${Date.now()}`, displayName: "Generic Node Mapper", steamId: `generic-node-${Date.now()}`, password: "generic-node-pass", roleDocs: [mapperRole.id], }, overrideAccess: true, depth: 0, })) as unknown as { id: number }; copper = (await payload.create({ collection: "resources", data: { name: `Copper ${Date.now()}`, codeName: `generic_copper_${Date.now()}`, approvalStatus: "in_progress", type: "physical", baseValue: 1, rarity: "common", unitOfMeasure: "kg", massPerUnit: 1, gridWidth: 1, gridHeight: 1, }, overrideAccess: true, depth: 0, })) as unknown as Resource; }); it("persists a node with an empty resource list", async () => { const node = await payload.create({ collection: "resource-nodes", data: { name: "Landmark marker", map: map.id, resources: [], position: [1000, 1000], }, overrideAccess: true, depth: 0, }); expect(node.resources ?? []).toEqual([]); }); it("checkResourcesInRange never matches resource-less nodes", () => { const requirement = [{ resource: copper.id, range: 500 }]; const genericNode = { id: 1, position: [1000, 1000] as [number, number], resources: [] }; const realNode = { id: 2, position: [1200, 1000] as [number, number], resources: [copper.id] }; const withGenericOnly = checkResourcesInRange([genericNode], requirement, [1100, 1000]); expect(withGenericOnly.allowed).toBe(false); const withRealNode = checkResourcesInRange([genericNode, realNode], requirement, [1100, 1000]); expect(withRealNode.allowed).toBe(true); }); it("saveMapNode persists a chosen icon and rejects unknown ones", async () => { authSpy = vi.spyOn(payload, "auth").mockResolvedValue({ user: mapper as never, } as never); try { const flagged = await saveMapNode({ mapId: map.id, name: "Flag marker", resourceIds: [], position: [3000, 3000], icon: "flag", }); expect(flagged.success).toBe(true); const flaggedNode = await payload.findByID({ collection: "resource-nodes", id: (flagged.data as { id: number }).id, depth: 0, overrideAccess: true, }); expect(flaggedNode.icon).toBe("flag"); const rejected = await saveMapNode({ mapId: map.id, name: "Evil marker", resourceIds: [], position: [3000, 3000], icon: "rocket", }); expect(rejected.success).toBe(false); expect(rejected.error).toContain("icon"); } finally { authSpy.mockRestore(); } }); it("nodeIconOption falls back to the default marker", () => { expect(nodeIconOption("flag").value).toBe("flag"); expect(nodeIconOption("nonsense").value).toBe("marker"); expect(nodeIconOption(null).value).toBe("marker"); }); it("saveMapNode accepts an empty resource list as a plain marker", async () => { authSpy = vi.spyOn(payload, "auth").mockResolvedValue({ user: mapper as never, } as never); try { const result = await saveMapNode({ mapId: map.id, name: "Authored marker", resourceIds: [], position: [2000, 2000], richness: 100, }); expect(result.success).toBe(true); const created = await payload.findByID({ collection: "resource-nodes", id: (result.data as { id: number }).id, depth: 0, overrideAccess: true, }); expect(created.resources ?? []).toEqual([]); } finally { authSpy.mockRestore(); } }); });