Files
DDI/server/internal/textdiff/textdiff_test.go
T
beug.k@beugsiud.deandClaude Opus 5.5 d5511b3286 feat: M4 contracts for push deployments, diff, rollback and live events
ADR 0022, OpenAPI additions (agent mode, deploy, rollback, diff, pending
changes, server-sent events), deployment_id in GetDesiredStateResponse
for pushes picked up by polling, and the textdiff package (Myers,
unified format).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:53:21 +02:00

115 lines
3.3 KiB
Go

// SPDX-License-Identifier: LicenseRef-DDI-Proprietary
package textdiff
import (
"strings"
"testing"
)
// apply rebuilds both sides from a diff.
func apply(lines []Line) (a, b []string) {
for _, l := range lines {
if l.Op != Insert {
a = append(a, l.Text)
}
if l.Op != Delete {
b = append(b, l.Text)
}
}
return a, b
}
func TestLinesReconstructsBothSides(t *testing.T) {
cases := [][2]string{
{"", ""},
{"", "a\nb\n"},
{"a\nb\n", ""},
{"a\nb\nc\n", "a\nb\nc\n"},
{"a\nb\nc\n", "a\nx\nc\n"},
{"a\nb\nc\nd\ne\n", "b\nc\nf\nd\n"},
{"x\ny\nz", "x\nq\ny\nz"},
}
for _, c := range cases {
lines := Lines(c[0], c[1])
a, b := apply(lines)
if strings.Join(a, "\n") != strings.Join(split(c[0]), "\n") ||
strings.Join(b, "\n") != strings.Join(split(c[1]), "\n") {
t.Errorf("%q -> %q: diff %+v does not reconstruct the inputs", c[0], c[1], lines)
}
}
}
func TestLinesIsMinimal(t *testing.T) {
ins, del := Stats(Lines("a\nb\nc\nd\ne\nf\n", "a\nb\nX\nd\ne\nf\nY\n"))
if ins != 2 || del != 1 {
t.Errorf("inserted %d, deleted %d; want 2 and 1", ins, del)
}
ins, del = Stats(Lines("a\nb\nc\nd\n", "b\nc\nd\na\n"))
if ins != 1 || del != 1 {
t.Errorf("rotation: inserted %d, deleted %d; want 1 and 1", ins, del)
}
}
func TestUnified(t *testing.T) {
a := "$TTL 3600\n@ IN SOA ns1 hostmaster 1 3600 600 1209600 300\n@ IN NS ns1\nns1 IN A 192.0.2.53\n" +
"www IN A 192.0.2.10\nz1 IN A 192.0.2.101\nz2 IN A 192.0.2.102\nz3 IN A 192.0.2.103\nz4 IN A 192.0.2.104\n" +
"z5 IN A 192.0.2.105\nz6 IN A 192.0.2.106\nz7 IN A 192.0.2.107\n"
b := strings.Replace(a, "hostmaster 1 ", "hostmaster 2 ", 1)
b = strings.Replace(b, "z7 IN A 192.0.2.107\n", "z7 IN A 192.0.2.107\nmail IN A 192.0.2.20\n", 1)
got := Unified(a, b, 3)
want := "@@ -1,5 +1,5 @@\n" +
" $TTL 3600\n" +
"-@ IN SOA ns1 hostmaster 1 3600 600 1209600 300\n" +
"+@ IN SOA ns1 hostmaster 2 3600 600 1209600 300\n" +
" @ IN NS ns1\n" +
" ns1 IN A 192.0.2.53\n" +
" www IN A 192.0.2.10\n" +
"@@ -10,3 +10,4 @@\n" +
" z5 IN A 192.0.2.105\n" +
" z6 IN A 192.0.2.106\n" +
" z7 IN A 192.0.2.107\n" +
"+mail IN A 192.0.2.20\n"
if got != want {
t.Errorf("Unified:\n%s\nwant:\n%s", got, want)
}
if Unified(a, a, 3) != "" {
t.Error("equal inputs produced a diff")
}
if got := Unified("", "x\n", 3); got != "@@ -0,0 +1 @@\n+x\n" {
t.Errorf("new file: %q", got)
}
if got := Unified("x\n", "", 3); got != "@@ -1 +0,0 @@\n-x\n" {
t.Errorf("removed file: %q", got)
}
// Changes close together share one hunk.
if got := Unified("1\n2\n3\n4\n5\n", "1\nX\n3\nY\n5\n", 1); strings.Count(got, "@@ -") != 1 {
t.Errorf("close changes split into several hunks:\n%s", got)
}
}
func TestLargeInputFallsBack(t *testing.T) {
var a, b strings.Builder
for i := 0; i < 3*maxEdits; i++ {
a.WriteString("a\n")
b.WriteString("b\n")
}
lines := Lines(a.String(), b.String())
ins, del := Stats(lines)
if ins != 3*maxEdits || del != 3*maxEdits {
t.Errorf("inserted %d, deleted %d", ins, del)
}
}
func FuzzLines(f *testing.F) {
f.Add("a\nb\nc\n", "a\nc\nd\n")
f.Add("", "x")
f.Fuzz(func(t *testing.T, a, b string) {
x, y := apply(Lines(a, b))
if strings.Join(x, "\n") != strings.Join(split(a), "\n") || strings.Join(y, "\n") != strings.Join(split(b), "\n") {
t.Fatalf("diff of %q and %q does not reconstruct the inputs", a, b)
}
_ = Unified(a, b, 3)
})
}