The previous commit let users cycle empty matrix cells in the modal but the engine only iterated `payload.matrix` — overrides for keys absent from the library were silently ignored. Cycling produced dead UI: state saved, engine ignored, runtime identical. Engine extension: after the library-merge pass, do a second pass over `cell_mode_overrides` ∪ `cell_factor_overrides` minus `covered_keys`. Each leftover key becomes a new matrix entry with implicit baseline mode "allow" + factor 1.0; whichever side(s) the user set populate the rest. Disabled set still wins — entries in `disabled_matrix_cells` skip both passes. Added `decode_key` helper alongside `encode_key` for the second-pass src/tgt extraction. Round-trip + malformed-input rejection covered by new key tests. Malformed override keys in the resolve path are skipped silently rather than crashing the run (defensive — instance JSON could have rotted across schema migrations). Updated the existing "unknown key silently ignored" test to assert the new behavior (entry created from override). Added focused tests for: factor-only override defaults to allow mode, disabled set still skips empty-cell overrides, malformed key in override map doesn't crash.
59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
"""Cross-language fitness test for composite-key encoding.
|
|
|
|
Reads the JSON fixture written by the Vitest fitness writer
|
|
(`src/components/context/editors/instance/keys.fitness.ts`) and asserts
|
|
Python `encode_key` produces byte-exact identical output for each input.
|
|
|
|
If JS encoding ever drifts from Python (e.g. someone changes the
|
|
separators or escape strategy), this test fails before the divergence
|
|
ships to users.
|
|
"""
|
|
import json
|
|
import pathlib
|
|
|
|
import pytest
|
|
|
|
from engine.modules._keys import decode_key, encode_key
|
|
|
|
FIXTURE = pathlib.Path(__file__).parent / "fixtures" / "encode_key_js_outputs.json"
|
|
|
|
|
|
def test_python_matches_js_outputs():
|
|
assert FIXTURE.exists(), (
|
|
"Fixture missing — regenerate via "
|
|
"`npx tsx src/components/context/editors/instance/keys.fitness.ts`"
|
|
)
|
|
cases = json.loads(FIXTURE.read_text(encoding="utf-8"))
|
|
for case in cases:
|
|
py_output = encode_key(case["input"])
|
|
assert py_output == case["output"], (
|
|
f"input={case['input']!r}: js={case['output']!r} py={py_output!r}"
|
|
)
|
|
|
|
|
|
def test_decode_key_round_trip():
|
|
cases = [
|
|
["red", "black"],
|
|
["", ""],
|
|
["ascii", "unicode-é"],
|
|
["with,comma", "with]bracket"],
|
|
]
|
|
for parts in cases:
|
|
encoded = encode_key(parts)
|
|
assert decode_key(encoded) == parts
|
|
|
|
|
|
def test_decode_key_rejects_non_array():
|
|
with pytest.raises(ValueError):
|
|
decode_key('"just-a-string"')
|
|
|
|
|
|
def test_decode_key_rejects_array_with_non_strings():
|
|
with pytest.raises(ValueError):
|
|
decode_key('["red",42]')
|
|
|
|
|
|
def test_decode_key_rejects_malformed_json():
|
|
with pytest.raises(ValueError):
|
|
decode_key("not-json")
|