101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { PERMS } from "@/lib/permission-slugs";
|
|
import type { HousekeepingCapabilityContext } from "../../foundation/contracts";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
execute: vi.fn(),
|
|
fetchTicketQueueOpenCounts: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/lib/admin/ticket-queue-counts", () => ({
|
|
fetchTicketQueueOpenCounts: mocks.fetchTicketQueueOpenCounts,
|
|
}));
|
|
vi.mock("@/lib/db", () => {
|
|
return { db: { execute: mocks.execute } };
|
|
});
|
|
|
|
import { PEOPLE_WIDGETS } from "./widgets";
|
|
|
|
function context(granted: readonly string[]): HousekeepingCapabilityContext {
|
|
const permissions = new Set(granted);
|
|
return {
|
|
actor: { id: 42, username: "operator", rank: 4 },
|
|
isSuperAdmin: false,
|
|
has: (slug) => permissions.has(slug),
|
|
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
|
|
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
|
|
};
|
|
}
|
|
|
|
async function load(granted: readonly string[]) {
|
|
return PEOPLE_WIDGETS[0].load(
|
|
context(granted),
|
|
new AbortController().signal,
|
|
);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mocks.fetchTicketQueueOpenCounts.mockResolvedValue({
|
|
cmsOpen: 1,
|
|
helpOpen: 2,
|
|
});
|
|
});
|
|
|
|
describe("People production queue widget", () => {
|
|
it("wires ticket-only, CFH-only, ban-only, mixed, and denied contexts without unauthorized reads", async () => {
|
|
await expect(load([PERMS.MOD_TICKETS_VIEW])).resolves.toMatchObject({
|
|
ok: true,
|
|
data: { tickets: 1, helpTickets: 2, cfh: 0, activeBans: 0 },
|
|
});
|
|
expect(mocks.fetchTicketQueueOpenCounts).toHaveBeenCalledTimes(1);
|
|
expect(mocks.execute).not.toHaveBeenCalled();
|
|
|
|
vi.clearAllMocks();
|
|
mocks.execute.mockResolvedValueOnce([[{ total: 3 }], []]);
|
|
await expect(load([PERMS.MOD_CFH_VIEW])).resolves.toMatchObject({
|
|
ok: true,
|
|
data: { tickets: 0, helpTickets: 0, cfh: 3, activeBans: 0 },
|
|
});
|
|
expect(mocks.fetchTicketQueueOpenCounts).not.toHaveBeenCalled();
|
|
expect(mocks.execute).toHaveBeenCalledTimes(1);
|
|
|
|
vi.clearAllMocks();
|
|
mocks.execute.mockResolvedValueOnce([[{ total: 4 }], []]);
|
|
await expect(load([PERMS.MOD_BANS_VIEW])).resolves.toMatchObject({
|
|
ok: true,
|
|
data: { tickets: 0, helpTickets: 0, cfh: 0, activeBans: 4 },
|
|
});
|
|
expect(mocks.fetchTicketQueueOpenCounts).not.toHaveBeenCalled();
|
|
expect(mocks.execute).toHaveBeenCalledTimes(1);
|
|
|
|
vi.clearAllMocks();
|
|
mocks.fetchTicketQueueOpenCounts.mockResolvedValue({
|
|
cmsOpen: 1,
|
|
helpOpen: 2,
|
|
});
|
|
mocks.execute
|
|
.mockResolvedValueOnce([[{ total: 3 }], []])
|
|
.mockResolvedValueOnce([[{ total: 4 }], []]);
|
|
const mixed = await load([
|
|
PERMS.MOD_TICKETS_VIEW,
|
|
PERMS.MOD_CFH_VIEW,
|
|
PERMS.MOD_BANS_VIEW,
|
|
]);
|
|
expect(mocks.fetchTicketQueueOpenCounts).toHaveBeenCalledTimes(1);
|
|
expect(mocks.execute).toHaveBeenCalledTimes(2);
|
|
expect(mixed).toMatchObject({
|
|
ok: true,
|
|
data: { tickets: 1, helpTickets: 2, cfh: 3, activeBans: 4 },
|
|
});
|
|
|
|
vi.clearAllMocks();
|
|
await expect(load([])).resolves.toMatchObject({
|
|
ok: false,
|
|
error: { code: "FORBIDDEN" },
|
|
});
|
|
expect(mocks.fetchTicketQueueOpenCounts).not.toHaveBeenCalled();
|
|
expect(mocks.execute).not.toHaveBeenCalled();
|
|
});
|
|
});
|