"""Unit tests for scripts/kb_claim_source_contract_preview.py.""" from __future__ import annotations import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(REPO_ROOT / "scripts")) import kb_claim_source_contract_preview as preview # noqa: E402 def _proposal(): return { "id": "14fa5ecc-ac7a-41c1-807d-a2e85b936617", "proposal_type": "attach_evidence", "status": "approved", "reviewed_by_handle": "m3ta", "review_note": "approved with caveats", "source_ref": "telegram:m3ta:2026-07-05", "payload": { "old_claim": {"id": "d0000000-0000-0000-0000-000000000005"}, "claim_candidates": [ { "claim_key": "capital_allocation_is_civilizational_steering", "headline": "Capital allocation is a form of civilizational steering.", "body": "Capital allocation chooses which futures get resources.", "type": "structural", "confidence": 0.65, "tags": ["capital-allocation"], "review_note": "Keep the caveat.", "evidence": [{"source_key": "m3ta_telegram_approval"}], "edges": [ { "edge_type": "supersedes_component_of", "target_claim_id": "d0000000-0000-0000-0000-000000000005", } ], }, { "claim_key": "internet_finance_is_mechanism_not_telos", "headline": "Internet finance is not Teleo's telos.", "body": "Internet finance is a candidate mechanism.", "type": "normative", "confidence": 0.8, "tags": ["telos"], }, ], "source_candidates": [ { "source_key": "m3ta_telegram_approval", "source_type": "dm", "title": "m3ta Telegram approval", "source_quality": "primary operator direction", "usage_limits": "not external evidence", "key_excerpts": ["approved as caveated"], } ], "proposed_concept_map_dependency": { "concept_key": "distributed_market_intelligence", "headline": "Distributed market intelligence", }, }, } def test_preview_names_claim_and_source_rows_needed(): result = preview.preview_proposal(_proposal()) assert result["direct_apply_ready"] is False assert len(result["claim_candidates"]) == 2 assert result["claim_candidates"][0]["requires_canonical_id"] is True assert result["source_candidates"][0]["requires_deduplication"] is True def test_preview_flags_schema_and_contract_gaps(): result = preview.preview_proposal(_proposal()) gaps = {item["gap"] for item in result["contract_gaps"]} assert "current_apply_role_cannot_insert_claims_or_sources" in gaps assert "claim_candidates_need_canonical_ids" in gaps assert "public_claims_has_no_body_or_review_note_columns" in gaps assert "old_claim_supersession_is_many_successors" in gaps assert "concept_map_dependency_has_no_first_class_storage" in gaps def test_edge_and_evidence_fragments_wait_for_canonical_ids(): result = preview.preview_proposal(_proposal()) edge = result["edge_fragments"][0] assert edge["from_claim_key"] == "capital_allocation_is_civilizational_steering" assert edge["to_claim_id"] == "d0000000-0000-0000-0000-000000000005" evidence = result["evidence_fragments"][0] assert evidence["claim_key"] == "capital_allocation_is_civilizational_steering" assert evidence["source_key"] == "m3ta_telegram_approval" assert "canonical source_id" in evidence["blocked_until"] if __name__ == "__main__": import traceback failures = 0 for name, fn in sorted(globals().items()): if name.startswith("test_") and callable(fn): try: fn() print(f"PASS {name}") except Exception: failures += 1 print(f"FAIL {name}") traceback.print_exc() raise SystemExit(1 if failures else 0)