package platform import ( "encoding/json" "testing" ) const operationsFixture = `{"schema_version":2,"nodes":[{"id":"a","type":"plainText","position":{"x":0,"y":0},"width":240,"height":72,"data":{"kind":"plain-text","title":"original","text":"hello"}}],"edges":[],"viewport":{"x":0,"y":0,"zoom":1}}` func TestCanvasOperationBatchStagesAndMerges(t *testing.T) { initial := map[string]canvasSnapshot{"board": {BoardID: "board", Version: 7, Document: json.RawMessage(operationsFixture)}} operations := []canvasOperation{ {ID: "11111111-1111-4111-8111-111111111111", Type: "node.patch", CanvasID: "board", ElementID: "a", Patch: map[string]json.RawMessage{"data": json.RawMessage(`{"title":"renamed"}`)}}, {ID: "22222222-2222-4222-8222-222222222222", Type: "node.patch", CanvasID: "board", ElementID: "a", Patch: map[string]json.RawMessage{"position": json.RawMessage(`{"x":90,"y":20}`)}}, } result, err := applyCanvasOperations(initial, operations) if err != nil { t.Fatal(err) } if result["board"].Version != 8 { t.Fatal("version did not advance once") } var document struct { Nodes []struct { Position struct{ X float64 } Data struct { Title string Text string } } } if err = json.Unmarshal(result["board"].Document, &document); err != nil { t.Fatal(err) } if document.Nodes[0].Data.Title != "renamed" || document.Nodes[0].Data.Text != "hello" || document.Nodes[0].Position.X != 90 { t.Fatal("field merge lost unrelated content") } if string(initial["board"].Document) != operationsFixture { t.Fatal("input snapshot mutated") } } func TestCanvasOperationBatchRejectsWithoutPartialOutput(t *testing.T) { initial := map[string]canvasSnapshot{"board": {BoardID: "board", Version: 1, Document: json.RawMessage(operationsFixture)}} for _, operation := range []canvasOperation{ {ID: "11111111-1111-4111-8111-111111111111", Type: "node.patch", CanvasID: "board", ElementID: "a", Patch: map[string]json.RawMessage{"id": json.RawMessage(`"stolen"`)}}, {ID: "11111111-1111-4111-8111-111111111111", Type: "node.patch", CanvasID: "board", ElementID: "missing", Patch: map[string]json.RawMessage{"data": json.RawMessage(`{"title":"no"}`)}}, {ID: "11111111-1111-4111-8111-111111111111", Type: "edge.create", CanvasID: "board", ElementID: "edge", Record: json.RawMessage(`{"id":"edge","source":"a","target":"missing"}`)}, } { if result, err := applyCanvasOperations(initial, []canvasOperation{operation}); err == nil || result != nil { t.Fatal("invalid batch partially accepted") } } } func TestCanvasOperationBatchValidatesFinalGraphInsteadOfIntermediateState(t *testing.T) { initial := map[string]canvasSnapshot{"board": {BoardID: "board", Version: 1, Document: json.RawMessage(operationsFixture)}} operations := []canvasOperation{ {ID: "11111111-1111-4111-8111-111111111111", Type: "edge.create", CanvasID: "board", ElementID: "edge", Record: json.RawMessage(`{"id":"edge","source":"a","target":"b"}`)}, {ID: "22222222-2222-4222-8222-222222222222", Type: "node.create", CanvasID: "board", ElementID: "b", Record: json.RawMessage(`{"id":"b","type":"plainText","position":{"x":20,"y":20},"width":240,"height":72,"data":{"kind":"plain-text","title":"b","text":""}}`)}, } if _, err := applyCanvasOperations(initial, operations); err != nil { t.Fatal(err) } }