Files
Epicnabbo-Catalogus-Updated…/src/lib/services/audit.test.ts
T
openhands 17847545dd Improvements: remove dead config, fix ESM, add URL validation, unify types, add missing logging
- Remove .prettierrc (dead config, Biome replaces Prettier)
- Rename lighthouserc.json to lighthouserc.cjs with module.exports for ESM compat
- Add logger.warn to empty catch blocks in auth, register, site-settings, prisma-cache, redis, security, rate-limit
- Unify ActionResult type: action-helper.ts uses 'ok' consistent with safe-action-shared.ts
- Add noUnusedLocals + noUnusedParameters to tsconfig + fix 25 pre-existing unused vars
- Replace barrel export src/types/index.ts with direct @/types/common imports
- Make trustHost conditional (development only) in auth.ts
- Add pre-flight URL validation to update-Nitrov3.sh to catch image.library.url misconfigurations
- Improve NITRO_IMAGE_LIBRARY_URL content validation in pre-flight & post-compute checks
2026-07-26 20:28:11 +02:00

163 lines
3.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const create = vi.hoisted(() => vi.fn());
const findMany = vi.hoisted(() => vi.fn());
const count = vi.hoisted(() => vi.fn());
const userFindMany = vi.hoisted(() => vi.fn());
vi.mock("@/lib/prisma", () => ({
prisma: {
adminAuditLog: { create, findMany, count },
user: { findMany: userFindMany },
},
}));
vi.mock("@/env", () => ({ env: {} }));
import { getAuditLogs, logAudit } from "./audit";
beforeEach(() => {
vi.clearAllMocks();
});
describe("logAudit", () => {
it("creates an audit entry", async () => {
create.mockResolvedValue({ id: 1 });
await logAudit({
userId: 1,
action: "test_action",
target: "user",
targetId: 42,
});
const data = create.mock.calls[0][0].data;
expect(data.userId).toBe(1);
expect(data.action).toBe("test_action");
expect(data.target).toBe("user");
expect(data.targetId).toBe(42);
});
it("redacts sensitive keys in payload", async () => {
create.mockResolvedValue({ id: 1 });
await logAudit({
userId: 1,
action: "update",
target: "user",
targetId: 1,
before: { username: "foo", password: "secret123" },
after: { username: "bar", password: "newsecret" },
});
const data = create.mock.calls[0][0].data;
expect(JSON.parse(data.before).password).toBe("[Redacted]");
expect(JSON.parse(data.after).password).toBe("[Redacted]");
expect(JSON.parse(data.diff).username).toEqual({ from: "foo", to: "bar" });
});
it("omits diff when only before or after is missing", async () => {
create.mockResolvedValue({ id: 1 });
await logAudit({
userId: 1,
action: "delete",
target: "user",
before: { username: "foo" },
});
const data = create.mock.calls[0][0].data;
expect(data.diff).toBeNull();
});
it("handles empty payloads", async () => {
create.mockResolvedValue({ id: 1 });
await logAudit({ userId: 1, action: "view", target: "page" });
expect(create).toHaveBeenCalledOnce();
});
it("flattens nested objects", async () => {
create.mockResolvedValue({ id: 1 });
await logAudit({
userId: 1,
action: "update_settings",
target: "user",
before: { nested: { key: "val" } },
after: {},
});
const data = create.mock.calls[0][0].data;
expect(JSON.parse(data.before)).toEqual({ nested: { key: "val" } });
});
});
describe("getAuditLogs", () => {
it("returns paginated logs with usernames", async () => {
findMany.mockResolvedValue([
{
id: 1,
userId: 1,
action: "test",
target: "user",
targetId: null,
before: null,
after: null,
diff: null,
createdAt: "2024-01-01",
},
{
id: 2,
userId: 2,
action: "test2",
target: "room",
targetId: null,
before: null,
after: null,
diff: null,
createdAt: "2024-01-02",
},
]);
count.mockResolvedValue(2);
userFindMany.mockResolvedValue([
{ id: 1, username: "alice" },
{ id: 2, username: "bob" },
]);
const result = await getAuditLogs({ page: 1, perPage: 20 });
expect(result.rows).toHaveLength(2);
expect(result.rows[0].username).toBe("alice");
expect(result.rows[1].username).toBe("bob");
expect(result.total).toBe(2);
expect(result.lastPage).toBe(1);
});
it("filters by search term", async () => {
findMany.mockResolvedValue([]);
count.mockResolvedValue(0);
await getAuditLogs({ search: "test" });
expect(findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: {
OR: [
{ action: { contains: "test" } },
{ target: { contains: "test" } },
],
},
}),
);
});
it("falls back to User #id for unknown users", async () => {
findMany.mockResolvedValue([
{
id: 1,
userId: 99,
action: "x",
target: "y",
targetId: null,
before: null,
after: null,
diff: null,
createdAt: "2024-01-01",
},
]);
count.mockResolvedValue(1);
userFindMany.mockResolvedValue([]);
const result = await getAuditLogs();
expect(result.rows[0].username).toBe("User #99");
});
});