fix(design): preserve existing canvas nodes when compressed snapshot omits them
SaveCanvas treated a nil Nodes/Connections slice from a compressed snapshot as an empty canvas, wiping existing content. Distinguish nil (field omitted, keep the project's current nodes/connections) from an explicit empty slice. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -182,6 +182,60 @@ func TestSaveCanvasAcceptsCompressedSnapshotPayload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSaveCanvasPreservesNodesWhenCompressedSnapshotOmitsThem(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
repo := memory.NewProjectRepository()
|
||||||
|
service := NewDesignService(repo, nil, nil, nil, nil)
|
||||||
|
service.now = func() time.Time {
|
||||||
|
return time.Unix(101, 0).UTC()
|
||||||
|
}
|
||||||
|
project := design.Project{
|
||||||
|
ID: "project-null-snapshot-nodes",
|
||||||
|
Title: "Null snapshot nodes",
|
||||||
|
Status: design.StatusReady,
|
||||||
|
UpdatedAt: time.Unix(100, 0).UTC(),
|
||||||
|
Version: "100000",
|
||||||
|
SnapshotID: "snapshot-old",
|
||||||
|
Viewport: design.Viewport{X: 1, Y: 2, K: 1},
|
||||||
|
Nodes: []design.Node{
|
||||||
|
{ID: "existing-image", Type: design.NodeTypeImage, Title: "Existing", Content: "http://localhost:19000/canvas-assets/existing.png", Status: "success"},
|
||||||
|
},
|
||||||
|
Connections: []design.Connection{
|
||||||
|
{ID: "existing-connection", FromNodeID: "existing-image", ToNodeID: "existing-image"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := repo.Save(ctx, project); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
snapshotProject := project
|
||||||
|
snapshotProject.SnapshotID = "snapshot-client"
|
||||||
|
snapshotProject.Viewport = design.Viewport{X: 12, Y: 34, K: 0.8}
|
||||||
|
snapshotProject.Nodes = nil
|
||||||
|
snapshotProject.Connections = nil
|
||||||
|
canvas, err := design.EncodeCanvasSnapshot(snapshotProject)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
saved, err := service.SaveCanvas(ctx, project.ID, design.Viewport{}, nil, nil, "", "", canvas)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if saved.SnapshotID != "snapshot-client" {
|
||||||
|
t.Fatalf("expected client snapshot id, got %s", saved.SnapshotID)
|
||||||
|
}
|
||||||
|
if saved.Viewport != snapshotProject.Viewport {
|
||||||
|
t.Fatalf("unexpected viewport: %#v", saved.Viewport)
|
||||||
|
}
|
||||||
|
if len(saved.Nodes) != 1 || saved.Nodes[0].ID != "existing-image" {
|
||||||
|
t.Fatalf("expected existing nodes to be preserved, got %#v", saved.Nodes)
|
||||||
|
}
|
||||||
|
if len(saved.Connections) != 1 || saved.Connections[0].ID != "existing-connection" {
|
||||||
|
t.Fatalf("expected existing connections to be preserved, got %#v", saved.Connections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSaveCanvasDoesNotDeleteImageAssetReferencedByAgentMessage(t *testing.T) {
|
func TestSaveCanvasDoesNotDeleteImageAssetReferencedByAgentMessage(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
repo := memory.NewProjectRepository()
|
repo := memory.NewProjectRepository()
|
||||||
|
|||||||
@@ -1082,10 +1082,18 @@ func (s *DesignService) SaveCanvas(ctx context.Context, projectID string, viewpo
|
|||||||
return design.Project{}, fmt.Errorf("%w: invalid canvas snapshot", design.ErrInvalidInput)
|
return design.Project{}, fmt.Errorf("%w: invalid canvas snapshot", design.ErrInvalidInput)
|
||||||
}
|
}
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
nodes = snapshot.Nodes
|
if snapshot.Nodes != nil {
|
||||||
|
nodes = snapshot.Nodes
|
||||||
|
} else {
|
||||||
|
nodes = append([]design.Node(nil), project.Nodes...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(connections) == 0 {
|
if len(connections) == 0 {
|
||||||
connections = snapshot.Connections
|
if snapshot.Connections != nil {
|
||||||
|
connections = snapshot.Connections
|
||||||
|
} else {
|
||||||
|
connections = append([]design.Connection(nil), project.Connections...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if viewport.K == 0 {
|
if viewport.K == 0 {
|
||||||
viewport = snapshot.Viewport
|
viewport = snapshot.Viewport
|
||||||
|
|||||||
Reference in New Issue
Block a user