Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/services/admin/tenant-notes.server.test.ts
T
2026-07-08 16:15:37 +02:00

83 lines
3.0 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
createTenantNote,
deleteTenantNote,
getTenantNoteRows,
normalizeNoteCategory,
TenantNoteError,
type TenantNotesDeps,
} from "./tenant-notes.server";
function makeDeps(findManyResult: unknown[] = []): {
deps: TenantNotesDeps;
create: ReturnType<typeof vi.fn>;
deleteMany: ReturnType<typeof vi.fn>;
recordAction: ReturnType<typeof vi.fn>;
} {
const create = vi.fn(async () => ({ id: "note-1" }));
const deleteMany = vi.fn(async () => ({ count: 1 }));
const recordAction = vi.fn(async () => {});
const deps = {
db: { adminTenantNote: { findMany: vi.fn(async () => findManyResult), create, deleteMany } },
recordAction,
} as unknown as TenantNotesDeps;
return { deps, create, deleteMany, recordAction };
}
describe("normalizeNoteCategory", () => {
it("keeps known categories and defaults unknown to general", () => {
expect(normalizeNoteCategory("BILLING")).toBe("billing");
expect(normalizeNoteCategory("weird")).toBe("general");
expect(normalizeNoteCategory(null)).toBe("general");
});
});
describe("getTenantNoteRows", () => {
it("maps DB rows (author relation, ISO date) into VM input rows", async () => {
const { deps } = makeDeps([
{
id: "n1",
createdAt: new Date("2026-06-20T10:00:00.000Z"),
category: "billing",
pinned: true,
noteText: "body",
author: { id: "a1", email: "ops@hivegrid.de" },
},
]);
const rows = await getTenantNoteRows("t1", deps);
expect(rows[0]).toEqual({
id: "n1",
createdAtIso: "2026-06-20T10:00:00.000Z",
category: "billing",
pinned: true,
noteText: "body",
authorEmail: "ops@hivegrid.de",
authorId: "a1",
});
});
});
describe("createTenantNote", () => {
it("rejects an empty body before writing", async () => {
const { deps, create } = makeDeps();
await expect(createTenantNote({ tenantId: "t1", noteText: " ", authorUserId: "a1" }, deps)).rejects.toBeInstanceOf(TenantNoteError);
expect(create).not.toHaveBeenCalled();
});
it("creates the note with a normalized category and audits", async () => {
const { deps, create, recordAction } = makeDeps();
await createTenantNote({ tenantId: "t1", noteText: " hi ", category: "WEIRD", pinned: true, authorUserId: "a1" }, deps);
expect(create.mock.calls[0][0].data).toMatchObject({ tenantId: "t1", noteText: "hi", category: "general", pinned: true });
expect(recordAction.mock.calls[0][0]).toMatchObject({ action: "tenant.note.created", targetId: "t1" });
});
});
describe("deleteTenantNote", () => {
it("scopes the delete to tenant+note and audits", async () => {
const { deps, deleteMany, recordAction } = makeDeps();
await deleteTenantNote({ noteId: "n1", tenantId: "t1", actorUserId: "a1" }, deps);
expect(deleteMany.mock.calls[0][0].where).toEqual({ id: "n1", tenantId: "t1" });
expect(recordAction.mock.calls[0][0]).toMatchObject({ action: "tenant.note.deleted", after: { noteId: "n1" } });
});
});