99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const actionState = vi.hoisted(() => ({
|
|
selected: [] as unknown[][],
|
|
inserted: [] as unknown[],
|
|
deleted: 0,
|
|
authorized: 0,
|
|
failAudit: false,
|
|
}));
|
|
|
|
vi.mock("@/lib/auth/require-admin", () => ({
|
|
requireAdminSession: async () => {
|
|
actionState.authorized += 1;
|
|
return { email: "admin@example.test", name: "Admin" };
|
|
},
|
|
}));
|
|
|
|
vi.mock("next/headers", () => ({ headers: async () => new Headers() }));
|
|
vi.mock("next/navigation", () => ({
|
|
redirect: (path: string) => {
|
|
throw new Error(`REDIRECT:${path}`);
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/lib/database", () => {
|
|
function selection(response: unknown[]) {
|
|
const chain: Record<string, unknown> = {};
|
|
for (const method of ["from", "where", "orderBy"]) chain[method] = () => chain;
|
|
chain.limit = () => Promise.resolve(response);
|
|
chain.then = (resolve: (value: unknown[]) => unknown, reject: (reason: unknown) => unknown) =>
|
|
Promise.resolve(response).then(resolve, reject);
|
|
return chain;
|
|
}
|
|
const tx = {
|
|
execute: async () => undefined,
|
|
select: () => selection(actionState.selected.shift() ?? []),
|
|
delete: () => ({ where: async () => { actionState.deleted += 1; } }),
|
|
insert: () => ({
|
|
values: async (value: unknown) => {
|
|
if (actionState.failAudit && !Array.isArray(value)) throw new Error("audit unavailable");
|
|
actionState.inserted.push(value);
|
|
},
|
|
}),
|
|
};
|
|
return { db: { transaction: async (callback: (transaction: typeof tx) => Promise<unknown>) => callback(tx) } };
|
|
});
|
|
|
|
import { replaceGroupSchedule } from "./actions";
|
|
|
|
function scheduleForm() {
|
|
const formData = new FormData();
|
|
formData.set("groupId", "11111111-1111-4111-8111-111111111111");
|
|
formData.append("startMinuteOfWeek", "6960");
|
|
formData.append("endMinuteOfWeek", "7200");
|
|
return formData;
|
|
}
|
|
|
|
describe("replaceGroupSchedule", () => {
|
|
beforeEach(() => {
|
|
actionState.selected = [
|
|
[{ id: "11111111-1111-4111-8111-111111111111", name: "Friday friends" }],
|
|
[{ startMinuteOfWeek: 480, endMinuteOfWeek: 540 }],
|
|
];
|
|
actionState.inserted = [];
|
|
actionState.deleted = 0;
|
|
actionState.authorized = 0;
|
|
actionState.failAudit = false;
|
|
});
|
|
|
|
it("reauthorizes and replaces all windows with an audit in one transaction", async () => {
|
|
await expect(replaceGroupSchedule(scheduleForm())).rejects.toThrow("REDIRECT:/admin/groups/11111111-1111-4111-8111-111111111111?saved=schedule");
|
|
expect(actionState.authorized).toBe(1);
|
|
expect(actionState.deleted).toBe(1);
|
|
expect(actionState.inserted[0]).toEqual([{
|
|
groupId: "11111111-1111-4111-8111-111111111111",
|
|
startMinuteOfWeek: 6960,
|
|
endMinuteOfWeek: 7200,
|
|
}]);
|
|
expect(actionState.inserted[1]).toEqual(expect.objectContaining({
|
|
type: "games.minecraft.account-manager.group.schedule-updated",
|
|
data: expect.objectContaining({
|
|
previousWindows: [{ startMinuteOfWeek: 480, endMinuteOfWeek: 540 }],
|
|
windows: [{ startMinuteOfWeek: 6960, endMinuteOfWeek: 7200 }],
|
|
adminEmail: "admin@example.test",
|
|
}),
|
|
}));
|
|
});
|
|
|
|
it("does not report success when the atomic audit write fails", async () => {
|
|
actionState.failAudit = true;
|
|
await expect(replaceGroupSchedule(scheduleForm())).rejects.toThrow("audit unavailable");
|
|
expect(actionState.inserted).toEqual([[{
|
|
groupId: "11111111-1111-4111-8111-111111111111",
|
|
startMinuteOfWeek: 6960,
|
|
endMinuteOfWeek: 7200,
|
|
}]]);
|
|
});
|
|
});
|