import sys import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).parents[1] / "tools")) from basis import ( bases as catalog_bases, load_catalog, tier_of, decision_grade, is_evidenced, profile, rank, resolve, strongest, validate_value, weakest, ) def value(basis="measured", **overrides): result = {"name": "v", "basis": basis, "value": 1} if basis == "unknown": result = {"name": "v", "basis": basis, "value": None, "gap": "not measured (owner: x)"} result.update(overrides) return result class CanonBindingTest(unittest.TestCase): """The vocabulary is owned by ITC-GOV; we read it, we do not vendor it.""" def test_catalog_is_read_from_the_canon(self): catalog = load_catalog() self.assertEqual("0.1.0", catalog["version"]) self.assertIn("info-tech-canon", catalog["source"]) def test_every_basis_comes_from_the_canon_catalog(self): self.assertEqual( ["invoiced", "measured", "quoted", "derived", "projected", "estimated", "assumed", "unknown"], catalog_bases(), ) class TierTest(unittest.TestCase): def test_order_runs_strongest_to_weakest(self): self.assertLess(rank("measured"), rank("projected")) self.assertLess(rank("projected"), rank("estimated")) self.assertLess(rank("estimated"), rank("unknown")) def test_invoiced_and_measured_are_peers_not_ranked(self): """An invoice is authoritative for a payment, a measurement for a quantity. Neither outranks the other outside its own domain.""" self.assertEqual("observed", tier_of("invoiced")) self.assertEqual("observed", tier_of("measured")) self.assertEqual(rank("invoiced"), rank("measured")) self.assertLess(rank("measured"), rank("quoted")) def test_estimated_and_assumed_are_peers_in_the_judgement_tier(self): """Canon 0.4.0 groups them; neither is stronger than the other.""" self.assertEqual("judgement", tier_of("estimated")) self.assertEqual("judgement", tier_of("assumed")) self.assertEqual(rank("estimated"), rank("assumed")) def test_quoted_is_below_observed_for_propagation_but_still_evidenced(self): self.assertGreater(rank("quoted"), rank("measured")) self.assertTrue(is_evidenced("quoted")) def test_derived_has_no_resolved_tier(self): with self.assertRaisesRegex(ValueError, "no resolved tier"): tier_of("derived") def test_a_peer_pair_does_not_report_a_false_weakest(self): self.assertEqual(rank("invoiced"), rank(weakest(["invoiced", "measured"]))) self.assertEqual(weakest(["invoiced", "measured"]), weakest(["measured", "invoiced"])) self.assertEqual(weakest(["estimated", "assumed"]), weakest(["assumed", "estimated"])) def test_weakest_and_strongest_pick_opposite_ends(self): bases = ["measured", "assumed", "quoted"] self.assertEqual("assumed", weakest(bases)) self.assertEqual("measured", strongest(bases)) def test_empty_collection_is_unknown_not_an_error(self): self.assertEqual("unknown", weakest([])) def test_only_observed_or_contracted_bases_count_as_evidenced(self): for basis in ("invoiced", "measured", "quoted"): self.assertTrue(is_evidenced(basis)) for basis in ("projected", "estimated", "assumed", "unknown"): self.assertFalse(is_evidenced(basis)) # derived is not evidenced or unevidenced until it is resolved with self.assertRaises(ValueError): is_evidenced("derived") def test_unknown_basis_name_is_rejected(self): with self.assertRaises(ValueError): rank("vibes") class PropagationTest(unittest.TestCase): """A derived value is only as strong as its weakest input.""" def test_derivation_from_measured_inputs_stays_measured(self): derived = value("derived", derived_from=[value("measured"), value("measured")]) self.assertEqual("measured", resolve(derived)) def test_one_assumed_input_drags_the_result_down(self): derived = value("derived", derived_from=[value("quoted"), value("assumed")]) self.assertEqual("judgement", tier_of(resolve(derived))) def test_propagation_is_recursive(self): inner = value("derived", derived_from=[value("measured"), value("estimated")]) outer = value("derived", derived_from=[value("measured"), inner]) self.assertEqual("judgement", tier_of(resolve(outer))) def test_derived_without_inputs_is_rejected(self): with self.assertRaisesRegex(ValueError, "derived_from"): resolve(value("derived")) def test_non_derived_value_resolves_to_itself(self): self.assertEqual("quoted", resolve(value("quoted"))) class ValidationTest(unittest.TestCase): def test_unknown_must_not_carry_a_quantity(self): with self.assertRaises(ValueError): validate_value({"basis": "unknown", "value": 0, "gap": "g"}) def test_unknown_must_name_the_gap_and_owner(self): with self.assertRaisesRegex(ValueError, "name the gap"): validate_value({"basis": "unknown", "value": None}) def test_known_basis_must_carry_a_quantity(self): with self.assertRaisesRegex(ValueError, "must carry a quantity"): validate_value({"basis": "measured", "value": None}) def test_zero_is_a_measurement_not_an_absence(self): validate_value({"basis": "measured", "value": 0}) def test_derived_from_only_allowed_on_derived(self): with self.assertRaisesRegex(ValueError, "only a derived value"): validate_value(value("measured", derived_from=[value()])) def test_empty_proxy_target_is_rejected_but_absent_one_is_fine(self): with self.assertRaisesRegex(ValueError, "proxy_for"): validate_value(value("measured", proxy_for=" ")) validate_value(value("measured", proxy_for=None)) validate_value(value("measured", proxy_for="cost of the shared host")) class DecisionGradeTest(unittest.TestCase): def test_all_evidenced_values_grade_evidenced(self): result = decision_grade([value("measured"), value("invoiced"), value("quoted")]) self.assertEqual("evidenced", result["grade"]) self.assertEqual(1.0, result["evidenced_ratio"]) def test_a_single_assumption_makes_the_whole_decision_indicative(self): result = decision_grade([value("measured"), value("measured"), value("assumed")]) self.assertEqual("indicative", result["grade"]) self.assertEqual("judgement", result["weakest_tier"]) def test_an_unknown_makes_the_decision_insufficient(self): result = decision_grade([value("measured"), value("unknown")]) self.assertEqual("insufficient", result["grade"]) def test_precise_arithmetic_does_not_upgrade_weak_inputs(self): """The provider comparison: a two-decimal euro figure built on assumptions.""" euros = value( "derived", name="labour_eur_month", value=60.0, derived_from=[value("assumed", name="hours"), value("assumed", name="rate")], ) result = decision_grade([value("quoted", name="price"), euros]) self.assertEqual("judgement", result["weakest_tier"]) self.assertEqual("indicative", result["grade"]) def test_projected_values_grade_separately_from_estimates(self): result = decision_grade([value("measured"), value("projected")]) self.assertEqual("projected", result["grade"]) def test_proxies_are_reported_in_the_note(self): result = decision_grade([value("measured", proxy_for="cost of the shared host")]) self.assertEqual(1, len(result["proxies"])) self.assertIn("proxy", result["note"]) def test_profile_counts_by_basis_in_canonical_order(self): result = profile([value("assumed"), value("measured"), value("measured")]) self.assertEqual(["measured", "assumed"], list(result["by_basis"])) self.assertEqual(2, result["by_basis"]["measured"]) if __name__ == "__main__": unittest.main()