github.com/gagliardetto/solana-go@v1.11.0/diff/diff_test.go (about) 1 // Copyright 2020 dfuse Platform Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package diff 16 17 import ( 18 "fmt" 19 "testing" 20 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func TestDiff(t *testing.T) { 26 type pair struct { 27 left interface{} 28 right interface{} 29 } 30 31 tests := []struct { 32 name string 33 in pair 34 expecteds []string 35 }{ 36 // Plain 37 {"plain - left nil, right nil", 38 pair{nil, nil}, 39 nil, 40 }, 41 {"plain - left nil, right set", 42 pair{nil, &plainStruct{}}, 43 []string{"<nil> => &{0} (*diff.plainStruct) (added)"}, 44 }, 45 {"plain - left set, right nil", 46 pair{&plainStruct{}, nil}, 47 []string{"&{0} (*diff.plainStruct) => <nil> (removed)"}, 48 }, 49 {"plain - equal", 50 pair{&plainStruct{}, &plainStruct{}}, 51 nil, 52 }, 53 {"plain - diff", 54 pair{&plainStruct{Field: 1}, &plainStruct{Field: 2}}, 55 []string{"1 (int) => 2 (int) (changed @ Field)"}, 56 }, 57 58 // Slice 59 {"slice - equal both nil", 60 pair{[]string(nil), []string(nil)}, 61 nil, 62 }, 63 {"slice - equal both length 0", 64 pair{[]string{}, []string{}}, 65 nil, 66 }, 67 {"slice - diff both length 1", 68 pair{[]string{"a"}, []string{"b"}}, 69 []string{ 70 "a (string) => <nil> (removed @ [0])", 71 "<nil> => b (string) (added @ [0])", 72 }, 73 }, 74 {"slice - diff both length 2 re-ordered", 75 pair{[]string{"a", "b"}, []string{"b", "a"}}, 76 []string{ 77 "a (string) => <nil> (removed @ [0])", 78 "<nil> => b (string) (added @ [0])", 79 "b (string) => <nil> (removed @ [1])", 80 "<nil> => a (string) (added @ [1])", 81 }, 82 }, 83 {"slice - diff left is longer than right, all different", 84 pair{[]string{"a", "b"}, []string{"c"}}, 85 []string{ 86 "a (string) => <nil> (removed @ [0])", 87 "<nil> => c (string) (added @ [0])", 88 "b (string) => <nil> (removed @ [1->?])", 89 }, 90 }, 91 {"slice - diff left is longer than right, some equals", 92 pair{[]string{"a", "b"}, []string{"a"}}, 93 []string{ 94 "b (string) => <nil> (removed @ [1->?])", 95 }, 96 }, 97 {"slice - diff left is smaller than right, all different", 98 pair{[]string{"a"}, []string{"b", "c"}}, 99 []string{ 100 "a (string) => <nil> (removed @ [0])", 101 "<nil> => b (string) (added @ [0])", 102 "<nil> => c (string) (added @ [?->1])", 103 }, 104 }, 105 {"slice - diff left is smaller than right, some equals", 106 pair{[]string{"a"}, []string{"a", "b"}}, 107 []string{ 108 "<nil> => b (string) (added @ [?->1])", 109 }, 110 }, 111 112 // Full 113 {"full - everything diff", 114 pair{ 115 &topStruct{ 116 Literal: "x", 117 Pointer: &plainStruct{Field: 1}, 118 Array: []string{"a", "b"}, 119 Child: &childStruct{Literal: "1", Pointer: &plainStruct{Field: 10}, Array: []string{"1", "2"}}, 120 }, 121 &topStruct{ 122 Literal: "y", 123 Pointer: &plainStruct{Field: 2}, 124 Array: []string{"b", "c"}, 125 Child: &childStruct{Literal: "2", Pointer: &plainStruct{Field: 20}, Array: []string{"2"}}, 126 }, 127 }, 128 []string{ 129 "x (string) => y (string) (changed @ Literal)", 130 "1 (int) => 2 (int) (changed @ Pointer.Field)", 131 "a (string) => <nil> (removed @ Array[0])", 132 "<nil> => b (string) (added @ Array[0])", 133 "b (string) => <nil> (removed @ Array[1])", 134 "<nil> => c (string) (added @ Array[1])", 135 "1 (string) => 2 (string) (changed @ Child.Literal)", 136 "10 (int) => 20 (int) (changed @ Child.Pointer.Field)", 137 "1 (string) => <nil> (removed @ Child.Array[0->?])", 138 }, 139 }, 140 } 141 142 for _, test := range tests { 143 t.Run(test.name, func(t *testing.T) { 144 actuals := accumulateDiffStrings(test.in.left, test.in.right) 145 assert.Equal(t, test.expecteds, actuals) 146 }) 147 } 148 } 149 150 func eventToString(event *Event) string { 151 path := "" 152 if len(event.Path) > 1 { 153 path = " @ " + event.Path.String() 154 } 155 156 return fmt.Sprintf("%s => %s (%s%s)", reflectValueToString(event.Old), reflectValueToString(event.New), event.Kind, path) 157 } 158 159 func TestDiff_EventMatch(t *testing.T) { 160 tests := []struct { 161 name string 162 left interface{} 163 right interface{} 164 pattern string 165 expectedMatch bool 166 expectedGroups []string 167 }{ 168 { 169 "deep array added one", 170 &topStruct{Child: &childStruct{Array: []string{"1"}}}, 171 &topStruct{Child: &childStruct{Array: []string{"1", "2"}}}, 172 "Child.Array[#]", 173 true, 174 []string{"?->1"}, 175 }, 176 } 177 178 for _, test := range tests { 179 t.Run(test.name, func(t *testing.T) { 180 events := accumulateDiff(test.left, test.right) 181 require.Len(t, events, 1) 182 183 match, groups := events[0].Match(test.pattern) 184 if test.expectedMatch { 185 assert.True(t, match, "Expected pattern %q to match diff path %q", test.pattern, events[0].Path) 186 assert.Equal(t, test.expectedGroups, groups) 187 } else { 188 assert.False(t, match, "Expected pattern %q to NOT match diff path %q", test.pattern, events[0].Path) 189 } 190 }) 191 } 192 } 193 194 // There is something inherently broken with this, the accumulation seems to broke leading to incorrect 195 // results. I assume it's a Golang thing related to slice and struct as value versus pointers. It works 196 // only single event but starts to act weirdly when there > 1, like the Event's Path is all wrong. It's 197 // better to try to avoid it when possible. 198 func accumulateDiff(left, right interface{}) (out []Event) { 199 Diff(left, right, OnEvent(func(event Event) { 200 out = append(out, event) 201 })) 202 return 203 } 204 205 func accumulateDiffStrings(left, right interface{}) (out []string) { 206 Diff(left, right, OnEvent(func(event Event) { 207 out = append(out, eventToString(&event)) 208 })) 209 return 210 } 211 212 type topStruct struct { 213 Literal string 214 Pointer *plainStruct 215 Array []string 216 Child *childStruct 217 } 218 219 type childStruct struct { 220 Literal string 221 Pointer *plainStruct 222 Array []string 223 } 224 225 type plainStruct struct { 226 Field int 227 }