feat(housekeeping): deliver people account workflows
This commit is contained in:
+104
@@ -0,0 +1,104 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
|
||||
|
||||
const { audit, disconnectUser, insertValues, selectLimit } = vi.hoisted(() => ({
|
||||
audit: vi.fn(),
|
||||
disconnectUser: vi.fn(),
|
||||
insertValues: vi.fn(),
|
||||
selectLimit: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/auth", () => ({ invalidateLoginCache: vi.fn() }));
|
||||
vi.mock("@/lib/auth/password", () => ({ hashPassword: vi.fn() }));
|
||||
vi.mock("@/lib/db", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@/lib/db")>();
|
||||
return {
|
||||
...actual,
|
||||
db: {
|
||||
...actual.db,
|
||||
select: vi.fn(() => ({
|
||||
from: vi.fn(() => ({
|
||||
where: vi.fn(() => ({ limit: selectLimit })),
|
||||
})),
|
||||
})),
|
||||
insert: vi.fn(() => ({ values: insertValues })),
|
||||
},
|
||||
};
|
||||
});
|
||||
vi.mock("@/lib/services/audit", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@/lib/services/audit")>()),
|
||||
logAudit: audit,
|
||||
}));
|
||||
vi.mock("@/lib/services/rcon", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@/lib/services/rcon")>()),
|
||||
rcon: { disconnectUser },
|
||||
}));
|
||||
vi.mock("@/lib/services/webhook", () => ({ notify: vi.fn() }));
|
||||
|
||||
import { peopleMutationService } from "./mutations";
|
||||
|
||||
function capabilityContext(
|
||||
granted: readonly string[],
|
||||
): HousekeepingCapabilityContext {
|
||||
const permissions = new Set(granted);
|
||||
return {
|
||||
actor: { id: 42, username: "operator", rank: 6 },
|
||||
isSuperAdmin: false,
|
||||
has: (slug) => permissions.has(slug),
|
||||
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
|
||||
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
selectLimit.mockResolvedValue([
|
||||
{
|
||||
id: 7,
|
||||
username: "Alice",
|
||||
mail: "[email protected]",
|
||||
rank: 2,
|
||||
motto: "Ready",
|
||||
credits: 100,
|
||||
pixels: 50,
|
||||
online: "1",
|
||||
},
|
||||
]);
|
||||
insertValues.mockResolvedValue([{}]);
|
||||
disconnectUser.mockResolvedValue(true);
|
||||
audit.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
describe("People production reason audit", () => {
|
||||
it("persists the nonblank sanction reason with before and after evidence", async () => {
|
||||
const result = await peopleMutationService.execute(
|
||||
{
|
||||
capability: capabilityContext([PERMS.USERS_BAN]),
|
||||
correlationId: "production-ban",
|
||||
},
|
||||
"user.ban",
|
||||
{
|
||||
userId: 7,
|
||||
reason: "Repeated harassment",
|
||||
duration: 24,
|
||||
type: "account",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({ ok: true });
|
||||
expect(audit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
action: "people.user.ban",
|
||||
before: expect.objectContaining({ id: 7, banned: false }),
|
||||
after: expect.objectContaining({
|
||||
id: 7,
|
||||
banned: true,
|
||||
reason: "Repeated harassment",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(audit.mock.calls[0]?.[0]?.before).not.toHaveProperty("mail");
|
||||
expect(audit.mock.calls[0]?.[0]?.after).not.toHaveProperty("mail");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user