feat(#62): IMP-46 cache fingerprint forwarding u1~u4 (router kwarg + step12 forward + 8 scenarios)
This commit is contained in:
@@ -500,3 +500,105 @@ def test_production_non_provisional_reject_skipped_before_route_gate(monkeypatch
|
||||
assert records[0]["skip_reason"] == "not_provisional"
|
||||
assert records[0]["ai_called"] is False
|
||||
router.assert_not_called()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# IMP-46 u4 — Step 12 ↔ router fingerprints forwarding (integration scope)
|
||||
# ---------------------------------------------------------------------------
|
||||
# Locks the producer→consumer wiring added in u3: Step 12 builds the
|
||||
# fingerprints dict at step12.py:179-185, stamps it onto record["fingerprints"]
|
||||
# at step12.py:185, and now (post-u3) forwards the SAME object into
|
||||
# route_ai_fallback via the fingerprints= kwarg at step12.py:203. These tests
|
||||
# assert end-to-end forwarding (router receives exactly record["fingerprints"]
|
||||
# for AI-eligible units; router untouched and record["fingerprints"] is None
|
||||
# for skipped / non-AI records).
|
||||
|
||||
|
||||
def test_router_receives_exactly_record_fingerprints_for_ai_eligible(monkeypatch):
|
||||
"""Integration scope: route_ai_fallback fingerprints kwarg == record['fingerprints']."""
|
||||
router = MagicMock(return_value=None)
|
||||
monkeypatch.setattr(step12_mod, "route_ai_fallback", router)
|
||||
contract = {"frame_id": "fid_123", "payload": {"k": "v"}, "sub_zones": []}
|
||||
partial = {"deeper": [9, 8, 7], "shallow": "x"}
|
||||
catalog_value = "c0ffee00" * 8
|
||||
recs = _call(
|
||||
[_ai_unit()],
|
||||
get_contract_fn=lambda _t: contract,
|
||||
figma_partial_loader=lambda _t: partial,
|
||||
catalog_sha_loader=lambda: catalog_value,
|
||||
)
|
||||
record_fingerprints = recs[0]["fingerprints"]
|
||||
router.assert_called_once()
|
||||
forwarded = router.call_args.kwargs["fingerprints"]
|
||||
# Strict equality: same keys, same SHA values — Step 12 producer is wired
|
||||
# to the router consumer with no transformation between them.
|
||||
assert forwarded == record_fingerprints
|
||||
assert forwarded == {
|
||||
"contract_sha": hashlib.sha256(
|
||||
json.dumps(contract, sort_keys=True, ensure_ascii=False).encode("utf-8")
|
||||
).hexdigest(),
|
||||
"partial_sha": hashlib.sha256(
|
||||
json.dumps(partial, sort_keys=True, ensure_ascii=False).encode("utf-8")
|
||||
).hexdigest(),
|
||||
"catalog_sha": catalog_value,
|
||||
}
|
||||
|
||||
|
||||
def test_router_fingerprints_kwarg_is_present_even_with_default_catalog(monkeypatch):
|
||||
"""Integration scope: fingerprints kwarg is supplied (not omitted) even when catalog_sha defaults to ''."""
|
||||
router = MagicMock(return_value=None)
|
||||
monkeypatch.setattr(step12_mod, "route_ai_fallback", router)
|
||||
_call([_ai_unit()])
|
||||
router.assert_called_once()
|
||||
# The fingerprints kwarg must be present in the call (not relying on the
|
||||
# router's default None) — proves step12.py:203 forwards explicitly.
|
||||
assert "fingerprints" in router.call_args.kwargs
|
||||
forwarded = router.call_args.kwargs["fingerprints"]
|
||||
assert isinstance(forwarded, dict)
|
||||
assert set(forwarded.keys()) == {"contract_sha", "partial_sha", "catalog_sha"}
|
||||
assert forwarded["catalog_sha"] == "" # default sentinel, still forwarded
|
||||
|
||||
|
||||
def test_router_not_called_and_fingerprints_none_for_non_provisional(monkeypatch):
|
||||
"""Integration scope: non-provisional unit → router untouched, record['fingerprints'] is None."""
|
||||
router = MagicMock(return_value=None)
|
||||
monkeypatch.setattr(step12_mod, "route_ai_fallback", router)
|
||||
recs = _call([FakeUnit(label="restructure", provisional=False)])
|
||||
router.assert_not_called()
|
||||
assert recs[0]["ai_called"] is False
|
||||
assert recs[0]["skip_reason"] == "not_provisional"
|
||||
assert recs[0]["fingerprints"] is None
|
||||
assert recs[0]["cache_key"] is None
|
||||
|
||||
|
||||
def test_router_not_called_and_fingerprints_none_for_non_ai_route(monkeypatch):
|
||||
"""Integration scope: light_edit (non-AI route) → router untouched, record['fingerprints'] is None."""
|
||||
router = MagicMock(return_value=None)
|
||||
monkeypatch.setattr(step12_mod, "route_ai_fallback", router)
|
||||
recs = _call([FakeUnit(label="light_edit", provisional=True)])
|
||||
router.assert_not_called()
|
||||
assert recs[0]["ai_called"] is False
|
||||
assert recs[0]["skip_reason"] == (
|
||||
"route_not_ai_adaptation:deterministic_minor_adjustment"
|
||||
)
|
||||
assert recs[0]["fingerprints"] is None
|
||||
assert recs[0]["cache_key"] is None
|
||||
|
||||
|
||||
def test_mixed_units_router_receives_fingerprints_only_for_ai_eligible(monkeypatch):
|
||||
"""Integration scope: in a mixed batch, only the AI-eligible unit forwards fingerprints to router."""
|
||||
router = MagicMock(return_value=None)
|
||||
monkeypatch.setattr(step12_mod, "route_ai_fallback", router)
|
||||
units = [
|
||||
FakeUnit(label="restructure", provisional=False), # not_provisional
|
||||
FakeUnit(label="light_edit", provisional=True), # non-AI route
|
||||
_ai_unit(), # AI-eligible
|
||||
]
|
||||
recs = _call(units)
|
||||
# Exactly one router invocation — the AI-eligible unit.
|
||||
router.assert_called_once()
|
||||
forwarded = router.call_args.kwargs["fingerprints"]
|
||||
assert forwarded == recs[2]["fingerprints"]
|
||||
# Skipped records carry None.
|
||||
assert recs[0]["fingerprints"] is None
|
||||
assert recs[1]["fingerprints"] is None
|
||||
|
||||
Reference in New Issue
Block a user