1
0
Fork 0
polaris-task-force/tests/int/supply-box-actions.int.spec.ts

226 lines
No EOL
7.5 KiB
TypeScript

import { getPayload, Payload } from "payload";
import config from "@/payload.config";
import { afterAll, beforeAll, describe, expect, it, vi, type MockInstance } from "vitest";
import type { ArmaCommand, SupplyBox, User } from "@/payload-types";
import { queueCrateCommand } from "@/app/(frontend)/logistics/supply-boxes/actions";
// Mock next/headers so the action's authenticate() can run outside a request.
vi.mock("next/headers", () => ({
headers: async () => new Headers(),
}));
let payload: Payload;
let authSpy: MockInstance;
const RUN = `sbx-${Date.now().toString(36)}`;
// ---------------------------------------------------------------------------
// Fixture tracking for cleanup
// ---------------------------------------------------------------------------
const userIds: number[] = [];
const roleIds: number[] = [];
const syncEventIds: number[] = [];
const boxIds: number[] = [];
const commandIds: number[] = [];
let serverId: number;
let commandUser: User;
let plainUser: User;
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function makeUser(label: string, roleDocIds?: number[]): Promise<User> {
const user = (await payload.create({
collection: "users",
data: {
username: `${RUN}-${label}`,
discordUsername: `${RUN}-${label}-discord`,
displayName: `${RUN} ${label}`,
steamId: `${RUN}-steam-${label}`,
password: "Test1234",
roles: ["user"],
...(roleDocIds && roleDocIds.length > 0 ? { roleDocs: roleDocIds } : {}),
},
overrideAccess: true,
depth: 0,
})) as unknown as User;
userIds.push(user.id);
return user;
}
// Creates a supply box directly (the crate-sync handler is covered by
// supply-box-sync.int.spec.ts) with a real arma-sync-events row as the
// required sourceEvent provenance anchor.
async function makeBox(boxId: string): Promise<SupplyBox> {
const raw = await payload.create({
collection: "arma-sync-events",
data: {
eventId: `${RUN}-evt-${boxId}`,
server: serverId,
type: "fixture",
occurredAt: new Date().toISOString(),
payload: [],
},
overrideAccess: true,
depth: 0,
});
syncEventIds.push(raw.id);
const box = (await payload.create({
collection: "supply-boxes",
data: {
boxId,
sessionId: RUN,
server: serverId,
boxClass: "Box_NATO_Wps_F",
displayName: "Ammo Crate",
location: { x: 100, y: 200, z: 0 },
contents: [{ class: "Item_1", count: 10 }],
lastSyncedAt: new Date().toISOString(),
status: "active",
sourceEvent: raw.id,
},
overrideAccess: true,
depth: 0,
})) as unknown as SupplyBox;
boxIds.push(box.id);
return box;
}
async function findCommand(commandId: string): Promise<ArmaCommand | null> {
const found = await payload.find({
collection: "arma-commands",
where: { commandId: { equals: commandId } },
limit: 1,
depth: 0,
overrideAccess: true,
});
return (found.docs[0] as ArmaCommand | undefined) ?? null;
}
// ---------------------------------------------------------------------------
// Setup / teardown
// ---------------------------------------------------------------------------
beforeAll(async () => {
const payloadConfig = await config;
payload = await getPayload({ config: payloadConfig });
authSpy = vi.spyOn(payload, "auth");
const superRole = await payload.create({
collection: "roles",
data: { name: `${RUN} Command`, slug: `${RUN}-command`, isSuperuser: true },
overrideAccess: true,
depth: 0,
});
roleIds.push(superRole.id);
commandUser = await makeUser("command", [superRole.id]);
plainUser = await makeUser("plain");
const server = await payload.create({
collection: "game-servers",
data: { serverId: `${RUN}-srv`, name: `${RUN} Server`, status: "online" },
overrideAccess: true,
depth: 0,
});
serverId = server.id;
});
afterAll(async () => {
if (!payload) return;
for (const id of commandIds) {
await payload.delete({ collection: "arma-commands", id, overrideAccess: true }).catch(() => {});
}
for (const id of boxIds) {
await payload.delete({ collection: "supply-boxes", id, overrideAccess: true }).catch(() => {});
}
for (const id of syncEventIds) {
await payload.delete({ collection: "arma-sync-events", id, overrideAccess: true }).catch(() => {});
}
for (const id of userIds) {
await payload.delete({ collection: "users", id, overrideAccess: true }).catch(() => {});
}
for (const id of roleIds) {
await payload.delete({ collection: "roles", id, overrideAccess: true }).catch(() => {});
}
if (serverId) {
await payload.delete({ collection: "game-servers", id: serverId, overrideAccess: true }).catch(() => {});
}
});
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
describe("supply box crate command actions", () => {
describe("queueCrateCommand", () => {
it("queues a crate.apply command with the contract payload for a qualified user", async () => {
authSpy.mockResolvedValue({ user: commandUser });
const box = await makeBox(`box:${RUN}:apply`);
const result = await queueCrateCommand(box.boxId, "apply", [
[
["Item_1", 10],
["Item_2", 5],
],
]);
expect(result.success).toBe(true);
expect(result.data?.status).toBe("queued");
expect(result.data?.commandId).toMatch(/^crate-[0-9a-f]{24}$/);
const cmd = await findCommand(result.data!.commandId);
expect(cmd).not.toBeNull();
expect(cmd?.type).toBe("crate.apply");
expect(cmd?.server).toBe(serverId);
expect(cmd?.status).toBe("queued");
expect(cmd?.payload).toEqual([
box.boxId,
[
["Item_1", 10],
["Item_2", 5],
],
]);
commandIds.push(cmd!.id);
});
it("rejects a user without the logistics qualification with a forbidden error", async () => {
authSpy.mockResolvedValue({ user: plainUser });
const box = await makeBox(`box:${RUN}:forbidden`);
const result = await queueCrateCommand(box.boxId, "despawn", []);
expect(result.success).toBe(false);
expect(result.error).toContain("forbidden");
});
it("maps an unknown box id to a not-found error", async () => {
authSpy.mockResolvedValue({ user: commandUser });
const result = await queueCrateCommand(`box:${RUN}:missing`, "despawn", []);
expect(result.success).toBe(false);
expect(result.error).toContain("not found");
});
it("mints a unique command id per queue", async () => {
authSpy.mockResolvedValue({ user: commandUser });
const box = await makeBox(`box:${RUN}:unique`);
const first = await queueCrateCommand(box.boxId, "despawn", []);
const second = await queueCrateCommand(box.boxId, "despawn", []);
expect(first.success).toBe(true);
expect(second.success).toBe(true);
expect(first.data?.commandId).not.toBe(second.data?.commandId);
const firstCmd = await findCommand(first.data!.commandId);
const secondCmd = await findCommand(second.data!.commandId);
expect(firstCmd).not.toBeNull();
expect(secondCmd).not.toBeNull();
expect(firstCmd?.id).not.toBe(secondCmd?.id);
expect(firstCmd?.payload).toEqual([box.boxId]);
expect(secondCmd?.payload).toEqual([box.boxId]);
commandIds.push(firstCmd!.id, secondCmd!.id);
});
});
});