"""IMP-56 (#90) u6 — tests for ``src.structure_override_resolver``. Covers the resolver contract called out in the Stage 2 plan : 1. ``validate_structure_overrides`` returns ``{}`` for non-mapping input. 2. ``validate_structure_overrides`` preserves well-formed entries. 3. ``validate_structure_overrides`` drops malformed per-entry rows without rejecting the whole batch (per-entry tolerance — mirrors u4 text_override_resolver contract). 4. ``validate_structure_overrides`` REJECTS frame swap (any inner key other than slot_order / hidden_slots is silently dropped — SCOPE LOCK). 5. ``validate_structure_overrides`` drops non-list slot_order / hidden_slots values. 6. ``validate_structure_overrides`` drops non-string or empty slot_keys inside slot_order / hidden_slots. 7. ``validate_structure_overrides`` de-duplicates slot_key entries within each list. 8. ``validate_structure_overrides`` returns fresh nested dicts AND lists (caller can mutate without aliasing the source). 9. ``validate_structure_overrides`` drops per-zone payloads that contain neither a non-empty slot_order nor a non-empty hidden_slots after sanitization. 10. ``apply_structure_override`` removes hidden_slots in-place and returns ``True``. 11. ``apply_structure_override`` reorders the slot-payload mapping per slot_order (partial reorder; unmentioned slots keep tail order). 12. ``apply_structure_override`` combines hide + reorder atomically. 13. ``apply_structure_override`` silently skips stale slot_keys (frame swap / layout regression) without raising. 14. ``apply_structure_override`` returns ``False`` (no mutation) on a no-op override (empty, or all stale). 15. ``apply_structure_override`` preserves the caller's reference identity on ``zone`` (in-place mutation via clear + update). 16. ``apply_structure_override`` NEVER inspects or mutates per-slot payload values — only top-level key membership / ordering. 17. ``apply_structure_override`` is defensive against non-list slot_order / hidden_slots leaking through (treats as empty, no raise). All tests are pure-Python — no filesystem, no Selenium, no fixtures. """ from __future__ import annotations from src.structure_override_resolver import ( InvalidStructureOverride, apply_structure_override, validate_structure_overrides, ) # -- module surface --------------------------------------------------------- def test_invalid_structure_override_is_value_error_subclass(): # Reserved future strict-mode exception — kept as a public surface so # u7 / strict callers can branch on source-malformation vs stale-DOM. assert issubclass(InvalidStructureOverride, ValueError) # -- validate_structure_overrides ------------------------------------------ def test_validate_structure_overrides_non_mapping_returns_empty(): for bad_input in [None, [], "string", 42, 1.5]: assert validate_structure_overrides(bad_input) == {} def test_validate_structure_overrides_passes_well_formed(): payload = { "zone-top": { "slot_order": ["slot_title", "slot_body"], "hidden_slots": ["slot_caption"], }, "zone-bottom": {"slot_order": ["slot_a", "slot_b"]}, "zone-only-hide": {"hidden_slots": ["slot_x"]}, } out = validate_structure_overrides(payload) assert out == { "zone-top": { "slot_order": ["slot_title", "slot_body"], "hidden_slots": ["slot_caption"], }, "zone-bottom": {"slot_order": ["slot_a", "slot_b"]}, "zone-only-hide": {"hidden_slots": ["slot_x"]}, } assert out is not payload # fresh dict def test_validate_structure_overrides_per_entry_tolerance(): payload = { "zone-top": { "slot_order": ["slot_title", "slot_body"], "hidden_slots": ["slot_caption"], }, "": {"slot_order": ["x"]}, # empty zone_id dropped 42: {"slot_order": ["y"]}, # non-string zone_id dropped "zone-non-mapping": "not a dict", # non-mapping payload dropped "zone-bottom": {"hidden_slots": ["slot_aux"]}, } out = validate_structure_overrides(payload) assert out == { "zone-top": { "slot_order": ["slot_title", "slot_body"], "hidden_slots": ["slot_caption"], }, "zone-bottom": {"hidden_slots": ["slot_aux"]}, } def test_validate_structure_overrides_rejects_frame_swap_inner_keys(): # SCOPE LOCK — frame swap attempts MUST be silently dropped. The only # mechanism for swapping a frame is the existing ``frames`` axis; this # resolver intentionally has no escape hatch so the Phase Z # no-AI-HTML-structure invariant stays intact. payload = { "zone-top": { "slot_order": ["slot_title"], # The following 4 keys are all frame-swap / DOM-rebuild # attempts and MUST be dropped by validate. "frame_id": "compare_v2", "template_id": "topic_left_right", "unit_id": "03-1+03-2", "slot_payload": {"injected_slot": ["unsafe"]}, }, } out = validate_structure_overrides(payload) assert out == {"zone-top": {"slot_order": ["slot_title"]}} def test_validate_structure_overrides_rejects_frame_swap_zone_with_no_lock_keys(): # If a per-zone payload contains ONLY frame-swap attempts (no # slot_order / hidden_slots), the whole zone gets dropped after # sanitization (no signal remains). payload = { "zone-attempt-swap": { "frame_id": "compare_v2", "template_id": "topic_left_right", }, } out = validate_structure_overrides(payload) assert out == {} def test_validate_structure_overrides_drops_non_list_slot_arrays(): payload = { "zone-top": { "slot_order": "not a list", "hidden_slots": {"also": "not a list"}, }, "zone-bottom": { "slot_order": 42, "hidden_slots": None, }, "zone-good": {"slot_order": ["slot_title"]}, } out = validate_structure_overrides(payload) assert out == {"zone-good": {"slot_order": ["slot_title"]}} def test_validate_structure_overrides_drops_bad_slot_key_entries(): payload = { "zone-top": { "slot_order": [ "good_slot", "", # empty string dropped 42, # non-string dropped None, # non-string dropped {"nested": "obj"}, # non-string dropped "another_good", ], "hidden_slots": ["", "valid_hide", 99, "valid_hide_2"], }, } out = validate_structure_overrides(payload) assert out == { "zone-top": { "slot_order": ["good_slot", "another_good"], "hidden_slots": ["valid_hide", "valid_hide_2"], }, } def test_validate_structure_overrides_dedupes_slot_key_entries(): payload = { "zone-top": { "slot_order": ["a", "b", "a", "c", "b"], "hidden_slots": ["x", "x", "y", "x"], }, } out = validate_structure_overrides(payload) assert out == { "zone-top": { "slot_order": ["a", "b", "c"], "hidden_slots": ["x", "y"], }, } def test_validate_structure_overrides_drops_empty_payload_after_sanitization(): # Per-zone payloads that have empty slot_order AND empty hidden_slots # after sanitization carry no signal → drop the zone entirely. payload = { "zone-empty-lists": {"slot_order": [], "hidden_slots": []}, "zone-only-bad-entries": {"slot_order": ["", None, 99]}, "zone-good": {"slot_order": ["slot_title"]}, } out = validate_structure_overrides(payload) assert out == {"zone-good": {"slot_order": ["slot_title"]}} def test_validate_structure_overrides_returns_fresh_nested_dicts_and_lists(): # Mutating the returned dict's per-zone payload (or any list inside) # must not leak back into the source. payload = { "zone-top": { "slot_order": ["a", "b"], "hidden_slots": ["x"], }, } out = validate_structure_overrides(payload) out["zone-top"]["slot_order"].append("mutated") out["zone-top"]["hidden_slots"].append("mutated_hide") out["zone-top"]["new_key"] = "leaked?" assert payload["zone-top"]["slot_order"] == ["a", "b"] assert payload["zone-top"]["hidden_slots"] == ["x"] assert "new_key" not in payload["zone-top"] # -- apply_structure_override ---------------------------------------------- def test_apply_structure_override_hide_only_mutates_in_place(): zone: dict = { "slot_title": ["title line"], "slot_body": ["body line"], "slot_caption": ["caption"], } assert apply_structure_override(zone, {"hidden_slots": ["slot_caption"]}) is True assert list(zone.keys()) == ["slot_title", "slot_body"] assert zone == { "slot_title": ["title line"], "slot_body": ["body line"], } def test_apply_structure_override_reorder_only_partial(): # Partial reorder — listed keys move to front in order; unmentioned # keys keep their original relative order at the tail. zone: dict = { "slot_title": ["t"], "slot_body": ["b"], "slot_caption": ["c"], "slot_aux": ["a"], } assert apply_structure_override(zone, {"slot_order": ["slot_aux", "slot_title"]}) is True assert list(zone.keys()) == ["slot_aux", "slot_title", "slot_body", "slot_caption"] def test_apply_structure_override_combines_hide_and_reorder(): zone: dict = { "slot_title": ["t"], "slot_body": ["b"], "slot_caption": ["c"], "slot_aux": ["a"], } override = { "slot_order": ["slot_aux", "slot_body"], "hidden_slots": ["slot_caption"], } assert apply_structure_override(zone, override) is True # slot_caption hidden; slot_aux + slot_body moved to front; remaining # (slot_title) appended at tail in original order. assert list(zone.keys()) == ["slot_aux", "slot_body", "slot_title"] def test_apply_structure_override_silently_skips_stale_slot_keys(): # Frame swap / layout regression — the prior render's override # references slot_keys that the new render's frame no longer emits. # The resolver must silently skip those without raising. zone: dict = {"slot_title": ["t"], "slot_body": ["b"]} override = { "slot_order": ["slot_phantom_1", "slot_body", "slot_phantom_2"], "hidden_slots": ["slot_does_not_exist"], } assert apply_structure_override(zone, override) is True # slot_body moves to front; slot_title appended at tail; phantoms # silently ignored; hidden_slots no-op. assert list(zone.keys()) == ["slot_body", "slot_title"] assert zone == {"slot_body": ["b"], "slot_title": ["t"]} def test_apply_structure_override_no_op_returns_false(): # Empty override → no mutation. zone: dict = {"slot_title": ["t"], "slot_body": ["b"]} snapshot = dict(zone) assert apply_structure_override(zone, {}) is False assert zone == snapshot assert list(zone.keys()) == ["slot_title", "slot_body"] def test_apply_structure_override_all_stale_returns_false(): # All slot_keys in the override are absent from zone → no mutation. zone: dict = {"slot_title": ["t"], "slot_body": ["b"]} snapshot = dict(zone) override = { "slot_order": ["phantom_a", "phantom_b"], "hidden_slots": ["phantom_c"], } assert apply_structure_override(zone, override) is False assert zone == snapshot assert list(zone.keys()) == ["slot_title", "slot_body"] def test_apply_structure_override_already_in_desired_order_returns_false(): # slot_order matches the existing key order exactly → no mutation. zone: dict = {"slot_title": ["t"], "slot_body": ["b"]} override = {"slot_order": ["slot_title", "slot_body"]} assert apply_structure_override(zone, override) is False assert list(zone.keys()) == ["slot_title", "slot_body"] def test_apply_structure_override_preserves_zone_reference_identity(): # In-place mutation via clear + update — caller's reference must # remain valid after reorder. zone: dict = {"slot_a": ["a"], "slot_b": ["b"], "slot_c": ["c"]} zone_ref = zone # capture reference apply_structure_override(zone, {"slot_order": ["slot_c", "slot_a"]}) assert zone_ref is zone assert list(zone.keys()) == ["slot_c", "slot_a", "slot_b"] def test_apply_structure_override_never_inspects_per_slot_values(): # The resolver MUST NOT inspect / mutate per-slot list[str] contents. # Use weird non-list values to confirm passthrough. zone: dict = { "slot_a": ["a1", "a2", "a3"], "slot_b": {"nested": "object"}, # non-list payload — passthrough "slot_c": None, # None payload — passthrough "slot_d": 42, # int payload — passthrough } snapshot = {k: zone[k] for k in zone} apply_structure_override(zone, {"slot_order": ["slot_d", "slot_a"]}) assert list(zone.keys()) == ["slot_d", "slot_a", "slot_b", "slot_c"] # values are identity-preserved for key in zone: assert zone[key] is snapshot[key] def test_apply_structure_override_defensive_on_non_list_arrays(): # If a non-validated override leaks through, non-list slot_order / # hidden_slots should be treated as empty rather than raising. zone: dict = {"slot_title": ["t"], "slot_body": ["b"]} snapshot = dict(zone) override = { "slot_order": "not a list", "hidden_slots": {"also": "not a list"}, } assert apply_structure_override(zone, override) is False assert zone == snapshot assert list(zone.keys()) == ["slot_title", "slot_body"] def test_apply_structure_override_hide_wins_over_reorder(): # Edge case: a slot_key appears in BOTH slot_order and hidden_slots. # hidden_slots is applied first, so the slot is gone by the time # reorder runs — the reorder entry silently no-ops. zone: dict = {"slot_a": ["a"], "slot_b": ["b"], "slot_c": ["c"]} override = { "slot_order": ["slot_b", "slot_a", "slot_c"], "hidden_slots": ["slot_b"], } assert apply_structure_override(zone, override) is True # slot_b removed first; slot_a + slot_c reordered to front (slot_b # silently skipped because it no longer exists). assert list(zone.keys()) == ["slot_a", "slot_c"] def test_apply_structure_override_returns_true_on_pure_reorder_only(): # Pure reorder (no hide) — should still return True when key order # actually changes. zone: dict = {"slot_a": ["a"], "slot_b": ["b"]} override = {"slot_order": ["slot_b", "slot_a"]} assert apply_structure_override(zone, override) is True assert list(zone.keys()) == ["slot_b", "slot_a"] def test_apply_structure_override_returns_true_on_pure_hide_only(): # Pure hide (no reorder) — should still return True when a key was # actually removed. zone: dict = {"slot_a": ["a"], "slot_b": ["b"]} override = {"hidden_slots": ["slot_a"]} assert apply_structure_override(zone, override) is True assert list(zone.keys()) == ["slot_b"]