solana-go/diff/diff_test.go

214 lines
5.5 KiB
Go

package diff
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDiff(t *testing.T) {
type pair struct {
left interface{}
right interface{}
}
tests := []struct {
name string
in pair
expecteds []string
}{
// Plain
{"plain - left nil, right nil",
pair{nil, nil},
nil,
},
{"plain - left nil, right set",
pair{nil, &plainStruct{}},
[]string{"<nil> => &{0} (*diff.plainStruct) (added)"},
},
{"plain - left set, right nil",
pair{&plainStruct{}, nil},
[]string{"&{0} (*diff.plainStruct) => <nil> (removed)"},
},
{"plain - equal",
pair{&plainStruct{}, &plainStruct{}},
nil,
},
{"plain - diff",
pair{&plainStruct{Field: 1}, &plainStruct{Field: 2}},
[]string{"1 (int) => 2 (int) (changed @ Field)"},
},
// Slice
{"slice - equal both nil",
pair{[]string(nil), []string(nil)},
nil,
},
{"slice - equal both length 0",
pair{[]string{}, []string{}},
nil,
},
{"slice - diff both length 1",
pair{[]string{"a"}, []string{"b"}},
[]string{
"a (string) => <nil> (removed @ [0])",
"<nil> => b (string) (added @ [0])",
},
},
{"slice - diff both length 2 re-ordered",
pair{[]string{"a", "b"}, []string{"b", "a"}},
[]string{
"a (string) => <nil> (removed @ [0])",
"<nil> => b (string) (added @ [0])",
"b (string) => <nil> (removed @ [1])",
"<nil> => a (string) (added @ [1])",
},
},
{"slice - diff left is longer than right, all different",
pair{[]string{"a", "b"}, []string{"c"}},
[]string{
"a (string) => <nil> (removed @ [0])",
"<nil> => c (string) (added @ [0])",
"b (string) => <nil> (removed @ [1->?])",
},
},
{"slice - diff left is longer than right, some equals",
pair{[]string{"a", "b"}, []string{"a"}},
[]string{
"b (string) => <nil> (removed @ [1->?])",
},
},
{"slice - diff left is smaller than right, all different",
pair{[]string{"a"}, []string{"b", "c"}},
[]string{
"a (string) => <nil> (removed @ [0])",
"<nil> => b (string) (added @ [0])",
"<nil> => c (string) (added @ [?->1])",
},
},
{"slice - diff left is smaller than right, some equals",
pair{[]string{"a"}, []string{"a", "b"}},
[]string{
"<nil> => b (string) (added @ [?->1])",
},
},
// Full
{"full - everything diff",
pair{
&topStruct{
Literal: "x",
Pointer: &plainStruct{Field: 1},
Array: []string{"a", "b"},
Child: &childStruct{Literal: "1", Pointer: &plainStruct{Field: 10}, Array: []string{"1", "2"}},
},
&topStruct{
Literal: "y",
Pointer: &plainStruct{Field: 2},
Array: []string{"b", "c"},
Child: &childStruct{Literal: "2", Pointer: &plainStruct{Field: 20}, Array: []string{"2"}},
},
},
[]string{
"x (string) => y (string) (changed @ Literal)",
"1 (int) => 2 (int) (changed @ Pointer.Field)",
"a (string) => <nil> (removed @ Array[0])",
"<nil> => b (string) (added @ Array[0])",
"b (string) => <nil> (removed @ Array[1])",
"<nil> => c (string) (added @ Array[1])",
"1 (string) => 2 (string) (changed @ Child.Literal)",
"10 (int) => 20 (int) (changed @ Child.Pointer.Field)",
"1 (string) => <nil> (removed @ Child.Array[0->?])",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actuals := accumulateDiffStrings(test.in.left, test.in.right)
assert.Equal(t, test.expecteds, actuals)
})
}
}
func eventToString(event *Event) string {
path := ""
if len(event.Path) > 1 {
path = " @ " + event.Path.String()
}
return fmt.Sprintf("%s => %s (%s%s)", reflectValueToString(event.Old), reflectValueToString(event.New), event.Kind, path)
}
func TestDiff_EventMatch(t *testing.T) {
tests := []struct {
name string
left interface{}
right interface{}
pattern string
expectedMatch bool
expectedGroups []string
}{
{
"deep array added one",
&topStruct{Child: &childStruct{Array: []string{"1"}}},
&topStruct{Child: &childStruct{Array: []string{"1", "2"}}},
"Child.Array[#]",
true,
[]string{"?->1"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
events := accumulateDiff(test.left, test.right)
require.Len(t, events, 1)
match, groups := events[0].Match(test.pattern)
if test.expectedMatch {
assert.True(t, match, "Expected pattern %q to match diff path %q", test.pattern, events[0].Path)
assert.Equal(t, test.expectedGroups, groups)
} else {
assert.False(t, match, "Expected pattern %q to NOT match diff path %q", test.pattern, events[0].Path)
}
})
}
}
// There is something inherently broken with this, the accumulation seems to broke leading to incorrect
// results. I assume it's a Golang thing related to slice and struct as value versus pointers. It works
// only single event but starts to act weirdly when there > 1, like the Event's Path is all wrong. It's
// better to try to avoid it when possible.
func accumulateDiff(left, right interface{}) (out []Event) {
Diff(left, right, OnEvent(func(event Event) {
out = append(out, event)
}))
return
}
func accumulateDiffStrings(left, right interface{}) (out []string) {
Diff(left, right, OnEvent(func(event Event) {
out = append(out, eventToString(&event))
}))
return
}
type topStruct struct {
Literal string
Pointer *plainStruct
Array []string
Child *childStruct
}
type childStruct struct {
Literal string
Pointer *plainStruct
Array []string
}
type plainStruct struct {
Field int
}