Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/utils/db/logs.db.server.test.ts
T

102 lines
3.2 KiB
TypeScript

// @vitest-environment node
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../db.server", () => {
return {
db: {
log: {
groupBy: vi.fn(),
findMany: vi.fn(),
create: vi.fn(),
},
},
};
});
import { db } from "../db.server";
import { getRlsExecutionContext } from "@hivegrid/db/rls-context.server";
import { createLogLogin, getLastUserLogsByUserIds } from "./logs.db.server";
const groupBy = db.log.groupBy as unknown as ReturnType<typeof vi.fn>;
const findMany = db.log.findMany as unknown as ReturnType<typeof vi.fn>;
const create = db.log.create as unknown as ReturnType<typeof vi.fn>;
describe("getLastUserLogsByUserIds", () => {
beforeEach(() => {
groupBy.mockReset();
findMany.mockReset();
});
it("issues no queries when given an empty user id list", async () => {
const result = await getLastUserLogsByUserIds([]);
expect(result.size).toBe(0);
expect(groupBy).not.toHaveBeenCalled();
expect(findMany).not.toHaveBeenCalled();
});
it("uses a single groupBy and a single findMany regardless of user count (no N+1)", async () => {
const userIds = ["u1", "u2", "u3", "u4", "u5"];
const newest: Record<string, Date> = {
u1: new Date("2026-05-01T10:00:00.000Z"),
u2: new Date("2026-05-02T10:00:00.000Z"),
u3: new Date("2026-05-03T10:00:00.000Z"),
};
groupBy.mockResolvedValueOnce(
Object.entries(newest).map(([userId, createdAt]) => ({ userId, _max: { createdAt } }))
);
findMany.mockResolvedValueOnce(
Object.entries(newest).map(([userId, createdAt]) => ({ id: `log-${userId}`, userId, createdAt }))
);
const result = await getLastUserLogsByUserIds(userIds);
expect(groupBy).toHaveBeenCalledTimes(1);
expect(findMany).toHaveBeenCalledTimes(1);
expect(groupBy).toHaveBeenCalledWith({
by: ["userId"],
where: { userId: { in: userIds } },
_max: { createdAt: true },
});
expect(findMany).toHaveBeenCalledWith({
where: {
OR: Object.entries(newest).map(([userId, createdAt]) => ({ userId, createdAt })),
},
select: { id: true, userId: true, action: true, createdAt: true },
});
expect(result.get("u1")).toMatchObject({ id: "log-u1", userId: "u1" });
expect(result.get("u3")).toMatchObject({ id: "log-u3" });
expect(result.size).toBe(3);
});
it("skips the findMany when no users have logs", async () => {
groupBy.mockResolvedValueOnce([{ userId: "u1", _max: { createdAt: null } }]);
const result = await getLastUserLogsByUserIds(["u1"]);
expect(groupBy).toHaveBeenCalledTimes(1);
expect(findMany).not.toHaveBeenCalled();
expect(result.size).toBe(0);
});
});
describe("identity-owned authentication logs", () => {
it("creates a pre-tenant login log inside the user's RLS context", async () => {
create.mockImplementationOnce(async () => {
expect(getRlsExecutionContext()).toMatchObject({ userId: "user_1" });
return {};
});
await createLogLogin(new Request("https://app.example.test/login"), { id: "user_1" } as never);
expect(create).toHaveBeenCalledWith({
data: {
userId: "user_1",
url: "",
action: "Login",
details: "",
},
});
});
});