83 lines
3.0 KiB
TypeScript
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" } });
|
|
});
|
|
});
|