From 24b27dcac4283d1f1decc7fe4c060cd25feba5cf Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sun, 8 Nov 2026 02:52:29 +0800 Subject: [PATCH] Verify atomic scaled-container unit metadata and inverse writes --- .../platform/canvas_model_integration_test.go | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/internal/platform/canvas_model_integration_test.go b/internal/platform/canvas_model_integration_test.go index 469b3cc..963e799 100644 --- a/internal/platform/canvas_model_integration_test.go +++ b/internal/platform/canvas_model_integration_test.go @@ -257,4 +257,48 @@ func TestCanonicalModelMigrationAndIncrementalLifecycle(t *testing.T) { if model.Versions[newCanvas] != 3 || modelInt(model.Elements[newFrame]["incarnation"]) != 2 { t.Fatal("partition restore reused a stale lifecycle or version") } + + // A cross-scale entry move writes ownership, child binding and presentation units together. + innerCanvas, innerFrame, memberID := modelTestID(5100), modelTestID(5101), modelTestID(5102) + container := modelTestNode(innerFrame, root) + container["componentId"] = "frame" + originalPresentation := response{"contentScale": .75, "future": response{"keep": "metadata"}} + container["data"].(map[string]any)["presentation"] = originalPresentation + member := modelTestNode(memberID, innerCanvas) + member["data"].(map[string]any)["fontSize"] = float64(17) + inner := modelCanvas{ID: innerCanvas, Parent: &modelParent{CanvasID: root, EntryElementID: innerFrame}} + absent := false + edit(1013, map[string]int64{root: model.Versions[root], newCanvas: model.Versions[newCanvas], innerCanvas: 0}, + modelChange{Kind: "field", Target: modelTarget{ID: newFrame, Incarnation: 2}, Field: "data.presentation", Before: modelJSON(nil), BeforePresent: &absent, After: modelJSON(response{"contentScale": 2})}, + modelChange{Kind: "element", ID: innerFrame, Before: modelJSON(nil), After: modelJSON(container)}, + modelChange{Kind: "canvas", ID: innerCanvas, Before: modelJSON(nil), After: modelJSON(inner)}, + modelChange{Kind: "element", ID: memberID, Before: modelJSON(nil), After: modelJSON(member)}) + model = read() + oldGeometry := model.Elements[innerFrame]["geometry"] + geometry := oldGeometry.(map[string]any) + normalizedGeometry := response{"x": (geometry["x"].(float64) - 24) / 2, "y": (geometry["y"].(float64) - 36) / 2, "width": geometry["width"].(float64) / 2, "height": geometry["height"].(float64) / 2, "rotation": geometry["rotation"]} + normalizedPresentation := response{"contentScale": .75, "unitScale": .5, "future": response{"keep": "metadata"}} + movedInner := inner + movedInner.Parent = &modelParent{CanvasID: newCanvas, EntryElementID: innerFrame} + edit(1014, map[string]int64{root: model.Versions[root], newCanvas: model.Versions[newCanvas], innerCanvas: model.Versions[innerCanvas]}, + modelChange{Kind: "field", Target: modelTarget{ID: innerFrame, Incarnation: 1}, Field: "ownerCanvasId", Before: modelJSON(root), After: modelJSON(newCanvas)}, + modelChange{Kind: "field", Target: modelTarget{ID: innerFrame, Incarnation: 1}, Field: "geometry", Before: modelJSON(oldGeometry), After: modelJSON(normalizedGeometry)}, + modelChange{Kind: "field", Target: modelTarget{ID: innerFrame, Incarnation: 1}, Field: "data.presentation", Before: modelJSON(originalPresentation), After: modelJSON(normalizedPresentation)}, + modelChange{Kind: "canvas", ID: innerCanvas, Before: modelJSON(inner), After: modelJSON(movedInner)}) + model = read() + if modelString(model.Elements[innerFrame]["ownerCanvasId"]) != newCanvas || model.Canvases[innerCanvas].Parent.CanvasID != newCanvas || !modelEqual(model.Elements[innerFrame]["data"].(map[string]any)["presentation"], normalizedPresentation) || !modelEqual(model.Elements[memberID], member) { + t.Fatal("scaled container move changed its member or lost the atomic unit/ownership boundary") + } + if call("GET", base+"/model/operations/"+modelTestID(1014), nil, "", 200)["status"] != "applied" { + t.Fatal("scaled move receipt was not queryable by its original identity") + } + edit(1015, map[string]int64{root: model.Versions[root], newCanvas: model.Versions[newCanvas], innerCanvas: model.Versions[innerCanvas]}, + modelChange{Kind: "field", Target: modelTarget{ID: innerFrame, Incarnation: 1}, Field: "ownerCanvasId", Before: modelJSON(newCanvas), After: modelJSON(root)}, + modelChange{Kind: "field", Target: modelTarget{ID: innerFrame, Incarnation: 1}, Field: "geometry", Before: modelJSON(normalizedGeometry), After: modelJSON(oldGeometry)}, + modelChange{Kind: "field", Target: modelTarget{ID: innerFrame, Incarnation: 1}, Field: "data.presentation", Before: modelJSON(normalizedPresentation), After: modelJSON(originalPresentation)}, + modelChange{Kind: "canvas", ID: innerCanvas, Before: modelJSON(movedInner), After: modelJSON(inner)}) + model = read() + if _, exists := model.Elements[innerFrame]["data"].(map[string]any)["presentation"].(map[string]any)["unitScale"]; exists || !modelEqual(model.Elements[memberID], member) || model.Canvases[innerCanvas].Parent.CanvasID != root { + t.Fatal("scaled move inverse did not restore absent unit metadata and the original child binding") + } }