resource-control/tests/test_basis.py
tegwick 13c2b82281 feat: restate the backup case in ITC-CAP terms; add evidence basis; publish consumption-mode signal
Three things.

1. CANON RESTATEMENT (info-tech-canon's ask after accepting our demand)
data/capability/platform-audit-storage.json restates the backup case against
ITC-CAP 0.2.0: requirement with profile, targets and the failure-domain
constraint that decided the procurement; two provisions (data.object and
data.backup); all four data.backup evidence hooks satisfied and measured; and
consumption in native units — GB, hours, tokens — with unknown never zero.

tools/capability.py reads their capabilities.yaml directly rather than copying
it, so drift in either repo fails here. The requirement asks D5, the provision
is D4, and the review reports below_requirement rather than inflating maturity.

2. EVIDENCE BASIS (tools/basis.py, docs/evidence-basis.md)
Every value declares how it was obtained on an ordered scale: invoiced,
measured, quoted, derived, projected, estimated, assumed, unknown. A derived
value resolves to the weakest basis among its inputs, so precise arithmetic
cannot launder weak assumptions.

First application is a finding about our own biggest decision: the Scaleway vs
Hetzner comparison, EUR 29.14/month stated to the cent, grades "indicative" —
1 of 4 load-bearing values evidenced, weakest "assumed". The direction is
robust; the magnitude is a model output. The cheapest fix is recording real
operator hours, not better arithmetic.

3. CONSUMPTION-MODE SIGNAL (railiance-platform RAILIANCE-WP-0017)
settlement.py gains a consumption-mode command projecting statements into the
signal they consume; make consumption-mode PERIOD=YYYY-MM publishes
data/consumption-mode/current.json. Currently an empty list: no live charges for
2026-09, so no entity is restricted. Publishing the empty list makes that an
assertion rather than an absence, which their contract distinguishes. The
validator fails if the published signal is stale.

185 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 18:42:06 +02:00

147 lines
5.9 KiB
Python

import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parents[1] / "tools"))
from basis import (
BASIS_ORDER,
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 OrderTest(unittest.TestCase):
def test_order_runs_strongest_to_weakest(self):
self.assertEqual("invoiced", BASIS_ORDER[0])
self.assertEqual("unknown", BASIS_ORDER[-1])
self.assertLess(rank("measured"), rank("estimated"))
self.assertLess(rank("estimated"), rank("assumed"))
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 ("derived", "projected", "estimated", "assumed", "unknown"):
self.assertFalse(is_evidenced(basis))
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("assumed", 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("estimated", 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("assumed", result["weakest"])
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("assumed", result["weakest"])
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()