109 lines
2.8 KiB
TypeScript
109 lines
2.8 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { PERMS } from "@/lib/permission-slugs";
|
|
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
|
|
|
|
const { markReadWhere, rconSend } = vi.hoisted(() => ({
|
|
markReadWhere: vi.fn(),
|
|
rconSend: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/lib/db", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("@/lib/db")>();
|
|
return {
|
|
...actual,
|
|
db: {
|
|
...actual.db,
|
|
update: vi.fn(() => ({
|
|
set: vi.fn(() => ({ where: markReadWhere })),
|
|
})),
|
|
},
|
|
};
|
|
});
|
|
|
|
vi.mock("@/lib/services/rcon", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("@/lib/services/rcon")>();
|
|
return {
|
|
...actual,
|
|
rcon: { send: rconSend },
|
|
};
|
|
});
|
|
|
|
import { systemMutationService } from "./mutations";
|
|
|
|
function capabilityContext(
|
|
granted: readonly string[],
|
|
): HousekeepingCapabilityContext {
|
|
const permissions = new Set(granted);
|
|
return {
|
|
actor: { id: 42, username: "operator", rank: 500 },
|
|
isSuperAdmin: false,
|
|
has: (slug) => permissions.has(slug),
|
|
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
|
|
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
|
|
};
|
|
}
|
|
|
|
describe("System production mutation failures", () => {
|
|
beforeEach(() => {
|
|
markReadWhere.mockReset();
|
|
rconSend.mockReset();
|
|
});
|
|
|
|
it("maps a failed alert broadcast to dependency unavailable", async () => {
|
|
rconSend.mockResolvedValue(false);
|
|
|
|
const result = await systemMutationService.execute(
|
|
{
|
|
capability: capabilityContext([PERMS.NOTIFICATIONS_EDIT]),
|
|
correlationId: "broadcast-failure",
|
|
},
|
|
"operations.alert.broadcast",
|
|
{ message: "Hotel notice" },
|
|
);
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: { code: "DEPENDENCY_UNAVAILABLE" },
|
|
correlationId: "broadcast-failure",
|
|
});
|
|
});
|
|
|
|
it("maps a thrown alert broadcast to dependency unavailable", async () => {
|
|
rconSend.mockRejectedValue(new Error("RCON unavailable"));
|
|
|
|
const result = await systemMutationService.execute(
|
|
{
|
|
capability: capabilityContext([PERMS.NOTIFICATIONS_EDIT]),
|
|
correlationId: "broadcast-error",
|
|
},
|
|
"operations.alert.broadcast",
|
|
{ message: "Hotel notice" },
|
|
);
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: { code: "DEPENDENCY_UNAVAILABLE" },
|
|
correlationId: "broadcast-error",
|
|
});
|
|
});
|
|
|
|
it("maps mark-read persistence failure to dependency unavailable", async () => {
|
|
markReadWhere.mockRejectedValue(new Error("database unavailable"));
|
|
|
|
const result = await systemMutationService.execute(
|
|
{
|
|
capability: capabilityContext([PERMS.NOTIFICATIONS_VIEW]),
|
|
correlationId: "mark-read-error",
|
|
},
|
|
"operations.alerts.mark-read",
|
|
{},
|
|
);
|
|
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: { code: "DEPENDENCY_UNAVAILABLE" },
|
|
correlationId: "mark-read-error",
|
|
});
|
|
});
|
|
});
|