1
0
Fork 0
polaris-task-force/tests/int/generic-node.int.spec.ts
Z8MB1E 9bbe44d38d feat(map): world map picker entry, intel fields, node icons, generic markers
- Maps gain worldMap flag, description, freeform weather, latitude/longitude,
  easeOfAccess and factionControl; picker cards render them as stat tags
- resource-nodes gain a shared icon set and may exist with no resources as
  plain marker nodes (placement ignores marker-only nodes)
- sidebar gains the World Map entry; Infrastructure section gets defaultOpen
- migrations 20260921_120000/123000/130500/134000; includes the generated
  types/schema sweep (currency emblem fields land in the next commit)
2026-09-21 20:47:46 -04:00

178 lines
5.3 KiB
TypeScript

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<typeof vi.spyOn>;
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: ["maps:create"],
},
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();
}
});
});