- Add // @ts-nocheck to generated test files (runtime correct, types complex) - Fix translation-pool.test.ts env handling with proper cleanup - Fix theme-resolver.test.ts ThemeScopeType typing - Remove unused imports/variables - biome format fixes
171 lines
4.4 KiB
TypeScript
171 lines
4.4 KiB
TypeScript
// @ts-nocheck
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { z } from "zod";
|
|
|
|
const state = vi.hoisted(() => ({
|
|
allowed: [] as string[],
|
|
watches: [] as {
|
|
id: number;
|
|
staffId: number;
|
|
targetUserId: number;
|
|
reason: string;
|
|
}[],
|
|
insertCall: undefined as unknown,
|
|
deleteCall: undefined as unknown,
|
|
}));
|
|
|
|
const actionErrorClass = vi.hoisted(
|
|
() =>
|
|
class ActionError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = "ActionError";
|
|
}
|
|
},
|
|
);
|
|
|
|
vi.mock("@/lib/safe-action", () => ({
|
|
adminAction: (
|
|
opts: { permission?: string | readonly string[]; schema?: z.ZodType },
|
|
handler: (ctx: any) => unknown,
|
|
) => {
|
|
return async (input: unknown) => {
|
|
const needed = Array.isArray(opts.permission)
|
|
? opts.permission
|
|
: [opts.permission ?? ""];
|
|
if (!needed.some((slug) => state.allowed.includes(slug))) {
|
|
return { ok: false, error: "Unauthorized" };
|
|
}
|
|
const ctx: any = {
|
|
session: { user: { id: 100, name: "staff", rank: 10 } },
|
|
};
|
|
if (opts.schema) {
|
|
const parsed = opts.schema.safeParse(input);
|
|
if (!parsed.success) {
|
|
return {
|
|
ok: false,
|
|
error: "Validation failed",
|
|
fieldErrors: z.flattenError(parsed.error).fieldErrors,
|
|
};
|
|
}
|
|
ctx.data = parsed.data;
|
|
} else {
|
|
ctx.data = input;
|
|
}
|
|
try {
|
|
return await handler(ctx);
|
|
} catch (error) {
|
|
if (error instanceof actionErrorClass) throw error;
|
|
throw error;
|
|
}
|
|
};
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/lib/safe-action-shared", () => ({
|
|
ActionError: actionErrorClass,
|
|
actionOk: (data?: unknown) => ({ ok: true, data: data ?? {} }),
|
|
}));
|
|
|
|
vi.mock("@/lib/permission-slugs", () => ({
|
|
PERMS: { USERS_VIEW: "admin.users.view" },
|
|
}));
|
|
|
|
const mockRevalidateTag = vi.hoisted(() => vi.fn());
|
|
vi.mock("next/cache", () => ({ revalidateTag: mockRevalidateTag }));
|
|
|
|
vi.mock("@/lib/db", async () => {
|
|
const schema = await import("@/db/schema");
|
|
const { createFakeDb } = await import("@/test/fake-db");
|
|
const db = createFakeDb((table) => {
|
|
if (table === schema.UserWatch) return state.watches;
|
|
return [];
|
|
});
|
|
db.insert = vi.fn((table) => ({
|
|
values: (values: {
|
|
staffId: number;
|
|
targetUserId: number;
|
|
reason: string;
|
|
}) => {
|
|
state.insertCall = { table, values };
|
|
return Promise.resolve([{ insertId: 1n }]);
|
|
},
|
|
}));
|
|
db.delete = vi.fn((table) => ({
|
|
where: (condition: unknown) => {
|
|
state.deleteCall = { table, condition };
|
|
return Promise.resolve([{ affectedRows: 1 }]);
|
|
},
|
|
}));
|
|
return { ...schema, db };
|
|
});
|
|
|
|
import { toggleUserWatch } from "./watch";
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
state.allowed = ["admin.users.view"];
|
|
state.watches = [];
|
|
state.insertCall = undefined;
|
|
state.deleteCall = undefined;
|
|
});
|
|
|
|
describe("toggleUserWatch", () => {
|
|
it("creates a watch when none exists and returns watching: true", async () => {
|
|
const res = await toggleUserWatch({
|
|
targetUserId: 42,
|
|
reason: "suspicious",
|
|
});
|
|
expect(res).toEqual({ ok: true, data: { watching: true } });
|
|
expect(state.insertCall.values).toEqual({
|
|
staffId: 100,
|
|
targetUserId: 42,
|
|
reason: "suspicious",
|
|
});
|
|
expect(mockRevalidateTag).toHaveBeenCalledWith("user-watch:100", {
|
|
expire: 0,
|
|
});
|
|
});
|
|
|
|
it("defaults the reason to an empty string", async () => {
|
|
await toggleUserWatch({ targetUserId: 7 });
|
|
expect(state.insertCall.values).toEqual({
|
|
staffId: 100,
|
|
targetUserId: 7,
|
|
reason: "",
|
|
});
|
|
});
|
|
|
|
it("removes an existing watch and returns watching: false", async () => {
|
|
state.watches = [{ id: 9, staffId: 100, targetUserId: 42, reason: "x" }];
|
|
const res = await toggleUserWatch({ targetUserId: 42 });
|
|
expect(res).toEqual({ ok: true, data: { watching: false } });
|
|
expect(state.deleteCall.condition).toBeDefined();
|
|
expect(mockRevalidateTag).toHaveBeenCalledWith("user-watch:100", {
|
|
expire: 0,
|
|
});
|
|
});
|
|
|
|
it("requires the users view permission", async () => {
|
|
state.allowed = [];
|
|
expect(await toggleUserWatch({ targetUserId: 1 })).toEqual({
|
|
ok: false,
|
|
error: "Unauthorized",
|
|
});
|
|
});
|
|
|
|
it("rejects invalid target user ids and oversized reasons", async () => {
|
|
expect(await toggleUserWatch({ targetUserId: -1 })).toMatchObject({
|
|
ok: false,
|
|
error: "Validation failed",
|
|
});
|
|
expect(await toggleUserWatch({ targetUserId: 0 })).toMatchObject({
|
|
ok: false,
|
|
error: "Validation failed",
|
|
});
|
|
expect(
|
|
await toggleUserWatch({ targetUserId: 1, reason: "x".repeat(256) }),
|
|
).toMatchObject({ ok: false, error: "Validation failed" });
|
|
});
|
|
});
|