1import { describe, it, expect } from "vitest";
2import { AUDIT_CATALOG, AUDIT_CATALOG_NAMES, type AuditCatalogEntry } from "./audit-catalog.js";
3
4
9describe("audit-catalog", () => {
10 it("has exactly the 21 mandatory events (PRODUCT-SPEC §23 + phase 12/13 security-mandatory)", () => {
11 expect(AUDIT_CATALOG).toHaveLength(21);
12 });
13
14 it("every entry is well-formed (name, description, fire, metadata[])", () => {
15 for (const e of AUDIT_CATALOG) {
16 expect(typeof e.name, `name of ${e.name}`).toBe("string");
17 expect(e.name.length).toBeGreaterThan(0);
18 expect(typeof e.description).toBe("string");
19 expect(typeof e.fire, `fire of ${e.name}`).toBe("string");
20 expect(e.fire.length).toBeGreaterThan(0);
21 expect(Array.isArray(e.metadata)).toBe(true);
22 for (const f of e.metadata) expect(typeof f).toBe("string");
23 }
24 });
25
26 it("names and fire ids are unique (the dispatch table keys off fire)", () => {
27 const names = new Set(AUDIT_CATALOG.map((e) => e.name));
28 const fires = new Set(AUDIT_CATALOG.map((e) => e.fire));
29 expect(names.size).toBe(AUDIT_CATALOG.length);
30 expect(fires.size).toBe(AUDIT_CATALOG.length);
31 });
32
33 it("covers the spec's mandatory surface (spot-check the renames + additions)", () => {
34 const names = AUDIT_CATALOG_NAMES as readonly string[];
35
36 expect(names).not.toContain("share.created");
37 expect(names).not.toContain("page.trashed");
38 expect(names).not.toContain("webhook.deleted");
39
40 expect(names).toContain("page.shared");
41 expect(names).toContain("page.deleted");
42 expect(names).toContain("webhook.revoked");
43 expect(names).toContain("permission.changed");
44 expect(names).toContain("database.created");
45 expect(names).toContain("automation.executed");
46
47 expect(names).toContain("sso.login.saml");
48 expect(names).toContain("sso.login.oidc");
49 });
50
51 it("AUDIT_CATALOG_NAMES matches the entries' names (in order)", () => {
52 expect([...AUDIT_CATALOG_NAMES]).toEqual(AUDIT_CATALOG.map((e: AuditCatalogEntry) => e.name));
53 });
54});
55