Files
aego/art/batch_test.go
sanspie a46347e2c6
ci-platforms / darwin arm64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 12m19s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
github-native / gitea-pending (push) Skipped
github-native / darwin amd64 native (push) Skipped
github-native / linux amd64 native (push) Skipped
github-native / windows amd64 native (push) Skipped
github-native / darwin arm64 native (push) Skipped
github-native / gitea-result (push) Skipped
ci / checks (push) Failing after 23m57s
ci / release (push) Skipped
GitHub / windows amd64 native GitHub-hosted native CI: failure
GitHub / darwin arm64 native GitHub-hosted native CI: failure
GitHub / darwin amd64 native GitHub-hosted native CI: failure
GitHub / linux amd64 native GitHub-hosted native CI: success
editor: add art studio and stage 12.5 workflows
2026-11-08 18:47:57 +03:00

107 lines
3.6 KiB
Go

package art
import (
"context"
"errors"
"fmt"
"image"
"reflect"
"testing"
)
func TestBatchEightFramesAliasesAndUserUndo(t *testing.T) {
s, j := sessionForTest(t)
base := s.Document.Info()
brush := Brush{Size: 1, Paint: Paint{Color: Color{255, 20, 40, 255}}}
ops := []Operation{{Kind: "stroke", Layer: s.Layer, Frame: s.Frame, Brush: brush, Points: []image.Point{{1, 1}}}}
last := s.Frame
for n := 1; n < 8; n++ {
name := fmt.Sprintf("frame%d", n)
ops = append(ops, Operation{Kind: "add-frame", Frame: last, DurationMS: 80 + n, As: name})
last = ID("$" + name)
ops = append(ops, Operation{Kind: "stroke", Layer: s.Layer, Frame: last, Brush: brush, Points: []image.Point{{n + 1, 1}}})
}
ops = append(ops, Operation{Kind: "tag", Tag: &Tag{Name: "walk", From: s.Frame, To: last, Direction: Forward}})
change, ids, err := s.Document.Propose(context.Background(), base.Revision, "eight frames", ops)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(base, s.Document.Info()) {
t.Fatal("proposal mutated visible state")
}
if len(ids) != len(ops) {
t.Fatal("missing operation results")
}
if err := j.Exec(change); err != nil {
t.Fatal(err)
}
info := s.Document.Info()
if len(info.Frames) != 8 || len(info.Tags) != 1 || s.Info().Undo != 1 {
t.Fatal("batch must be one complete undo step")
}
frame := info.Frames[4].ID
im, err := s.Document.Render(frame, RenderOptions{})
if err != nil || im.NRGBAAt(5, 1).A != 255 {
t.Fatalf("frame pixels: %v", err)
}
stale, _, err := s.Document.Propose(context.Background(), info.Revision, "stale agent", []Operation{{Kind: "duration", Frame: frame, DurationMS: 300}})
if err != nil {
t.Fatal(err)
}
_, err = s.Execute(context.Background(), Operation{Kind: "stroke", Frame: frame, Layer: s.Layer, Brush: brush, Points: []image.Point{{12, 12}}})
if err != nil {
t.Fatal(err)
}
if err = j.Exec(stale); !errors.Is(err, ErrConflict) {
t.Fatalf("stale proposal: %v", err)
}
snapshot, err := s.Document.Snapshot()
if err != nil {
t.Fatal(err)
}
im, err = snapshot.Render(frame, RenderOptions{})
if err != nil || im.NRGBAAt(12, 12).A != 255 {
t.Fatalf("dirty capture: %v", err)
}
if err = s.Undo(); err != nil {
t.Fatal(err)
}
if err = s.Undo(); err != nil {
t.Fatal(err)
}
restored := s.Document.Info()
restored.Revision = base.Revision
if !reflect.DeepEqual(base, restored) {
t.Fatal("batch undo did not restore content and links")
}
}
func TestBatchRejectsBadReferencesAtomically(t *testing.T) {
for _, ops := range [][]Operation{
{{Kind: "add-frame", As: "same"}, {Kind: "add-frame", As: "same"}},
{{Kind: "add-layer", Name: "x", LayerKind: Raster, As: "../bad"}},
{{Kind: "add-layer", Name: "x", LayerKind: Raster, As: "ok"}, {Kind: "add-frame", Frame: "$missing"}},
} {
s, _ := sessionForTest(t)
before := s.Document.Info()
if _, _, err := s.Document.Propose(context.Background(), before.Revision, "invalid", ops); err == nil {
t.Fatal("invalid batch accepted")
}
if !reflect.DeepEqual(before, s.Document.Info()) {
t.Fatal("failed batch leaked changes")
}
}
}
func TestDecodeOperationsBoundsAndSchema(t *testing.T) {
for _, raw := range []string{"null", "[]", `[{'kind':'stroke'}]`, `[{"kind":"add-frame","shell":"bad"}]`, `[{"kind":"add-frame"}] null`} {
if _, err := DecodeOperations([]byte(raw)); err == nil {
t.Fatalf("accepted %s", raw)
}
}
if _, err := DecodeOperations(make([]byte, MaxOperationBytes+1)); !errors.Is(err, ErrLimit) {
t.Fatal(err)
}
if _, err := DecodeOperations([]byte(`[{"kind":"add-frame","as":"next","durationMS":100}]`)); err != nil {
t.Fatal(err)
}
}