Files
Epicnabbo-Catalogus-Updated…/src/actions/housekeeping-command.test.ts
T

113 lines
3.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { z } from "zod";
import { registerHousekeepingCommand } from "@/features/housekeeping/foundation/commands/registry";
import {
anyCapability,
ok,
} from "@/features/housekeeping/foundation/contracts";
import type { AuditEntry } from "@/lib/services/audit";
const { auditEntries, context, rateLimitCalls } = vi.hoisted(() => ({
auditEntries: [] as AuditEntry[],
context: {
actor: { id: 71, username: "server-operator", rank: 4 },
isSuperAdmin: false,
has: (slug: string) => slug === "admin.settings.edit",
hasAny: (...slugs: string[]) => slugs.includes("admin.settings.edit"),
hasAll: (...slugs: string[]) =>
slugs.every((slug) => slug === "admin.settings.edit"),
},
rateLimitCalls: [] as Array<[string, number, number]>,
}));
vi.mock("@/features/housekeeping/foundation/server-capability-context", () => ({
getHousekeepingCapabilityContext: async () => context,
}));
vi.mock("@/lib/services/audit", () => ({
housekeepingAuditWriter: {
write: async (entry: AuditEntry) => {
auditEntries.push({ ...entry });
},
},
}));
vi.mock("@/lib/rate-limit", () => ({
clientIp: async () => "203.0.113.7",
rateLimit: async (key: string, attempts: number, windowMs: number) => {
rateLimitCalls.push([key, attempts, windowMs]);
return { ok: true, retryAfter: 0 };
},
}));
import { executeHousekeepingCommand } from "./housekeeping-command";
registerHousekeepingCommand({
id: "system.server-action.serializable",
owner: "system",
risk: "safe",
capability: anyCapability("admin.settings.edit"),
input: z.object({ value: z.string() }),
requiresReason: false,
rateLimit: { attempts: 5, windowMs: 120_000 },
execute: async (commandContext, input) =>
ok(
{
value: input.value,
actorId: commandContext.capability.actor.id,
ipAddress: commandContext.ipAddress,
},
commandContext.correlationId,
),
});
beforeEach(() => {
auditEntries.length = 0;
rateLimitCalls.length = 0;
});
describe("executeHousekeepingCommand", () => {
it("accepts a plain request and ignores spoofed server-owned metadata", async () => {
const request = {
commandId: "system.server-action.serializable",
input: { value: "saved" },
risk: "sensitive",
owner: "people",
capability: { mode: "any", slugs: ["forged.permission"] },
actor: { id: 999 },
ipAddress: "198.51.100.9",
rateLimit: { attempts: 999, windowMs: 1 },
audit: { action: "forged.action", target: "forged-target" },
};
const result = await executeHousekeepingCommand(request);
expect(result).toMatchObject({
ok: true,
data: {
value: "saved",
actorId: 71,
ipAddress: "203.0.113.7",
},
});
expect(rateLimitCalls).toEqual([
[
"housekeeping-command:71:203.0.113.7:system.server-action.serializable",
5,
120_000,
],
]);
expect(auditEntries).toMatchObject([
{
userId: 71,
action: "system.server-action.serializable",
target: "system",
domain: "system",
ipAddress: "203.0.113.7",
outcome: "success",
},
]);
expect(auditEntries[0]?.correlationId).toBe(result.correlationId);
});
});