volt: beam: Q82 -- test_json_canonical.py becomes 7 discoverable cells; lane gate Ran 191 -> 198

- the file was seven `test_*` functions that nothing called, plus a module-level
  `print("ALL PASS")`: running it printed a verdict with zero assertions, and
  `python3 -m unittest discover -s wizard-work/beam -p test_json_canonical.py`
  collected 0 (rc 5, NO TESTS RAN) while the import still printed ALL PASS.
- the seven cells are now methods of one `unittest.TestCase`; discover collects
  7, `python3 wizard-work/beam/test_json_canonical.py` runs 7 OK, and
  `bash wizard-work/beam/CHECK` moves Ran 191 -> Ran 198, rc 0, OK.
- the ALL PASS print is deleted; there is no module-level verdict on import.
- cells bite, measured against a mutant battery (harness
  /root/.hermes/profiles/volt/cache/scratch/q82/battery.py, rows in
  mutant_results.json, one tree per mutant under .../q82/mut/). Control 0/7 dead;
  `sort_keys=True` -> False: 2 dead (test_basic, test_nested); `allow_nan` allowed
  with `_normalize`'s non-finite guard removed: 1 dead (test_reject_nan);
  `ensure_ascii=True`: 1 dead (test_unicode); non-string-key guard removed: 1 dead
  (test_reject_nonstr_key); `canonical_bytes` returning str: 3 dead
  (test_bytes, test_roundtrip, test_unicode); bool serialised as "true"/"false":
  2 dead (test_basic, test_roundtrip). All 7 cells have a named killer.
- one equivalence measured, not counted as a kill: `allow_nan=True` alone is 0/7
  dead, because `_normalize` raises CanonicalError on a non-finite float before
  `json.dumps` is reached. The `allow_nan=False` argument is defence in depth on
  this API, not a reachable behaviour; the mutant that removes the guard is what
  a cell can see.

CHECK: bash wizard-work/beam/CHECK
This commit is contained in:
volt committed 2026-10-26 20:05:13 +00:00
1 parent bf14a7ae14
commit 07351026d4
1 file changed
+61 -30
+61 -30
View File
@@ -1,39 +1,70 @@
import sys, os, json #!/usr/bin/env python3
"""Cells for json_canonical.py: sorted keys, compact separators, UTF-8 pass-through,
the rejection surface, and round-trip.
Seven cells, one per behaviour the module claims in its docstring. They are
`unittest.TestCase` methods, so `unittest discover` collects them and the lane's
CHECK counts them; there is no module-level verdict printed at import.
The module path resolves from this file's own directory, never a host path.
"""
import json
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from json_canonical import canonical, canonical_bytes, CanonicalError from json_canonical import canonical, canonical_bytes, CanonicalError # noqa: E402
def test_basic():
assert canonical({"b":2,"a":1}) == '{"a":1,"b":2}'
assert canonical([3,1,2]) == '[3,1,2]'
assert canonical(True) == 'true'
assert canonical(None) == 'null'
assert canonical("x") == '"x"'
def test_nested(): class JsonCanonicalTest(unittest.TestCase):
assert canonical({"z":{"y":1,"x":2},"a":[1,2]}) == '{"a":[1,2],"z":{"x":2,"y":1}}' # cell 1: sorted object keys, list order preserved, the three scalars
def test_basic(self):
self.assertEqual(canonical({"b": 2, "a": 1}), '{"a":1,"b":2}')
self.assertEqual(canonical([3, 1, 2]), "[3,1,2]")
self.assertEqual(canonical(True), "true")
self.assertEqual(canonical(False), "false")
self.assertEqual(canonical(None), "null")
self.assertEqual(canonical("x"), '"x"')
def test_unicode(): # cell 2: sorting reaches nested objects and nothing is reordered inside lists
assert canonical({"é":1}) == '{"é":1}' def test_nested(self):
self.assertEqual(canonical({"z": {"y": 1, "x": 2}, "a": [1, 2]}),
'{"a":[1,2],"z":{"x":2,"y":1}}')
def test_reject_nan(): # cell 3: ensure_ascii=False -- non-ASCII survives as UTF-8, not \u escapes
try: def test_unicode(self):
canonical(float('nan')) self.assertEqual(canonical({"é": "ü"}), '{"é":"ü"}')
raise AssertionError("expected error") self.assertEqual(canonical_bytes({"é": "ü"}), '{"é":"ü"}'.encode("utf-8"))
except CanonicalError: self.assertNotIn("\\u", canonical({"é": "ü"}))
pass
def test_reject_nonstr_key(): # cell 4: every non-finite float is refused, not serialised as NaN/Infinity
try: def test_reject_nan(self):
canonical({1:2}) for bad in (float("nan"), float("inf"), float("-inf")):
raise AssertionError("expected error") with self.assertRaises(CanonicalError):
except CanonicalError: canonical(bad)
pass with self.assertRaises(CanonicalError):
canonical({"k": [bad]})
def test_bytes(): # cell 5: a non-string key is refused wherever it sits, including nested
assert canonical_bytes({"a":1}) == b'{"a":1}' def test_reject_nonstr_key(self):
for bad in ({1: 2}, {None: 2}, {1.0: 2}, {"a": {2: 3}}):
with self.assertRaises(CanonicalError):
canonical(bad)
def test_roundtrip(): # cell 6: canonical_bytes is bytes, encoded once, byte-identical to the str form
o = {"a":[1,2,3],"b":{"c":True}} def test_bytes(self):
assert json.loads(canonical(o)) == o out = canonical_bytes({"a": 1})
self.assertIsInstance(out, bytes)
self.assertEqual(out, b'{"a":1}')
self.assertEqual(canonical_bytes([1.5, "é"]), canonical([1.5, "é"]).encode("utf-8"))
print("ALL PASS") # cell 7: the output parses back to the input value
def test_roundtrip(self):
obj = {"a": [1, 2, 3], "b": {"c": True, "d": None}, "é": "ü", "f": 1.5}
self.assertEqual(json.loads(canonical(obj)), obj)
self.assertEqual(json.loads(canonical_bytes(obj).decode("utf-8")), obj)
if __name__ == "__main__":
unittest.main()