github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/committed/compare_iterator_test.go (about) 1 package committed_test 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 "github.com/go-test/deep" 9 "github.com/treeverse/lakefs/pkg/graveler" 10 "github.com/treeverse/lakefs/pkg/graveler/committed" 11 "github.com/treeverse/lakefs/pkg/graveler/testutil" 12 ) 13 14 var baseKeyToIdentity = map[string]string{"k1": "i1", "k2": "i2", "k3": "i3", "k4": "i4", "k6": "i6"} 15 16 const ( 17 added = graveler.DiffTypeAdded 18 removed = graveler.DiffTypeRemoved 19 changed = graveler.DiffTypeChanged 20 conflict = graveler.DiffTypeConflict 21 ) 22 23 func testMergeNewDiff(typ graveler.DiffType, key string, newIdentity string, oldIdentity string) graveler.Diff { 24 return graveler.Diff{ 25 Type: typ, 26 Key: graveler.Key(key), 27 Value: &graveler.Value{Identity: []byte(newIdentity)}, 28 LeftIdentity: []byte(oldIdentity), 29 } 30 } 31 32 func TestCompare(t *testing.T) { 33 tests := map[string]struct { 34 baseKeys []string 35 diffs []graveler.Diff 36 expectedKeys []string 37 expectedIdentities []string 38 expectedDiffTypes []graveler.DiffType 39 }{ 40 "added on right": { 41 baseKeys: []string{"k1", "k2"}, 42 diffs: []graveler.Diff{testMergeNewDiff(added, "k3", "i3", "")}, 43 expectedKeys: []string{"k3"}, 44 expectedIdentities: []string{"i3"}, 45 expectedDiffTypes: []graveler.DiffType{added}, 46 }, 47 "changed on right": { 48 baseKeys: []string{"k1", "k2"}, 49 diffs: []graveler.Diff{testMergeNewDiff(changed, "k2", "i2a", "i2")}, 50 expectedKeys: []string{"k2"}, 51 expectedIdentities: []string{"i2a"}, 52 expectedDiffTypes: []graveler.DiffType{changed}, 53 }, 54 "deleted on right": { 55 baseKeys: []string{"k1", "k2"}, 56 diffs: []graveler.Diff{testMergeNewDiff(removed, "k2", "i2", "i2")}, 57 expectedKeys: []string{"k2"}, 58 expectedIdentities: []string{"i2"}, 59 expectedDiffTypes: []graveler.DiffType{removed}, 60 }, 61 "added on left": { 62 baseKeys: []string{"k1"}, 63 diffs: []graveler.Diff{testMergeNewDiff(removed, "k2", "i2", "i2")}, 64 expectedIdentities: nil, 65 }, 66 "removed on left": { 67 baseKeys: []string{"k1", "k2"}, 68 diffs: []graveler.Diff{testMergeNewDiff(added, "k2", "i2", "")}, 69 expectedIdentities: nil, 70 }, 71 "changed on left": { 72 baseKeys: []string{"k1", "k2"}, 73 diffs: []graveler.Diff{testMergeNewDiff(changed, "k2", "i2", "i2a")}, 74 expectedIdentities: nil, 75 }, 76 "changed on both": { 77 baseKeys: []string{"k1", "k2"}, 78 diffs: []graveler.Diff{testMergeNewDiff(changed, "k2", "i2b", "i2a")}, 79 expectedKeys: []string{"k2"}, 80 expectedIdentities: []string{"i2b"}, 81 expectedDiffTypes: []graveler.DiffType{conflict}, 82 }, 83 "changed on left, removed on right": { 84 baseKeys: []string{"k1", "k2"}, 85 diffs: []graveler.Diff{testMergeNewDiff(removed, "k2", "i2a", "i2a")}, 86 expectedKeys: []string{"k2"}, 87 expectedIdentities: []string{"i2a"}, 88 expectedDiffTypes: []graveler.DiffType{conflict}, 89 }, 90 "removed on left, changed on right": { 91 baseKeys: []string{"k1", "k2"}, 92 diffs: []graveler.Diff{testMergeNewDiff(added, "k2", "i2a", "")}, 93 expectedIdentities: []string{"i2a"}, 94 expectedKeys: []string{"k2"}, 95 expectedDiffTypes: []graveler.DiffType{conflict}, 96 }, 97 "added on both with different identities": { 98 baseKeys: []string{"k1"}, 99 diffs: []graveler.Diff{testMergeNewDiff(changed, "k2", "i2a", "i2b")}, 100 expectedIdentities: []string{"i2a"}, 101 expectedKeys: []string{"k2"}, 102 expectedDiffTypes: []graveler.DiffType{conflict}, 103 }, 104 "multiple add and removes": { 105 baseKeys: []string{"k1", "k3"}, 106 diffs: []graveler.Diff{ 107 testMergeNewDiff(removed, "k1", "i1", "i1"), 108 testMergeNewDiff(added, "k2", "i2", ""), 109 testMergeNewDiff(removed, "k3", "i3", "i3"), 110 testMergeNewDiff(added, "k4", "i4", ""), 111 }, 112 expectedKeys: []string{"k1", "k2", "k3", "k4"}, 113 expectedIdentities: []string{"i1", "i2", "i3", "i4"}, 114 expectedDiffTypes: []graveler.DiffType{removed, added, removed, added}, 115 }, 116 "changes on each side": { 117 baseKeys: []string{"k1", "k2", "k3", "k4"}, 118 diffs: []graveler.Diff{ 119 testMergeNewDiff(changed, "k1", "i1a", "i1"), 120 testMergeNewDiff(changed, "k2", "i2", "i2a"), 121 testMergeNewDiff(changed, "k3", "i3a", "i3"), 122 testMergeNewDiff(changed, "k4", "i4", "i4a"), 123 }, 124 expectedKeys: []string{"k1", "k3"}, 125 expectedIdentities: []string{"i1a", "i3a"}, 126 expectedDiffTypes: []graveler.DiffType{changed, changed}, 127 }, 128 } 129 130 for name, tst := range tests { 131 t.Run(name, func(t *testing.T) { 132 diffIt := testutil.NewDiffIter(tst.diffs) 133 defer diffIt.Close() 134 base := makeBaseIterator(tst.baseKeys) 135 136 ctx := context.Background() 137 it := committed.NewCompareValueIterator(ctx, committed.NewDiffIteratorWrapper(diffIt), base) 138 defer it.Close() 139 140 var gotValues, gotKeys []string 141 var gotDiffTypes []graveler.DiffType 142 idx := 0 143 for it.Next() { 144 idx++ 145 gotKeys = append(gotKeys, string(it.Value().Key)) 146 gotValues = append(gotValues, string(it.Value().Value.Identity)) 147 gotDiffTypes = append(gotDiffTypes, it.Value().Type) 148 } 149 if it.Err() != nil { 150 t.Fatalf("got unexpected error: %v", it.Err()) 151 } 152 if diff := deep.Equal(tst.expectedKeys, gotKeys); diff != nil { 153 t.Fatalf("got unexpected keys from merge iterator. diff=%s", diff) 154 } 155 if diff := deep.Equal(tst.expectedIdentities, gotValues); diff != nil { 156 t.Fatalf("got unexpected values from merge iterator. diff=%s", diff) 157 } 158 if diff := deep.Equal(tst.expectedDiffTypes, gotDiffTypes); diff != nil { 159 t.Fatalf("got unexpected diff types from merge iterator. diff=%s", diff) 160 } 161 }) 162 } 163 } 164 165 func TestCompareSeek(t *testing.T) { 166 diffs := []graveler.Diff{ 167 testMergeNewDiff(added, "k1", "i1", ""), 168 testMergeNewDiff(removed, "k2", "i2", "i2"), 169 testMergeNewDiff(changed, "k3", "i3a", "i3"), 170 testMergeNewDiff(added, "k4", "i4", ""), 171 testMergeNewDiff(removed, "k5", "i5", "i5"), 172 testMergeNewDiff(changed, "k6", "i6", "i6a"), 173 testMergeNewDiff(added, "k7", "i7", ""), 174 testMergeNewDiff(removed, "k8", "i8", "i8"), 175 testMergeNewDiff(changed, "k9", "i9a", "i9"), 176 } 177 diffIt := testutil.NewDiffIter(diffs) 178 baseKeys := []string{"k2", "k3", "k4", "k6"} 179 base := makeBaseIterator(baseKeys) 180 ctx := context.Background() 181 it := committed.NewCompareValueIterator(ctx, committed.NewDiffIteratorWrapper(diffIt), base) 182 // expected diffs, +k1, -k2, Chng:k3,+k7, Conf:k9, 183 defer it.Close() 184 tests := []struct { 185 seekTo string 186 expectedKeys []string 187 expectedIdentities []string 188 expectedDiffTypes []graveler.DiffType 189 }{ 190 { 191 seekTo: "k1", 192 expectedKeys: []string{"k1", "k2", "k3", "k7", "k9"}, 193 expectedIdentities: []string{"i1", "i2", "i3a", "i7", "i9a"}, 194 expectedDiffTypes: []graveler.DiffType{added, removed, changed, added, conflict}, 195 }, 196 { 197 seekTo: "k2", 198 expectedKeys: []string{"k2", "k3", "k7", "k9"}, 199 expectedIdentities: []string{"i2", "i3a", "i7", "i9a"}, 200 expectedDiffTypes: []graveler.DiffType{removed, changed, added, conflict}, 201 }, 202 203 { 204 seekTo: "k3", 205 expectedKeys: []string{"k3", "k7", "k9"}, 206 expectedIdentities: []string{"i3a", "i7", "i9a"}, 207 expectedDiffTypes: []graveler.DiffType{changed, added, conflict}, 208 }, 209 210 { 211 seekTo: "k4", 212 expectedKeys: []string{"k7", "k9"}, 213 expectedIdentities: []string{"i7", "i9a"}, 214 expectedDiffTypes: []graveler.DiffType{added, conflict}, 215 }, 216 { 217 seekTo: "k8", 218 expectedKeys: []string{"k9"}, 219 expectedIdentities: []string{"i9a"}, 220 expectedDiffTypes: []graveler.DiffType{conflict}, 221 }, 222 { 223 seekTo: "k9", 224 expectedKeys: []string{"k9"}, 225 expectedIdentities: []string{"i9a"}, 226 expectedDiffTypes: []graveler.DiffType{conflict}, 227 }, 228 { 229 seekTo: "k99", 230 expectedKeys: nil, 231 expectedIdentities: nil, 232 }, 233 } 234 for _, tst := range tests { 235 t.Run(fmt.Sprintf("seek to %s", tst.seekTo), func(t *testing.T) { 236 it.SeekGE([]byte(tst.seekTo)) 237 if it.Value() != nil { 238 t.Fatalf("value expected to be nil after SeekGE. got=%v", it.Value()) 239 } 240 idx := 0 241 var gotValues, gotKeys []string 242 var gotDiffTypes []graveler.DiffType 243 for it.Next() { 244 idx++ 245 gotKeys = append(gotKeys, string(it.Value().Key)) 246 gotDiffTypes = append(gotDiffTypes, it.Value().Type) 247 gotValues = append(gotValues, string(it.Value().Value.Identity)) 248 } 249 if it.Err() != nil { 250 t.Fatalf("got unexpected error: %v", it.Err()) 251 } 252 if diff := deep.Equal(tst.expectedKeys, gotKeys); diff != nil { 253 t.Fatalf("got unexpected keys from compare iterator. diff=%s", diff) 254 } 255 if diff := deep.Equal(tst.expectedIdentities, gotValues); diff != nil { 256 t.Fatalf("got unexpected values from compare iterator. diff=%s", diff) 257 } 258 if diff := deep.Equal(tst.expectedDiffTypes, gotDiffTypes); diff != nil { 259 t.Fatalf("got unexpected diff types from compare iterator. diff=%s", diff) 260 } 261 }) 262 } 263 } 264 265 func makeBaseIterator(keys []string) *testutil.FakeIterator { 266 base := testutil.NewFakeIterator() 267 if len(keys) == 0 { 268 return base 269 } 270 var baseRecords []*graveler.ValueRecord 271 for _, key := range keys { 272 baseRecords = append(baseRecords, &graveler.ValueRecord{ 273 Key: []byte(key), 274 Value: &graveler.Value{Identity: []byte(baseKeyToIdentity[key])}, 275 }) 276 } 277 base.AddRange(&committed.Range{ 278 ID: "range", 279 MinKey: []byte(keys[0]), 280 }) 281 base.AddValueRecords(baseRecords...) 282 return base 283 }