import { revalidatePath } from "next/cache"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { requirePermission } from "@/lib/admin/guard"; const { execute, staff } = vi.hoisted(() => ({ execute: vi.fn(), staff: { id: 1, rank: 7, username: "admin" }, })); vi.mock("@/features/housekeeping/domains/people/services/mutations", () => ({ createPeopleMutationInvocation: vi.fn((staff, correlationId) => ({ expectedActorId: staff.id, correlationId, legacy: true, })), peopleMutationService: { execute }, })); vi.mock("@/lib/admin/guard", () => ({ requirePermission: vi.fn() })); vi.mock("@/lib/permissions", () => ({ PERMS: { USERS_EDIT: "admin.users.edit", USERS_BAN: "admin.users.ban", USERS_RESET_PASSWORD: "admin.users.reset_password", WORDFILTER_EDIT: "admin.wordfilter.edit", }, })); vi.mock("@/lib/safe-action", () => ({ adminAction: (_options: unknown, handler: (context: unknown) => unknown) => (data: unknown) => handler({ data, session: { user: staff } }), })); vi.mock("@/lib/safe-action-shared", () => ({ ActionError: class ActionError extends Error { constructor(message: string) { super(message); this.name = "ActionError"; } }, actionOk: (data?: unknown) => ({ ok: true, data: data ?? {} }), actionError: (error: string) => ({ ok: false, error }), })); vi.mock("@/lib/auth/password", () => ({ hashPassword: vi.fn() })); vi.mock("@/lib/db", () => ({ db: {}, User: {}, UsersBadges: {}, UsersCurrency: {}, UsersSettings: {}, })); vi.mock("@/lib/services/audit", () => ({ logAudit: vi.fn() })); vi.mock("@/lib/services/rcon", () => ({ rcon: {} })); vi.mock("@/lib/services/webhook", () => ({ notify: vi.fn() })); vi.mock("next/cache", () => ({ revalidatePath: vi.fn() })); import { dismissApplication } from "./admin-applications"; import { addWord, deleteWord } from "./admin-wordfilter"; import { banUser, resetPassword, updateUser } from "./users"; const form = (data: Record) => ({ get: (key: string) => data[key] ?? null }) as FormData; beforeEach(() => { vi.clearAllMocks(); vi.mocked(requirePermission).mockResolvedValue(staff); execute.mockImplementation(async (context, operation) => ({ ok: true, data: { before: {}, after: operation === "word-filter.update" ? { id: 12 } : {}, output: operation === "user.reset-password" ? { newPassword: "temporary-password" } : undefined, }, correlationId: context.correlationId, })); }); describe("legacy user safe-action wrappers", () => { it("preserves exact ACL-specific service delegation", async () => { await (updateUser as never as (input: unknown) => Promise)({ id: 7, motto: "Ready", }); await (banUser as never as (input: unknown) => Promise)({ userId: 7, reason: "abuse", duration: 0, type: "account", }); const reset = await ( resetPassword as never as (input: unknown) => Promise<{ ok: boolean; data: { newPassword: string }; }> )({ userId: 7 }); expect(execute.mock.calls.map((call) => call[1])).toEqual([ "user.update", "user.ban", "user.reset-password", ]); expect(execute.mock.calls.map((call) => call[0].expectedActorId)).toEqual([ 1, 1, 1, ]); expect(reset.data.newPassword).toBe("temporary-password"); }); }); describe("legacy application and word-filter wrappers", () => { it("keeps tolerant application dismissal and ASE revalidation", async () => { await dismissApplication(form({ id: "9" })); expect(execute).toHaveBeenCalledWith( expect.objectContaining({ expectedActorId: 1 }), "application.decide", { applicationId: "9", decision: "dismiss" }, ); expect(revalidatePath).toHaveBeenCalledWith( "/ase/people/staff/applications", ); }); it("preserves application and wordfilter IDs above Number.MAX_SAFE_INTEGER", async () => { await dismissApplication(form({ id: "9007199254740993" })); await expect(deleteWord({ id: "9007199254740993" })).resolves.toEqual({ ok: true, data: {}, }); expect(execute.mock.calls.slice(-2).map((call) => call[2])).toEqual([ { applicationId: "9007199254740993", decision: "dismiss" }, { action: "delete", id: "9007199254740993" }, ]); }); it("preserves word-filter ActionResult shapes and ASE revalidation", async () => { await expect(addWord({ word: "spam" })).resolves.toEqual({ ok: true, data: { id: "12" }, }); await expect(deleteWord({ id: "12" })).resolves.toEqual({ ok: true, data: {}, }); expect(execute.mock.calls.slice(-2).map((call) => call[2])).toEqual([ { action: "add", word: "spam" }, { action: "delete", id: "12" }, ]); expect(revalidatePath).toHaveBeenCalledWith( "/ase/people/moderation/word-filter", ); }); });