github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/committed/diff_test.go (about) 1 package committed_test 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/gob" 7 "errors" 8 "strings" 9 "testing" 10 11 "github.com/go-test/deep" 12 "github.com/treeverse/lakefs/pkg/graveler" 13 "github.com/treeverse/lakefs/pkg/graveler/committed" 14 "github.com/treeverse/lakefs/pkg/graveler/testutil" 15 ) 16 17 type diffTestRange struct { 18 MinKey string 19 MaxKey string 20 Count int64 21 } 22 23 func newDiffTestRange(p *committed.RangeDiff) *diffTestRange { 24 if p == nil || p.Range == nil { 25 return nil 26 } 27 return &diffTestRange{string(p.Range.MinKey), string(p.Range.MaxKey), p.Range.Count} 28 } 29 30 func TestDiff(t *testing.T) { 31 const ( 32 added = graveler.DiffTypeAdded 33 removed = graveler.DiffTypeRemoved 34 changed = graveler.DiffTypeChanged 35 ) 36 tests := map[string]struct { 37 leftKeys [][]string 38 leftIdentities [][]string 39 rightKeys [][]string 40 rightIdentities [][]string 41 expectedDiffKeys []string 42 expectedRanges []*diffTestRange 43 expectedDiffTypes []graveler.DiffType 44 expectedDiffIdentities []string 45 expectedLeftReadsByRange []int 46 expectedRightReadsByRange []int 47 }{ 48 "empty diff": { 49 leftKeys: [][]string{{"k1", "k2"}, {"k3"}}, 50 leftIdentities: [][]string{{"i1", "i2"}, {"i3"}}, 51 rightKeys: [][]string{{"k1", "k2"}, {"k3"}}, 52 rightIdentities: [][]string{{"i1", "i2"}, {"i3"}}, 53 expectedDiffKeys: []string{}, 54 expectedRanges: []*diffTestRange{}, 55 expectedLeftReadsByRange: []int{0, 0}, 56 expectedRightReadsByRange: []int{0, 0}, 57 }, 58 "added in existing rng": { 59 leftKeys: [][]string{{"k1", "k2"}, {"k3"}}, 60 leftIdentities: [][]string{{"i1", "i2"}, {"i3"}}, 61 rightKeys: [][]string{{"k1", "k2"}, {"k3", "k4"}}, 62 rightIdentities: [][]string{{"i1", "i2"}, {"i3", "i4"}}, 63 expectedDiffKeys: []string{"k4"}, 64 expectedRanges: []*diffTestRange{nil}, 65 expectedDiffTypes: []graveler.DiffType{added}, 66 expectedDiffIdentities: []string{"i4"}, 67 expectedLeftReadsByRange: []int{0, 1}, 68 expectedRightReadsByRange: []int{0, 2}, 69 }, 70 "removed from existing rng": { 71 leftKeys: [][]string{{"k1", "k2"}, {"k3", "k4"}}, 72 leftIdentities: [][]string{{"i1", "i2"}, {"i3", "i4"}}, 73 rightKeys: [][]string{{"k1", "k2"}, {"k3"}}, 74 rightIdentities: [][]string{{"i1", "i2"}, {"i3"}}, 75 expectedDiffKeys: []string{"k4"}, 76 expectedRanges: []*diffTestRange{nil}, 77 expectedDiffTypes: []graveler.DiffType{removed}, 78 expectedDiffIdentities: []string{"i4"}, 79 expectedLeftReadsByRange: []int{0, 2}, 80 expectedRightReadsByRange: []int{0, 1}, 81 }, 82 "added and removed": { 83 leftKeys: [][]string{{"k1", "k2"}, {"k3", "k5"}}, 84 leftIdentities: [][]string{{"i1", "i2"}, {"i3", "i5"}}, 85 rightKeys: [][]string{{"k1", "k2"}, {"k3", "k4"}}, 86 rightIdentities: [][]string{{"i1", "i2"}, {"i3", "i4"}}, 87 expectedDiffKeys: []string{"k4", "k5"}, 88 expectedRanges: []*diffTestRange{nil, nil}, 89 expectedDiffTypes: []graveler.DiffType{added, removed}, 90 expectedDiffIdentities: []string{"i4", "i5"}, 91 expectedLeftReadsByRange: []int{0, 2}, 92 expectedRightReadsByRange: []int{0, 2}, 93 }, 94 "change in existing rng": { 95 leftKeys: [][]string{{"k1", "k2"}, {"k3"}}, 96 leftIdentities: [][]string{{"i1", "i2"}, {"i3"}}, 97 rightKeys: [][]string{{"k1", "k2"}, {"k3"}}, 98 rightIdentities: [][]string{{"i1", "i2"}, {"i3a"}}, 99 expectedDiffKeys: []string{"k3"}, 100 expectedRanges: []*diffTestRange{{"k3", "k3", 1}, nil}, 101 expectedDiffTypes: []graveler.DiffType{changed}, 102 expectedDiffIdentities: []string{"i3a"}, 103 expectedLeftReadsByRange: []int{0, 1}, 104 expectedRightReadsByRange: []int{0, 1}, 105 }, 106 "ranges were split": { 107 leftKeys: [][]string{{"k1", "k2", "k3"}}, 108 leftIdentities: [][]string{{"i1", "i2", "i3"}}, 109 rightKeys: [][]string{{"k3", "k4"}, {"k5", "k6"}}, 110 rightIdentities: [][]string{{"i3a", "i4"}, {"i5", "i6"}}, 111 expectedDiffKeys: []string{"k1", "k2", "k3", "k4", "k5", "k6"}, 112 expectedRanges: []*diffTestRange{nil, nil, nil, nil, {"k5", "k6", 2}, {"k5", "k6", 2}, {"k5", "k6", 2}}, 113 expectedDiffTypes: []graveler.DiffType{removed, removed, changed, added, added, added}, 114 expectedDiffIdentities: []string{"i1", "i2", "i3a", "i4", "i5", "i6"}, 115 expectedLeftReadsByRange: []int{3}, 116 expectedRightReadsByRange: []int{2, 2}, 117 }, 118 "diff between empty iterators": { 119 expectedDiffKeys: []string{}, 120 expectedRanges: []*diffTestRange{}, 121 }, 122 "added on empty": { 123 leftKeys: [][]string{}, 124 leftIdentities: [][]string{}, 125 rightKeys: [][]string{{"k1", "k2"}, {"k3"}}, 126 rightIdentities: [][]string{{"i1", "i2"}, {"i3"}}, 127 expectedDiffKeys: []string{"k1", "k2", "k3"}, 128 expectedRanges: []*diffTestRange{{"k1", "k2", 2}, {"k1", "k2", 2}, {"k1", "k2", 2}, {"k3", "k3", 1}, {"k3", "k3", 1}}, 129 expectedDiffTypes: []graveler.DiffType{added, added, added}, 130 expectedDiffIdentities: []string{"i1", "i2", "i3"}, 131 expectedLeftReadsByRange: nil, 132 expectedRightReadsByRange: []int{2, 1}, 133 }, 134 "whole rng was replaced": { 135 leftKeys: [][]string{{"k1", "k2"}, {"k3", "k4", "k5", "k6"}}, 136 leftIdentities: [][]string{{"i1", "i2"}, {"i3", "i4", "i5", "i6"}}, 137 rightKeys: [][]string{{"k3", "k4"}, {"k5", "k6", "k7"}}, 138 rightIdentities: [][]string{{"i3", "i4"}, {"i5", "i6", "i7"}}, 139 expectedDiffKeys: []string{"k1", "k2", "k7"}, 140 expectedRanges: []*diffTestRange{{"k1", "k2", 2}, {"k1", "k2", 2}, {"k1", "k2", 2}, nil}, 141 expectedDiffTypes: []graveler.DiffType{removed, removed, added}, 142 expectedDiffIdentities: []string{"i1", "i2", "i7"}, 143 expectedLeftReadsByRange: []int{2, 4}, 144 expectedRightReadsByRange: []int{2, 3}, 145 }, 146 "added in beginning of rng": { 147 leftKeys: [][]string{{"k3", "k4", "k5"}}, 148 leftIdentities: [][]string{{"i3", "i4", "i5"}}, 149 rightKeys: [][]string{{"k1", "k2", "k3", "k4", "k5"}}, 150 rightIdentities: [][]string{{"i1", "i2", "i3", "i4", "i5"}}, 151 expectedDiffKeys: []string{"k1", "k2"}, 152 expectedRanges: []*diffTestRange{nil, nil}, 153 expectedDiffTypes: []graveler.DiffType{added, added}, 154 expectedDiffIdentities: []string{"i1", "i2"}, 155 expectedLeftReadsByRange: []int{3}, 156 expectedRightReadsByRange: []int{5}, 157 }, 158 "small ranges removed": { 159 leftKeys: [][]string{{"k1", "k2"}, {"k3"}, {"k4"}, {"k5"}, {"k6", "k7"}}, 160 leftIdentities: [][]string{{"i1", "i2"}, {"i3"}, {"i4"}, {"i5"}, {"i6", "i7"}}, 161 rightKeys: [][]string{{"k1", "k2"}, {"k6", "k7"}}, 162 rightIdentities: [][]string{{"i1", "i2"}, {"i6", "i7"}}, 163 expectedDiffKeys: []string{"k3", "k4", "k5"}, 164 expectedRanges: []*diffTestRange{{"k3", "k3", 1}, {"k3", "k3", 1}, {"k4", "k4", 1}, {"k4", "k4", 1}, {"k5", "k5", 1}, {"k5", "k5", 1}}, 165 expectedDiffTypes: []graveler.DiffType{removed, removed, removed}, 166 expectedDiffIdentities: []string{"i3", "i4", "i5"}, 167 expectedLeftReadsByRange: []int{0, 1, 1, 1, 0}, 168 expectedRightReadsByRange: []int{0, 0}, 169 }, 170 "small ranges merged": { 171 leftKeys: [][]string{{"k1", "k2"}, {"k3"}, {"k4"}, {"k5"}, {"k6", "k7"}}, 172 leftIdentities: [][]string{{"i1", "i2"}, {"i3"}, {"i4"}, {"i5"}, {"i6", "i7"}}, 173 rightKeys: [][]string{{"k1", "k2"}, {"k4", "k5"}}, 174 rightIdentities: [][]string{{"i1", "i2"}, {"i4", "i5"}}, 175 expectedDiffKeys: []string{"k3", "k6", "k7"}, 176 expectedRanges: []*diffTestRange{{"k3", "k3", 1}, {"k3", "k3", 1}, {"k6", "k7", 2}, {"k6", "k7", 2}, {"k6", "k7", 2}}, 177 expectedDiffTypes: []graveler.DiffType{removed, removed, removed}, 178 expectedDiffIdentities: []string{"i3", "i6", "i7"}, 179 expectedLeftReadsByRange: []int{0, 1, 1, 1, 2}, 180 expectedRightReadsByRange: []int{0, 2}, 181 }, 182 "empty ranges": { 183 leftKeys: [][]string{{"k1", "k2"}, {}, {}, {}, {}, {"k3", "k4"}}, 184 leftIdentities: [][]string{{"i1", "i2"}, {}, {}, {}, {}, {"i3", "i4"}}, 185 rightKeys: [][]string{{"k1", "k2"}, {}, {}, {"k3", "k4"}}, 186 rightIdentities: [][]string{{"i1", "i2"}, {}, {}, {"i3", "i4"}}, 187 expectedDiffKeys: []string{}, 188 expectedRanges: []*diffTestRange{{"", "", 0}, {"", "", 0}}, 189 expectedDiffTypes: []graveler.DiffType{}, 190 expectedDiffIdentities: []string{}, 191 expectedLeftReadsByRange: []int{0, 0, 0, 0, 0, 0}, 192 expectedRightReadsByRange: []int{0, 0, 0, 0}, 193 }, 194 "rng added in the middle": { 195 leftKeys: [][]string{{"k1", "k2"}, {"k5", "k6"}}, 196 leftIdentities: [][]string{{"i1", "i2"}, {"i5", "i6"}}, 197 rightKeys: [][]string{{"k1", "k2"}, {"k3", "k4"}, {"k5", "k6"}}, 198 rightIdentities: [][]string{{"i1", "i2"}, {"i3", "i4"}, {"i5", "i6"}}, 199 expectedDiffKeys: []string{"k3", "k4"}, 200 expectedRanges: []*diffTestRange{{"k3", "k4", 2}, {"k3", "k4", 2}, {"k3", "k4", 2}}, 201 expectedDiffTypes: []graveler.DiffType{added, added}, 202 expectedDiffIdentities: []string{"i3", "i4"}, 203 expectedLeftReadsByRange: []int{0, 0}, 204 expectedRightReadsByRange: []int{0, 2, 0}, 205 }, 206 "identical ranges in the middle": { 207 leftKeys: [][]string{{"k1", "k2"}, {"k3", "k4"}, {"k5", "k6"}}, 208 leftIdentities: [][]string{{"i1", "i2"}, {"i3", "i4"}, {"i5", "i6"}}, 209 rightKeys: [][]string{{"k1", "k2"}, {"k3", "k4"}, {"k5", "k6"}}, 210 rightIdentities: [][]string{{"i1", "i2a"}, {"i3", "i4"}, {"i5", "i6a"}}, 211 expectedDiffKeys: []string{"k2", "k6"}, 212 expectedRanges: []*diffTestRange{{"k1", "k2", 2}, nil, {"k5", "k6", 2}, nil}, 213 expectedDiffTypes: []graveler.DiffType{changed, changed}, 214 expectedDiffIdentities: []string{"i2a", "i6a"}, 215 expectedLeftReadsByRange: []int{2, 0, 2}, 216 expectedRightReadsByRange: []int{2, 0, 2}, 217 }, 218 } 219 for name, tst := range tests { 220 t.Run(name, func(t *testing.T) { 221 fakeLeft := newFakeMetaRangeIterator(tst.leftKeys, tst.leftIdentities) 222 fakeRight := newFakeMetaRangeIterator(tst.rightKeys, tst.rightIdentities) 223 ctx := context.Background() 224 it := committed.NewDiffIterator(ctx, fakeLeft, fakeRight) 225 defer it.Close() 226 var diffs []*graveler.Diff 227 actualDiffKeys := make([]string, 0) 228 actualDiffRanges := make([]*diffTestRange, 0) 229 for it.Next() { 230 diff, rng := it.Value() 231 actualDiffRanges = append(actualDiffRanges, newDiffTestRange(rng)) 232 if diff != nil { 233 actualDiffKeys = append(actualDiffKeys, string(diff.Key)) 234 diffs = append(diffs, diff) 235 } 236 } 237 if it.Err() != nil { 238 t.Fatalf("got unexpected error: %v", it.Err()) 239 } 240 if diff := deep.Equal(tst.expectedDiffKeys, actualDiffKeys); diff != nil { 241 t.Fatalf("keys in diff different than expected. diff=%s", diff) 242 } 243 if diff := deep.Equal(tst.expectedRanges, actualDiffRanges); diff != nil { 244 t.Fatalf("ranges in diff different than expected. diff=%s", diff) 245 } 246 for i, d := range diffs { 247 if d.Type != tst.expectedDiffTypes[i] { 248 t.Fatalf("unexpected key in diff index %d. expected=%s, got=%s", i, tst.expectedDiffKeys[i], string(d.Key)) 249 } 250 if string(d.Value.Identity) != tst.expectedDiffIdentities[i] { 251 t.Fatalf("unexpected identity in diff index %d. expected=%s, got=%s", i, tst.expectedDiffIdentities[i], string(d.Value.Identity)) 252 } 253 } 254 if diff := deep.Equal(tst.expectedLeftReadsByRange, fakeLeft.ReadsByRange()); diff != nil { 255 t.Fatalf("unexpected number of reads on left ranges. diff=%s", diff) 256 } 257 if diff := deep.Equal(tst.expectedRightReadsByRange, fakeRight.ReadsByRange()); diff != nil { 258 t.Fatalf("unexpected number of reads on right ranges. diff=%s", diff) 259 } 260 }) 261 } 262 } 263 264 func TestDiffCancelContext(t *testing.T) { 265 left := newFakeMetaRangeIterator([][]string{{"k1", "k2"}}, [][]string{{"v1", "v2"}}) 266 right := newFakeMetaRangeIterator([][]string{{"k1", "k2"}}, [][]string{{"v1", "v2"}}) 267 ctx, cancel := context.WithCancel(context.Background()) 268 cancel() 269 it := committed.NewDiffValueIterator(ctx, left, right) 270 defer it.Close() 271 if it.Next() { 272 t.Fatal("Next() should return false") 273 } 274 if err := it.Err(); !errors.Is(err, context.Canceled) { 275 t.Fatalf("Err() returned %v, should return context.Canceled", err) 276 } 277 } 278 279 // TODO(Guys): add test for range changed 280 func TestNextRange(t *testing.T) { 281 ctx := context.Background() 282 it := committed.NewDiffIterator(ctx, 283 newFakeMetaRangeIterator([][]string{{"k1", "k2"}, {"k3", "k4"}}, [][]string{{"i1", "i2"}, {"i3", "i4"}}), 284 newFakeMetaRangeIterator([][]string{{"k3", "k4", "k5"}, {"k6", "k7"}}, [][]string{{"i3a", "i4a", "i5a"}, {"i6", "i7"}})) 285 286 t.Run("next range - in middle of range", func(t *testing.T) { 287 if !it.Next() { // move to range k1-k2 288 t.Fatal("expected iterator to have value") 289 } 290 record, _ := it.Value() 291 if record != nil { 292 t.Errorf("expected record to be nil got %v", record) 293 } 294 if !it.Next() { // move to k1 295 t.Fatalf("expected it.Next() to return true (err %v)", it.Err()) 296 } 297 record, _ = it.Value() 298 if record == nil || string(record.Key) != "k1" { 299 t.Errorf("expected record with key=k1, got record %v", record) 300 } 301 if !it.NextRange() { // move to k3 (moves to end of current range, but can't start a new range because k3 is part of two different ranges) 302 t.Fatalf("expected it.NextRange() to return true (err %v)", it.Err()) 303 } 304 record, rng := it.Value() 305 if rng != nil { 306 t.Errorf("expected range to be nil got range %v", rng) 307 } 308 if record == nil || string(record.Key) != "k3" { 309 t.Errorf("expected record with key=k3, got record %v", record) 310 } 311 if !it.Next() { // move to k4 312 t.Fatalf("expected it.Next() to return true (err %v)", it.Err()) 313 } 314 if !it.Next() { // move to k5 315 t.Fatalf("expected it.Next() to return true (err %v)", it.Err()) 316 } 317 if !it.Next() { // move to range k6-k7 318 t.Fatalf("expected it.Next() to return true (err %v)", it.Err()) 319 } 320 record, _ = it.Value() 321 if record != nil { 322 t.Errorf("expected record to be nil got record %v", record) 323 } 324 if !it.Next() { // move to k6 325 t.Fatalf("expected it.Next() to return true (err %v)", it.Err()) 326 } 327 record, _ = it.Value() 328 if record == nil || string(record.Key) != "k6" { 329 t.Errorf("expected record with key=k6, got record %v", record) 330 } 331 if it.NextRange() { // move to end 332 t.Fatal("expected it.NextRange() to return false") 333 } 334 if err := it.Err(); err != nil { 335 t.Fatalf("unexpected error:%v", err) 336 } 337 }) 338 t.Run("call next range with no range", func(t *testing.T) { 339 it.SeekGE(graveler.Key("k3")) 340 if !it.Next() { 341 t.Fatal("expected iterator to have value") 342 } 343 record, rng := it.Value() 344 if record == nil || string(record.Key) != "k3" { 345 t.Fatal("expected record to have a value equal to k3") 346 } 347 if rng != nil { 348 t.Fatal("expected range to not have value") 349 } 350 if it.NextRange() { 351 t.Fatal("expected false from iterator after close") 352 } 353 if err := it.Err(); !errors.Is(err, committed.ErrNoRange) { 354 t.Fatalf("expected to get err=%s, got: %s", committed.ErrNoRange, err) 355 } 356 }) 357 } 358 359 func TestNextRangeChange(t *testing.T) { 360 ctx := context.Background() 361 it := committed.NewDiffIterator(ctx, 362 newFakeMetaRangeIterator([][]string{{"k1", "k3"}, {"k5", "k6"}}, [][]string{{"i1", "i3"}, {"i5", "i6"}}), 363 newFakeMetaRangeIterator([][]string{{"k1", "k2", "k3"}, {"k5", "k6"}}, [][]string{{"i1", "i2", "i3"}, {"i5", "i6"}})) 364 if !it.Next() { 365 t.Fatal("expected iterator to have value") 366 } 367 record, rng := it.Value() 368 if record != nil { 369 t.Errorf("expected record to be nil got record %v", record) 370 } 371 if rng.Type != changed { 372 t.Errorf("expected range diff type to be changed got %v", rng.Type) 373 } 374 if it.NextRange() { 375 t.Fatal("expected next range to return false") 376 } 377 } 378 379 func TestNextErr(t *testing.T) { 380 ctx := context.Background() 381 it := committed.NewDiffIterator(ctx, 382 newFakeMetaRangeIterator([][]string{{"k1", "k2"}}, [][]string{{"i1", "i2"}}), 383 newFakeMetaRangeIterator([][]string{{"k1", "k2", "k3"}}, [][]string{{"i1a", "i2a", "i3a"}})) 384 if !it.Next() { 385 t.Fatalf("unexptected result from it.Next(), expected true, got false with err=%s", it.Err()) 386 } 387 if it.NextRange() { 388 val, rng := it.Value() 389 t.Fatalf("unexptected result from it.NextRange(), expected false, got true with value=%v , rng=%v", val, rng) 390 } 391 if err := it.Err(); !errors.Is(err, committed.ErrNoRange) { 392 t.Fatalf("expected to get err=%s, got: %s", committed.ErrNoRange, err) 393 } 394 } 395 396 func TestSameBounds(t *testing.T) { 397 ctx := context.Background() 398 it := committed.NewDiffIterator(ctx, 399 newFakeMetaRangeIterator([][]string{{"k1", "k2"}, {"k3", "k4"}}, [][]string{{"i1", "i2"}, {"i3", "i4"}}), 400 newFakeMetaRangeIterator([][]string{{"k1", "k2"}, {"k3", "k4"}}, [][]string{{"i1a", "i2a"}, {"i3a", "i4a"}})) 401 if !it.Next() { 402 t.Fatalf("unexptected result from it.Next(), expected true, got false with err=%s", it.Err()) 403 } 404 if !it.NextRange() { 405 t.Fatalf("unexptected result from it.Next(), expected true, got false with err=%s", it.Err()) 406 } 407 val, _ := it.Value() 408 if val != nil { 409 t.Errorf("unexptected value after it.NextRange(), expected nil, got=%v", val) 410 } 411 if !it.Next() { 412 t.Fatalf("unexptected result from it.Next(), expected true, got false with err=%s", it.Err()) 413 } 414 val, _ = it.Value() 415 if val == nil || string(val.Value.Identity) != "i3a" { 416 t.Errorf("unexptected value after it.Next(), expected i3a, got=%v", val) 417 } 418 if err := it.Err(); err != nil { 419 t.Fatalf("unexpected error, got: %s", err) 420 } 421 } 422 423 func TestDiffSeek(t *testing.T) { 424 const ( 425 added = graveler.DiffTypeAdded 426 removed = graveler.DiffTypeRemoved 427 changed = graveler.DiffTypeChanged 428 ) 429 left := [][]string{{"k1", "k2"}, {"k4", "k5"}, {"k6", "k7"}} 430 right := [][]string{{"k1", "k3"}, {"k3a", "k3b"}, {"k4", "k5"}, {"k6", "k7"}} 431 leftIdentities := [][]string{{"i1", "i2"}, {"i4", "i5"}, {"i6", "i7"}} 432 rightIdentities := [][]string{{"i1", "i3"}, {"i2a", "i2b"}, {"i4", "i5"}, {"i6", "i7a"}} 433 diffTypeByKey := map[string]graveler.DiffType{"k2": removed, "k3a": added, "k3b": added, "k3": added, "k7": changed} 434 diffIdentityByKey := map[string]string{"k2": "i2", "k3a": "i2a", "k3b": "i2b", "k3": "i3", "k7": "i7a"} 435 ctx := context.Background() 436 it := committed.NewDiffIterator(ctx, newFakeMetaRangeIterator(left, leftIdentities), newFakeMetaRangeIterator(right, rightIdentities)) 437 defer it.Close() 438 439 tests := []struct { 440 seekTo string 441 expectedDiffKeys []string 442 expectedDiffRanges []*diffTestRange 443 }{ 444 { 445 seekTo: "k1", 446 expectedDiffKeys: []string{"k2", "k3", "k3a", "k3b", "k7"}, 447 expectedDiffRanges: []*diffTestRange{nil, nil, {"k3a", "k3b", 2}, {"k3a", "k3b", 2}, {"k3a", "k3b", 2}, {"k6", "k7", 2}, nil}, 448 }, 449 { 450 seekTo: "k2", 451 expectedDiffKeys: []string{"k2", "k3", "k3a", "k3b", "k7"}, 452 expectedDiffRanges: []*diffTestRange{nil, nil, {"k3a", "k3b", 2}, {"k3a", "k3b", 2}, {"k3a", "k3b", 2}, {"k6", "k7", 2}, nil}, 453 }, 454 { 455 seekTo: "k3", 456 expectedDiffKeys: []string{"k3", "k3a", "k3b", "k7"}, 457 expectedDiffRanges: []*diffTestRange{nil, {"k3a", "k3b", 2}, {"k3a", "k3b", 2}, {"k3a", "k3b", 2}, {"k6", "k7", 2}, nil}, 458 }, 459 { 460 seekTo: "k4", 461 expectedDiffKeys: []string{"k7"}, 462 expectedDiffRanges: []*diffTestRange{{"k6", "k7", 2}, nil}, 463 }, 464 { 465 seekTo: "k8", 466 expectedDiffKeys: []string{}, 467 expectedDiffRanges: []*diffTestRange{}, 468 }, 469 } 470 for _, tst := range tests { 471 t.Run(tst.seekTo, func(t *testing.T) { 472 it.SeekGE([]byte(tst.seekTo)) 473 diff, rangeDiff := it.Value() 474 if diff != nil || rangeDiff != nil { 475 t.Fatalf("value expected to be nil after SeekGE. got diff=%v rangeDiff=%v", diff, rangeDiff) 476 } 477 keys := make([]string, 0) 478 ranges := make([]*diffTestRange, 0) 479 for it.Next() { 480 currentDiff, currentRangeDiff := it.Value() 481 ranges = append(ranges, newDiffTestRange(currentRangeDiff)) 482 if currentDiff != nil { 483 key := currentDiff.Key.String() 484 identity := string(currentDiff.Value.Identity) 485 if currentDiff.Type != diffTypeByKey[key] { 486 t.Fatalf("unexpected diff type in index %d. expected=%d, got=%d", len(keys), diffTypeByKey[key], currentDiff.Type) 487 } 488 if identity != diffIdentityByKey[key] { 489 t.Fatalf("unexpected identity in diff index %d. expected=%s, got=%s", len(keys), diffIdentityByKey[key], identity) 490 } 491 keys = append(keys, key) 492 } 493 } 494 if diff := deep.Equal(keys, tst.expectedDiffKeys); diff != nil { 495 t.Fatal("unexpected keys in diff", diff) 496 } 497 if diff := deep.Equal(tst.expectedDiffRanges, ranges); diff != nil { 498 t.Fatalf("ranges in diff different than expected. diff=%s", diff) 499 } 500 }) 501 } 502 } 503 504 func TestNextOnClose(t *testing.T) { 505 ctx := context.Background() 506 it := committed.NewDiffIterator(ctx, newFakeMetaRangeIterator([][]string{{"k1", "k2"}}, [][]string{{"i1", "i2"}}), newFakeMetaRangeIterator([][]string{{"k1", "k2"}}, [][]string{{"i1a", "i2a"}})) 507 if !it.Next() { 508 t.Fatal("expected iterator to have value") 509 } 510 it.Close() 511 if it.Next() { 512 t.Fatal("expected false from iterator after close") 513 } 514 } 515 516 func TestDiffErr(t *testing.T) { 517 leftErr := errors.New("error from left") 518 leftIt := newFakeMetaRangeIterator([][]string{{"k1"}, {"k2"}}, [][]string{{"i1"}, {"i2"}}) 519 leftIt.SetErr(leftErr) 520 rightIt := newFakeMetaRangeIterator([][]string{{"k2"}}, [][]string{{"i2a"}}) 521 ctx := context.Background() 522 it := committed.NewDiffIterator(ctx, leftIt, rightIt) 523 defer it.Close() 524 if it.Next() { 525 t.Fatalf("expected false from iterator with error") 526 } 527 if !errors.Is(it.Err(), leftErr) { 528 t.Fatalf("unexpected error from iterator. expected=%v, got=%v", leftErr, it.Err()) 529 } 530 it.SeekGE([]byte("k2")) 531 if it.Err() != nil { 532 t.Fatalf("error expected to be nil after SeekGE. got=%v", it.Err()) 533 } 534 if it.Next() { 535 t.Fatalf("expected false from iterator with error") 536 } 537 if !errors.Is(it.Err(), leftErr) { 538 t.Fatalf("unexpected error from iterator. expected=%v, got=%v", leftErr, it.Err()) 539 } 540 rightErr := errors.New("error from right") 541 leftIt.SetErr(nil) 542 rightIt.SetErr(rightErr) 543 it.SeekGE([]byte("k2")) 544 if it.Err() != nil { 545 t.Fatalf("error expected to be nil after SeekGE. got=%v", it.Err()) 546 } 547 if it.Next() { 548 t.Fatalf("expected false from iterator with error") 549 } 550 if !errors.Is(it.Err(), rightErr) { 551 t.Fatalf("unexpected error from iterator. expected=%v, got=%v", rightErr, it.Err()) 552 } 553 } 554 555 func newFakeMetaRangeIterator(rangeKeys [][]string, rangeIdentities [][]string) *testutil.FakeIterator { 556 res := testutil.NewFakeIterator() 557 for rangeIdx, keys := range rangeKeys { 558 identities := rangeIdentities[rangeIdx] 559 var b bytes.Buffer 560 encoder := gob.NewEncoder(&b) 561 _ = encoder.Encode(rangeKeys[rangeIdx]) 562 _ = encoder.Encode(rangeIdentities[rangeIdx]) 563 var minKey, maxKey committed.Key 564 if len(rangeKeys[rangeIdx]) > 0 { 565 minKey = []byte(rangeKeys[rangeIdx][0]) 566 maxKey = []byte(rangeKeys[rangeIdx][len(rangeKeys[rangeIdx])-1]) 567 } 568 rangeValues := make([]*graveler.ValueRecord, 0, len(rangeKeys[rangeIdx])) 569 for idx := range keys { 570 rangeValues = append(rangeValues, &graveler.ValueRecord{ 571 Key: []byte(keys[idx]), 572 Value: &graveler.Value{ 573 Identity: []byte(identities[idx]), 574 Data: []byte("some-data"), 575 }, 576 }) 577 } 578 res. 579 AddRange(&committed.Range{ID: committed.ID(strings.Join(identities, "-")), MinKey: minKey, MaxKey: maxKey, Count: int64(len(rangeKeys[rangeIdx]))}). 580 AddValueRecords(rangeValues...) 581 } 582 return res 583 }