import { describe, it, expect } from "vitest"; import { AUDIT_CATALOG, AUDIT_CATALOG_NAMES, type AuditCatalogEntry } from "./audit-catalog.js"; /** * The catalog is the CONTRACT (the completeness test dispatches on `fire` and * asserts `metadata` fields) — so its shape is unit-asserted here: 20 * mandatory events, unique names + fire ids, every entry well-formed. */ describe("audit-catalog", () => { it("has exactly the 21 mandatory events (PRODUCT-SPEC §23 + phase 12/13 security-mandatory)", () => { expect(AUDIT_CATALOG).toHaveLength(21); }); it("every entry is well-formed (name, description, fire, metadata[])", () => { for (const e of AUDIT_CATALOG) { expect(typeof e.name, `name of ${e.name}`).toBe("string"); expect(e.name.length).toBeGreaterThan(0); expect(typeof e.description).toBe("string"); expect(typeof e.fire, `fire of ${e.name}`).toBe("string"); expect(e.fire.length).toBeGreaterThan(0); expect(Array.isArray(e.metadata)).toBe(true); for (const f of e.metadata) expect(typeof f).toBe("string"); } }); it("names and fire ids are unique (the dispatch table keys off fire)", () => { const names = new Set(AUDIT_CATALOG.map((e) => e.name)); const fires = new Set(AUDIT_CATALOG.map((e) => e.fire)); expect(names.size).toBe(AUDIT_CATALOG.length); expect(fires.size).toBe(AUDIT_CATALOG.length); }); it("covers the spec's mandatory surface (spot-check the renames + additions)", () => { const names = AUDIT_CATALOG_NAMES as readonly string[]; // Renames (the old names must be GONE — the viewer + tests key off these): expect(names).not.toContain("share.created"); expect(names).not.toContain("page.trashed"); expect(names).not.toContain("webhook.deleted"); // New surfaces: expect(names).toContain("page.shared"); expect(names).toContain("page.deleted"); expect(names).toContain("webhook.revoked"); expect(names).toContain("permission.changed"); expect(names).toContain("database.created"); expect(names).toContain("automation.executed"); // Both SSO protocols: expect(names).toContain("sso.login.saml"); expect(names).toContain("sso.login.oidc"); }); it("AUDIT_CATALOG_NAMES matches the entries' names (in order)", () => { expect([...AUDIT_CATALOG_NAMES]).toEqual(AUDIT_CATALOG.map((e: AuditCatalogEntry) => e.name)); }); });