feat(#93): IMP-55 u1~u12 frontend manual section swap detection (manual_section_assignment bool axis + drag-only marker gate + dual-axis persistence + backend manual-true gate)
Some checks failed
Multi-MDX Regression (IMP-91) / multi-mdx-regression (push) Failing after 9s
Some checks failed
Multi-MDX Regression (IMP-91) / multi-mdx-regression (push) Failing after 9s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
|
||||
Covers the persisted axes called out in the Stage 2 plan
|
||||
(IMP-51 #79 u1 extended this to 5 axes by adding ``image_overrides``;
|
||||
IMP-45 #74 u1 extended to 6 axes by adding ``slide_css``):
|
||||
IMP-45 #74 u1 extended to 6 axes by adding ``slide_css``;
|
||||
IMP-55 #93 u1 extended to 7 axes by adding ``manual_section_assignment``):
|
||||
|
||||
1. Round-trip ``save`` → ``load`` (6 KNOWN_AXES + foreign top-level keys).
|
||||
1. Round-trip ``save`` → ``load`` (7 KNOWN_AXES + foreign top-level keys).
|
||||
2. Unknown-key passthrough (foreign axes preserved across partial merges).
|
||||
3. Missing / corrupt / non-object behavior (graceful ``{}`` + stderr warning).
|
||||
4. Invalid keys (``InvalidOverrideKey`` raised on traversal / separators /
|
||||
@@ -121,19 +122,26 @@ def _full_payload() -> dict:
|
||||
"img-1": {"x": 10.0, "y": 20.0, "w": 30.0, "h": 25.0},
|
||||
},
|
||||
"slide_css": "<style>.slide .frame-process-product .label { font-size: 14px; }</style>",
|
||||
"manual_section_assignment": True,
|
||||
}
|
||||
|
||||
|
||||
def test_known_axes_includes_image_overrides():
|
||||
"""IMP-51 #79 u1 — ``image_overrides`` is a known axis (now 6 total)."""
|
||||
"""IMP-51 #79 u1 — ``image_overrides`` is a known axis (now 7 total)."""
|
||||
assert "image_overrides" in KNOWN_AXES
|
||||
assert len(KNOWN_AXES) == 6
|
||||
assert len(KNOWN_AXES) == 7
|
||||
|
||||
|
||||
def test_known_axes_includes_slide_css():
|
||||
"""IMP-45 #74 u1 — ``slide_css`` is a known axis (6 total)."""
|
||||
"""IMP-45 #74 u1 — ``slide_css`` is a known axis (7 total)."""
|
||||
assert "slide_css" in KNOWN_AXES
|
||||
assert len(KNOWN_AXES) == 6
|
||||
assert len(KNOWN_AXES) == 7
|
||||
|
||||
|
||||
def test_known_axes_includes_manual_section_assignment():
|
||||
"""IMP-55 #93 u1 — bool intent marker is a known axis (7 total)."""
|
||||
assert "manual_section_assignment" in KNOWN_AXES
|
||||
assert len(KNOWN_AXES) == 7
|
||||
|
||||
|
||||
def test_save_then_load_round_trip(tmp_path):
|
||||
@@ -161,6 +169,7 @@ def test_save_partial_payload_preserves_other_axes(tmp_path):
|
||||
assert loaded["frames"] == _full_payload()["frames"]
|
||||
assert loaded["image_overrides"] == _full_payload()["image_overrides"]
|
||||
assert loaded["slide_css"] == _full_payload()["slide_css"]
|
||||
assert loaded["manual_section_assignment"] is True
|
||||
|
||||
|
||||
def test_save_partial_image_overrides_preserves_other_axes(tmp_path):
|
||||
@@ -183,6 +192,7 @@ def test_save_partial_image_overrides_preserves_other_axes(tmp_path):
|
||||
assert loaded["zone_sections"] == _full_payload()["zone_sections"]
|
||||
assert loaded["frames"] == _full_payload()["frames"]
|
||||
assert loaded["slide_css"] == _full_payload()["slide_css"]
|
||||
assert loaded["manual_section_assignment"] is True
|
||||
|
||||
|
||||
def test_save_axis_replaces_not_deep_merges(tmp_path):
|
||||
@@ -226,6 +236,24 @@ def test_save_preserves_foreign_top_level_keys(tmp_path):
|
||||
assert loaded["schema_version"] == pre_seed["schema_version"]
|
||||
|
||||
|
||||
def test_save_manual_section_assignment_round_trips_both_booleans(tmp_path):
|
||||
"""IMP-55 #93 u1 — bool axis round-trips true/false and clears on None.
|
||||
|
||||
Asserts the bool is preserved literally (not coerced to int / string) so
|
||||
the backend pipeline (u9) can branch on ``is True`` without false-positive
|
||||
matches from truthy-but-not-True values seeded by older callers.
|
||||
"""
|
||||
key = "03"
|
||||
save(key, {"manual_section_assignment": True}, root=tmp_path)
|
||||
assert load(key, root=tmp_path)["manual_section_assignment"] is True
|
||||
|
||||
save(key, {"manual_section_assignment": False}, root=tmp_path)
|
||||
assert load(key, root=tmp_path)["manual_section_assignment"] is False
|
||||
|
||||
save(key, {"manual_section_assignment": None}, root=tmp_path)
|
||||
assert "manual_section_assignment" not in load(key, root=tmp_path)
|
||||
|
||||
|
||||
def test_save_creates_parent_directory(tmp_path):
|
||||
nested = tmp_path / "deep" / "nest"
|
||||
assert not nested.exists()
|
||||
@@ -241,6 +269,7 @@ def test_save_writes_pretty_sorted_json_for_diffability(tmp_path):
|
||||
pos_frames = raw.index('"frames"')
|
||||
pos_image_overrides = raw.index('"image_overrides"')
|
||||
pos_layout = raw.index('"layout"')
|
||||
pos_manual = raw.index('"manual_section_assignment"')
|
||||
pos_slide_css = raw.index('"slide_css"')
|
||||
pos_zg = raw.index('"zone_geometries"')
|
||||
pos_zs = raw.index('"zone_sections"')
|
||||
@@ -248,6 +277,7 @@ def test_save_writes_pretty_sorted_json_for_diffability(tmp_path):
|
||||
pos_frames
|
||||
< pos_image_overrides
|
||||
< pos_layout
|
||||
< pos_manual
|
||||
< pos_slide_css
|
||||
< pos_zg
|
||||
< pos_zs
|
||||
|
||||
Reference in New Issue
Block a user