Spaces:
Running
Running
| /** | |
| * compliance_reject.test.ts | |
| * Doctrine v6 R3 — Vertical Governance Receipts | |
| * 10 REJECT cases: malformed or non-compliant policy documents are correctly | |
| * rejected by schema validation. | |
| * | |
| * Each test constructs a deliberately invalid policy object and asserts that | |
| * AJV validation returns false (the schema rejects it). | |
| * | |
| * Test framework: Jest / ts-jest | |
| * Run: npx jest tests/compliance/compliance_reject.test.ts | |
| */ | |
| import Ajv from "ajv"; | |
| import addFormats from "ajv-formats"; | |
| import * as fs from "fs"; | |
| import * as path from "path"; | |
| const SCHEMA_PATH = path.resolve(__dirname, "../../a11oy-knowledge.schema.json"); | |
| function buildValidator() { | |
| const ajv = new Ajv({ allErrors: true, strict: false }); | |
| addFormats(ajv); | |
| const schema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf8")); | |
| return ajv.compile(schema); | |
| } | |
| // ── Minimal valid base object (used as mutation seed) ───────────────────────── | |
| function basePolicy(): any { | |
| return { | |
| schema_version: "1.0.0", | |
| vertical: "healthcare", | |
| regime: "HIPAA/HITECH", | |
| effective_date: "2025-07-01", | |
| jurisdiction: "US-Federal", | |
| meta: { | |
| title: "Healthcare AI Governance Policy — HIPAA Alignment", | |
| description: "Maps HIPAA provisions to Doctrine v6 axes for AI systems processing PHI.", | |
| authority: "45 CFR Parts 160, 162, 164", | |
| receipt_chain_required: true, | |
| merkle_root_algorithm: "SHA3-256", | |
| }, | |
| regulatory_clauses: Array.from({ length: 8 }, (_, i) => ({ | |
| clause_id: `CLAUSE-${i + 1}`, | |
| title: `Test clause ${i + 1}`, | |
| citation: `45 CFR § 164.${i + 100}`, | |
| full_ref: `Full reference for test clause ${i + 1} with adequate detail`, | |
| lambda_axes: [ | |
| { axis: "Λ3", label: "Privacy", weight: 0.9, enforcement: "mandatory", rationale: "Test rationale for this axis mapping with sufficient length." }, | |
| ], | |
| })), | |
| compliance_thresholds: { | |
| minimum_lambda_coverage: 6, | |
| mandatory_axes: ["Λ3", "Λ6", "Λ7"], | |
| receipt_retention_days: 2555, | |
| }, | |
| receipt_chain: { | |
| algorithm: "SHA3-256", | |
| chaining: "merkle_dag", | |
| quorum: "2-of-3", | |
| nodes: ["primary", "backup", "audit"], | |
| }, | |
| }; | |
| } | |
| describe("Compliance REJECT — invalid policy documents rejected", () => { | |
| const validate = buildValidator(); | |
| // REJECT-001: Missing required top-level field (schema_version) | |
| test("REJECT-001: missing schema_version is rejected", () => { | |
| const p = basePolicy(); | |
| delete p.schema_version; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-002: Invalid vertical enum value | |
| test("REJECT-002: unknown vertical 'blockchain' is rejected", () => { | |
| const p = basePolicy(); | |
| p.vertical = "blockchain"; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-003: effective_date format violation (not ISO 8601 YYYY-MM-DD) | |
| test("REJECT-003: effective_date '07/01/2025' (MM/DD/YYYY) is rejected", () => { | |
| const p = basePolicy(); | |
| p.effective_date = "07/01/2025"; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-004: regulatory_clauses count below minimum (< 8) | |
| test("REJECT-004: fewer than 8 regulatory_clauses is rejected", () => { | |
| const p = basePolicy(); | |
| p.regulatory_clauses = p.regulatory_clauses.slice(0, 5); | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-005: regulatory_clauses count above maximum (> 12) | |
| test("REJECT-005: more than 12 regulatory_clauses is rejected", () => { | |
| const p = basePolicy(); | |
| const extra = Array.from({ length: 5 }, (_, i) => ({ | |
| clause_id: `EXTRA-${i}`, | |
| title: `Extra clause ${i}`, | |
| citation: `45 CFR § 999.${i}`, | |
| full_ref: `Full reference for extra clause ${i} with adequate length`, | |
| lambda_axes: [ | |
| { axis: "Λ1", label: "Transparency", weight: 0.5, enforcement: "recommended", rationale: "Extra rationale for testing purposes." }, | |
| ], | |
| })); | |
| p.regulatory_clauses = [...p.regulatory_clauses, ...extra]; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-006: Invalid Λ-axis identifier (not in enum) | |
| test("REJECT-006: lambda axis 'Λ11' (out of range) is rejected", () => { | |
| const p = basePolicy(); | |
| p.regulatory_clauses[0].lambda_axes[0].axis = "Λ11"; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-007: weight out of range (> 1.0) | |
| test("REJECT-007: lambda weight 1.5 (> 1.0) is rejected", () => { | |
| const p = basePolicy(); | |
| p.regulatory_clauses[0].lambda_axes[0].weight = 1.5; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-008: invalid receipt chain algorithm | |
| test("REJECT-008: receipt chain algorithm 'MD5' is rejected", () => { | |
| const p = basePolicy(); | |
| p.receipt_chain.algorithm = "MD5"; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-009: receipt_retention_days below minimum (< 365) | |
| test("REJECT-009: receipt_retention_days 30 (< 365) is rejected", () => { | |
| const p = basePolicy(); | |
| p.compliance_thresholds.receipt_retention_days = 30; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| // REJECT-010: quorum string not matching N-of-M pattern | |
| test("REJECT-010: quorum 'majority' (not N-of-M format) is rejected", () => { | |
| const p = basePolicy(); | |
| p.receipt_chain.quorum = "majority"; | |
| expect(validate(p)).toBe(false); | |
| }); | |
| }); | |