File size: 5,300 Bytes
518343a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
 * 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);
  });
});