Both demands were accepted. Adopting what landed. EVIDENCE BASIS IS NOW ITC-GOV CANON (0.4.0) tools/basis.py reads infospace/models/governance/evidence-basis.yaml instead of defining its own vocabulary — same discipline we already applied to the capability catalog. Two semantic changes came back that we did not have: - estimated and assumed are peers in tier "judgement". We had them separately ranked, which asserted a difference the canon does not. - derived belongs to no tier at all; asking for its tier before resolving it is now an error rather than a silent rank. Tier membership is read from tiers[].members, not bases[].tier: the latter labels invoiced/measured/quoted all as "evidenced" while the tier list splits them across "observed" and "quoted". tiers[] is authoritative; reported upstream. USES_PROVISIONS IS NOW CANON (0.5.0, CAP-R11) Dropped the proposed_extensions marker. Renamed relation "uses" to "may_use" per their migration note. tools/capability.py now enforces CAP-R11: relation must be depends_on or may_use, a provider must be named, and a depends_on entry MUST be declared between those capabilities in the catalog. data.backup gained catalog may_use: security.secrets from our restatement, so our entry now checks out. Also in 0.4.0: §10.3 changed so a joinable consumer record counts as promotion proof, met by our restatement; ITC-CAP is now 0.4.0 / canon 0.6.0, status draft. Record and tests updated to those versions. 196 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
197 lines
8.5 KiB
Python
197 lines
8.5 KiB
Python
import json
|
|
import sys
|
|
import unittest
|
|
from copy import deepcopy
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).parents[1]
|
|
sys.path.insert(0, str(ROOT / "tools"))
|
|
from capability import (
|
|
consumption_profile,
|
|
evidence_coverage,
|
|
load_canon,
|
|
review,
|
|
validate_provision,
|
|
validate_requirement,
|
|
)
|
|
|
|
RECORD = json.loads((ROOT / "data/capability/platform-audit-storage.json").read_text())
|
|
|
|
|
|
def canon():
|
|
return load_canon()
|
|
|
|
|
|
class CanonBindingTest(unittest.TestCase):
|
|
"""We read the canon rather than copying it; drift must fail here."""
|
|
|
|
def setUp(self):
|
|
self.canon = canon()
|
|
|
|
def test_catalog_is_the_version_we_restated_against(self):
|
|
self.assertEqual("0.4.0", self.canon["version"])
|
|
self.assertEqual("0.6.0", self.canon["canon_version"])
|
|
self.assertEqual(self.canon["version"], RECORD["canon"]["model_version"])
|
|
self.assertEqual(self.canon["canon_version"], RECORD["canon"]["canon_version"])
|
|
|
|
def test_human_effort_and_intelligence_classes_exist_with_native_units(self):
|
|
classes = self.canon["resource_classes"]
|
|
self.assertEqual("hour", classes["H"]["native_unit"])
|
|
self.assertEqual("internal", classes["H"]["supply"])
|
|
self.assertEqual("constrained", classes["H"]["capacity_behaviour"])
|
|
self.assertEqual("token", classes["I"]["native_unit"])
|
|
|
|
def test_unknown_capability_is_rejected(self):
|
|
with self.assertRaisesRegex(ValueError, "unknown capability"):
|
|
validate_requirement({"capability": "data.telepathy"}, self.canon)
|
|
|
|
|
|
class RequirementTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.canon = canon()
|
|
self.requirement = deepcopy(RECORD["requires"][0])
|
|
|
|
def test_the_real_requirement_validates(self):
|
|
validate_requirement(self.requirement, self.canon)
|
|
|
|
def test_target_key_must_be_a_declared_quality_dimension(self):
|
|
self.requirement["targets"]["chattiness"] = {"value": 1, "unit": "x"}
|
|
with self.assertRaisesRegex(ValueError, "quality dimension"):
|
|
validate_requirement(self.requirement, self.canon)
|
|
|
|
def test_constraint_dimension_must_be_declared(self):
|
|
self.requirement["constraints"][0]["dimension"] = "vibes"
|
|
with self.assertRaisesRegex(ValueError, "quality dimension"):
|
|
validate_requirement(self.requirement, self.canon)
|
|
|
|
def test_predicate_must_be_in_the_closed_set(self):
|
|
self.requirement["constraints"][0]["predicate"] = "sort_of_near"
|
|
with self.assertRaisesRegex(ValueError, "closed set"):
|
|
validate_requirement(self.requirement, self.canon)
|
|
|
|
def test_profile_must_be_declared_on_that_capability(self):
|
|
self.requirement["profile"] = "volume-of-vibes"
|
|
with self.assertRaisesRegex(ValueError, "profile"):
|
|
validate_requirement(self.requirement, self.canon)
|
|
|
|
def test_the_constraint_that_decided_procurement_is_expressible(self):
|
|
constraint = self.requirement["constraints"][0]
|
|
self.assertEqual("geographical_separation", constraint["dimension"])
|
|
self.assertEqual("not_in", constraint["predicate"])
|
|
self.assertIn("railiance01", [entry["id"] for entry in constraint["of"]])
|
|
|
|
|
|
class ProvisionTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.canon = canon()
|
|
self.provisions = {p["capability"]: deepcopy(p) for p in RECORD["provisions"]}
|
|
|
|
def test_both_real_provisions_validate(self):
|
|
for provision in self.provisions.values():
|
|
validate_provision(provision, self.canon)
|
|
|
|
def test_consumption_unit_must_be_the_class_native_unit(self):
|
|
provision = self.provisions["data.object"]
|
|
row = next(r for r in provision["consumes"] if r["class"] == "H")
|
|
row["quantity"]["unit"] = "eur"
|
|
with self.assertRaisesRegex(ValueError, "native unit"):
|
|
validate_provision(provision, self.canon)
|
|
|
|
def test_currency_cannot_be_smuggled_in_as_a_class(self):
|
|
provision = self.provisions["data.object"]
|
|
provision["consumes"].append(
|
|
{"class": "EUR", "quantity": {"value": 7.35, "unit": "eur"}, "basis": "quoted"}
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "unknown resource class"):
|
|
validate_provision(provision, self.canon)
|
|
|
|
def test_unknown_consumption_may_not_be_recorded_as_zero(self):
|
|
provision = self.provisions["data.backup"]
|
|
row = next(r for r in provision["consumes"] if r["basis"] == "unknown")
|
|
row["quantity"]["value"] = 0
|
|
with self.assertRaises(ValueError):
|
|
validate_provision(provision, self.canon)
|
|
|
|
def test_evidence_hook_must_be_declared_on_the_capability(self):
|
|
provision = self.provisions["data.backup"]
|
|
provision["evidence"][0]["hook"] = "vibes_check"
|
|
with self.assertRaisesRegex(ValueError, "evidence hook"):
|
|
validate_provision(provision, self.canon)
|
|
|
|
def test_duplicate_class_rows_are_rejected(self):
|
|
provision = self.provisions["data.backup"]
|
|
provision["consumes"].append(deepcopy(provision["consumes"][0]))
|
|
with self.assertRaisesRegex(ValueError, "duplicate"):
|
|
validate_provision(provision, self.canon)
|
|
|
|
def test_backup_provision_satisfies_all_four_declared_evidence_hooks(self):
|
|
coverage = evidence_coverage(self.provisions["data.backup"], self.canon)
|
|
self.assertTrue(coverage["complete"])
|
|
self.assertEqual([], coverage["missing"])
|
|
self.assertEqual(4, len(coverage["supplied"]))
|
|
|
|
def test_object_provision_is_honest_about_missing_hooks(self):
|
|
coverage = evidence_coverage(self.provisions["data.object"], self.canon)
|
|
self.assertFalse(coverage["complete"])
|
|
self.assertIn("object_integrity_tests", coverage["missing"])
|
|
|
|
def test_using_another_capability_is_a_relation_not_a_p_row(self):
|
|
"""CAP-R11. The credential-custody P row was the wrong kind."""
|
|
backup = self.provisions["data.backup"]
|
|
classes = {r["class"] for r in backup["consumes"]}
|
|
self.assertNotIn("P", classes)
|
|
relations = {u["capability"]: u["relation"] for u in backup["uses_provisions"]}
|
|
self.assertEqual("may_use", relations["security.secrets"])
|
|
self.assertEqual("depends_on", relations["data.object"])
|
|
|
|
def test_depends_on_must_be_declared_between_the_capabilities(self):
|
|
backup = self.provisions["data.backup"]
|
|
entry = next(u for u in backup["uses_provisions"] if u["capability"] == "security.secrets")
|
|
entry["relation"] = "depends_on"
|
|
with self.assertRaisesRegex(ValueError, "does not declare depends_on"):
|
|
validate_provision(backup, self.canon)
|
|
|
|
def test_relation_outside_the_canon_vocabulary_is_rejected(self):
|
|
backup = self.provisions["data.backup"]
|
|
backup["uses_provisions"][0]["relation"] = "uses"
|
|
with self.assertRaisesRegex(ValueError, "depends_on or may_use"):
|
|
validate_provision(backup, self.canon)
|
|
|
|
def test_a_used_provision_must_name_its_provider(self):
|
|
backup = self.provisions["data.backup"]
|
|
backup["uses_provisions"][0]["provider"] = ""
|
|
with self.assertRaisesRegex(ValueError, "must name a provider"):
|
|
validate_provision(backup, self.canon)
|
|
|
|
def test_effort_and_tokens_are_recorded_in_native_units(self):
|
|
rows = {r["class"]: r for r in self.provisions["data.object"]["consumes"]}
|
|
self.assertEqual("hour", rows["H"]["quantity"]["unit"])
|
|
self.assertEqual("token", rows["I"]["quantity"]["unit"])
|
|
self.assertEqual("unknown", rows["I"]["basis"])
|
|
|
|
|
|
class ReviewTest(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.report = review(deepcopy(RECORD), canon())
|
|
|
|
def test_requirement_is_reported_as_below_the_asked_maturity(self):
|
|
requirement = self.report["requirements"][0]
|
|
self.assertEqual("D5", requirement["required"])
|
|
self.assertEqual("D4", requirement["provided"])
|
|
self.assertEqual("below_requirement", requirement["status"])
|
|
|
|
def test_provider_comparison_grades_as_indicative_not_evidenced(self):
|
|
grade = self.report["alternatives_grade"]
|
|
self.assertEqual("indicative", grade["grade"])
|
|
self.assertEqual("assumed", grade["weakest"])
|
|
self.assertEqual(0.25, grade["evidenced_ratio"])
|
|
|
|
def test_consumption_profiles_report_what_is_still_unknown(self):
|
|
for provision in self.report["provisions"]:
|
|
self.assertEqual("unknown", provision["consumption"]["weakest"])
|
|
self.assertGreater(provision["consumption"]["count"], 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|