/* RFC-1299 tests for capabilityGapSchema — the Zod mirror of the normative spec schema docs/specs/werkstatt-onboarding/schemas/ capability-gap.schema.yaml. Verifies the strict-object contract: valid records round-trip, malformed ids/hashes/kinds are rejected, and unknown keys are stripped nowhere (strict objects reject). Do not exercise CAPGAP-02/03 semantics — those live in the site validator. RFC-1299: created — schema boundary tests. RFC-1299: capability.brief + family.detect + capability.gap.validate */ import { describe, expect, it } from "vitest"; import { capabilityGapSchema } from "../capability-gap.ts"; const VALID_GAP = { id: "CAPGAP-001", systemId: "dp7", kind: "new-family", need: { statement: "No site family matches the intake classification", evidenceRefs: ["evidence-source/src-01"], }, catalogEvidence: { capabilityBriefSha256: `sha256:${"0".repeat(64)}`, familyScore: 0.5, candidates: [ { ref: "family-a", whyInsufficient: "unmatched signals: audienceAny", }, ], }, proposal: { functionalName: "donation-kindergarten", track: "data" }, interim: "compose with the closest family's required archetypes", }; describe("capabilityGapSchema (RFC-1299)", () => { it("accepts a complete new-family record", () => { const parsed = capabilityGapSchema.safeParse(VALID_GAP); expect(parsed.success).toBe(true); }); it.each([ ["bad id", { id: "gap-1" }], ["bad kind", { kind: "new-planet" }], ["empty evidenceRefs", { need: { statement: "s", evidenceRefs: [] } }], [ "bad brief hash", { catalogEvidence: { ...VALID_GAP.catalogEvidence, capabilityBriefSha256: "sha256:xyz", }, }, ], [ "familyScore above 1", { catalogEvidence: { ...VALID_GAP.catalogEvidence, familyScore: 1.5, }, }, ], [ "empty candidates", { catalogEvidence: { ...VALID_GAP.catalogEvidence, candidates: [] }, }, ], [ "bad functionalName", { proposal: { functionalName: "DP7 Partners", track: "data" } }, ], [ "bad track", { proposal: { functionalName: "partner-locator", track: "code" }, }, ], ["missing interim", { interim: undefined }], ])("rejects a record with %s", (_label, patch) => { const record = { ...VALID_GAP, ...patch }; expect(capabilityGapSchema.safeParse(record).success).toBe(false); }); it("rejects unknown keys (strict mirror of additionalProperties:false)", () => { const record = { ...VALID_GAP, note: "operator remark" }; expect(capabilityGapSchema.safeParse(record).success).toBe(false); }); it("accepts every spec kind", () => { for (const kind of [ "new-section-archetype", "section-props-extension", "new-family", "new-biome", "new-constellation", "new-pbp-entity-type", "new-component", ]) { expect( capabilityGapSchema.safeParse({ ...VALID_GAP, kind }).success, ).toBe(true); } }); it("familyScore is optional (non-family kinds)", () => { const { familyScore: _drop, ...evidence } = VALID_GAP.catalogEvidence; const record = { ...VALID_GAP, catalogEvidence: evidence }; expect(capabilityGapSchema.safeParse(record).success).toBe(true); }); });