github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/internal/upload/uploadstore_test.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package upload_test 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "fmt" 12 "math" 13 "math/rand" 14 "os" 15 "testing" 16 "time" 17 18 storage "github.com/ethersphere/bee/v2/pkg/storage" 19 "github.com/ethersphere/bee/v2/pkg/storage/storagetest" 20 chunktest "github.com/ethersphere/bee/v2/pkg/storage/testing" 21 "github.com/ethersphere/bee/v2/pkg/storer/internal" 22 "github.com/ethersphere/bee/v2/pkg/storer/internal/transaction" 23 "github.com/ethersphere/bee/v2/pkg/storer/internal/upload" 24 "github.com/ethersphere/bee/v2/pkg/swarm" 25 "github.com/google/go-cmp/cmp" 26 "github.com/google/go-cmp/cmp/cmpopts" 27 ) 28 29 // now is a function that returns the current time and replaces time.Now. 30 var now = func() time.Time { return time.Unix(1234567890, 0) } 31 32 // TestMain exists to adjust the time.Now function to a fixed value. 33 func TestMain(m *testing.M) { 34 upload.ReplaceTimeNow(now) 35 code := m.Run() 36 upload.ReplaceTimeNow(time.Now) 37 os.Exit(code) 38 } 39 40 func TestPushItem(t *testing.T) { 41 t.Parallel() 42 43 tests := []struct { 44 name string 45 test *storagetest.ItemMarshalAndUnmarshalTest 46 }{{ 47 name: "zero values", 48 test: &storagetest.ItemMarshalAndUnmarshalTest{ 49 Item: &upload.PushItem{}, 50 Factory: func() storage.Item { return new(upload.PushItem) }, 51 MarshalErr: upload.ErrPushItemMarshalAddressIsZero, 52 }, 53 }, { 54 name: "zero address", 55 test: &storagetest.ItemMarshalAndUnmarshalTest{ 56 Item: &upload.PushItem{ 57 Timestamp: 1, 58 Address: swarm.ZeroAddress, 59 TagID: 1, 60 }, 61 Factory: func() storage.Item { return new(upload.PushItem) }, 62 MarshalErr: upload.ErrPushItemMarshalAddressIsZero, 63 }, 64 }, { 65 name: "nil stamp", 66 test: &storagetest.ItemMarshalAndUnmarshalTest{ 67 Item: &upload.PushItem{ 68 Timestamp: 1, 69 Address: swarm.NewAddress(storagetest.MinAddressBytes[:]), 70 TagID: 1, 71 }, 72 Factory: func() storage.Item { return new(upload.PushItem) }, 73 MarshalErr: upload.ErrPushItemMarshalBatchInvalid, 74 }, 75 }, { 76 name: "min values", 77 test: &storagetest.ItemMarshalAndUnmarshalTest{ 78 Item: &upload.PushItem{ 79 Timestamp: 0, 80 Address: swarm.NewAddress(storagetest.MinAddressBytes[:]), 81 BatchID: storagetest.MinAddressBytes[:], 82 TagID: 0, 83 }, 84 Factory: func() storage.Item { return new(upload.PushItem) }, 85 }, 86 }, { 87 name: "max values", 88 test: &storagetest.ItemMarshalAndUnmarshalTest{ 89 Item: &upload.PushItem{ 90 Timestamp: math.MaxInt64, 91 Address: swarm.NewAddress(storagetest.MaxAddressBytes[:]), 92 BatchID: storagetest.MaxAddressBytes[:], 93 TagID: math.MaxUint64, 94 }, 95 Factory: func() storage.Item { return new(upload.PushItem) }, 96 }, 97 }, { 98 name: "random values", 99 test: &storagetest.ItemMarshalAndUnmarshalTest{ 100 Item: &upload.PushItem{ 101 Timestamp: rand.Int63(), 102 Address: swarm.RandAddress(t), 103 BatchID: swarm.RandAddress(t).Bytes(), 104 TagID: rand.Uint64(), 105 }, 106 Factory: func() storage.Item { return new(upload.PushItem) }, 107 }, 108 }, { 109 name: "invalid size", 110 test: &storagetest.ItemMarshalAndUnmarshalTest{ 111 Item: &storagetest.ItemStub{ 112 MarshalBuf: []byte{0xFF}, 113 UnmarshalBuf: []byte{0xFF}, 114 }, 115 Factory: func() storage.Item { return new(upload.PushItem) }, 116 UnmarshalErr: upload.ErrPushItemUnmarshalInvalidSize, 117 }, 118 }} 119 120 for _, tc := range tests { 121 tc := tc 122 123 t.Run(fmt.Sprintf("%s marshal/unmarshal", tc.name), func(t *testing.T) { 124 t.Parallel() 125 126 storagetest.TestItemMarshalAndUnmarshal(t, tc.test) 127 }) 128 129 t.Run(fmt.Sprintf("%s clone", tc.name), func(t *testing.T) { 130 t.Parallel() 131 132 storagetest.TestItemClone(t, &storagetest.ItemCloneTest{ 133 Item: tc.test.Item, 134 CmpOpts: tc.test.CmpOpts, 135 }) 136 }) 137 } 138 } 139 140 func TestTagItem(t *testing.T) { 141 t.Parallel() 142 143 tests := []struct { 144 name string 145 test *storagetest.ItemMarshalAndUnmarshalTest 146 }{{ 147 name: "zero values", 148 test: &storagetest.ItemMarshalAndUnmarshalTest{ 149 Item: &upload.TagItem{}, 150 Factory: func() storage.Item { return new(upload.TagItem) }, 151 }, 152 }, { 153 name: "max values", 154 test: &storagetest.ItemMarshalAndUnmarshalTest{ 155 Item: &upload.TagItem{ 156 TagID: math.MaxUint64, 157 Split: math.MaxUint64, 158 Seen: math.MaxUint64, 159 Stored: math.MaxUint64, 160 Sent: math.MaxUint64, 161 Synced: math.MaxUint64, 162 Address: swarm.NewAddress(storagetest.MaxAddressBytes[:]), 163 StartedAt: math.MaxInt64, 164 }, 165 Factory: func() storage.Item { return new(upload.TagItem) }, 166 }, 167 }, { 168 name: "random values", 169 test: &storagetest.ItemMarshalAndUnmarshalTest{ 170 Item: &upload.TagItem{ 171 TagID: rand.Uint64(), 172 Split: rand.Uint64(), 173 Seen: rand.Uint64(), 174 Stored: rand.Uint64(), 175 Sent: rand.Uint64(), 176 Synced: rand.Uint64(), 177 Address: swarm.RandAddress(t), 178 StartedAt: rand.Int63(), 179 }, 180 Factory: func() storage.Item { return new(upload.TagItem) }, 181 }, 182 }, { 183 name: "invalid size", 184 test: &storagetest.ItemMarshalAndUnmarshalTest{ 185 Item: &storagetest.ItemStub{ 186 MarshalBuf: []byte{0xFF}, 187 UnmarshalBuf: []byte{0xFF}, 188 }, 189 Factory: func() storage.Item { return new(upload.TagItem) }, 190 UnmarshalErr: upload.ErrTagItemUnmarshalInvalidSize, 191 }, 192 }} 193 194 for _, tc := range tests { 195 tc := tc 196 197 t.Run(fmt.Sprintf("%s marshal/unmarshal", tc.name), func(t *testing.T) { 198 t.Parallel() 199 200 storagetest.TestItemMarshalAndUnmarshal(t, tc.test) 201 }) 202 203 t.Run(fmt.Sprintf("%s clone", tc.name), func(t *testing.T) { 204 t.Parallel() 205 206 storagetest.TestItemClone(t, &storagetest.ItemCloneTest{ 207 Item: tc.test.Item, 208 CmpOpts: tc.test.CmpOpts, 209 }) 210 }) 211 } 212 } 213 214 func TestUploadItem(t *testing.T) { 215 t.Parallel() 216 217 randomAddress := swarm.RandAddress(t) 218 randomBatchId := swarm.RandAddress(t).Bytes() 219 220 tests := []struct { 221 name string 222 test *storagetest.ItemMarshalAndUnmarshalTest 223 }{{ 224 name: "zero values", 225 test: &storagetest.ItemMarshalAndUnmarshalTest{ 226 Item: &upload.UploadItem{}, 227 Factory: func() storage.Item { return new(upload.UploadItem) }, 228 MarshalErr: upload.ErrUploadItemMarshalAddressIsZero, 229 }, 230 }, { 231 name: "zero address", 232 test: &storagetest.ItemMarshalAndUnmarshalTest{ 233 Item: &upload.UploadItem{ 234 Address: swarm.ZeroAddress, 235 BatchID: storagetest.MinAddressBytes[:], 236 }, 237 Factory: func() storage.Item { return new(upload.UploadItem) }, 238 MarshalErr: upload.ErrUploadItemMarshalAddressIsZero, 239 }, 240 }, { 241 name: "nil stamp", 242 test: &storagetest.ItemMarshalAndUnmarshalTest{ 243 Item: &upload.UploadItem{ 244 Address: swarm.NewAddress(storagetest.MinAddressBytes[:]), 245 }, 246 Factory: func() storage.Item { return new(upload.UploadItem) }, 247 MarshalErr: upload.ErrUploadItemMarshalBatchInvalid, 248 }, 249 }, { 250 name: "min values", 251 test: &storagetest.ItemMarshalAndUnmarshalTest{ 252 Item: &upload.UploadItem{ 253 Address: swarm.NewAddress(storagetest.MinAddressBytes[:]), 254 BatchID: storagetest.MinAddressBytes[:], 255 }, 256 Factory: func() storage.Item { 257 return &upload.UploadItem{ 258 Address: swarm.NewAddress(storagetest.MinAddressBytes[:]), 259 BatchID: storagetest.MinAddressBytes[:], 260 } 261 }, 262 }, 263 }, { 264 name: "max values", 265 test: &storagetest.ItemMarshalAndUnmarshalTest{ 266 Item: &upload.UploadItem{ 267 Address: swarm.NewAddress(storagetest.MaxAddressBytes[:]), 268 BatchID: storagetest.MaxAddressBytes[:], 269 TagID: math.MaxUint64, 270 Uploaded: math.MaxInt64, 271 Synced: math.MaxInt64, 272 }, 273 Factory: func() storage.Item { 274 return &upload.UploadItem{ 275 Address: swarm.NewAddress(storagetest.MaxAddressBytes[:]), 276 BatchID: storagetest.MaxAddressBytes[:], 277 } 278 }, 279 }, 280 }, { 281 name: "random values", 282 test: &storagetest.ItemMarshalAndUnmarshalTest{ 283 Item: &upload.UploadItem{ 284 Address: randomAddress, 285 BatchID: randomBatchId, 286 TagID: rand.Uint64(), 287 Uploaded: rand.Int63(), 288 Synced: rand.Int63(), 289 }, 290 Factory: func() storage.Item { 291 return &upload.UploadItem{ 292 Address: randomAddress, 293 BatchID: randomBatchId, 294 } 295 }, 296 }, 297 }, { 298 name: "invalid size", 299 test: &storagetest.ItemMarshalAndUnmarshalTest{ 300 Item: &storagetest.ItemStub{ 301 MarshalBuf: []byte{0xFF}, 302 UnmarshalBuf: []byte{0xFF}, 303 }, 304 Factory: func() storage.Item { return new(upload.UploadItem) }, 305 UnmarshalErr: upload.ErrUploadItemUnmarshalInvalidSize, 306 }, 307 }} 308 309 for _, tc := range tests { 310 tc := tc 311 312 t.Run(fmt.Sprintf("%s marshal/unmarshal", tc.name), func(t *testing.T) { 313 t.Parallel() 314 315 storagetest.TestItemMarshalAndUnmarshal(t, tc.test) 316 }) 317 318 t.Run(fmt.Sprintf("%s clone", tc.name), func(t *testing.T) { 319 t.Parallel() 320 321 storagetest.TestItemClone(t, &storagetest.ItemCloneTest{ 322 Item: tc.test.Item, 323 CmpOpts: tc.test.CmpOpts, 324 }) 325 }) 326 } 327 } 328 329 func TestItemNextTagID(t *testing.T) { 330 t.Parallel() 331 332 zeroValue := upload.NextTagID(0) 333 maxValue := upload.NextTagID(math.MaxUint64) 334 335 tests := []struct { 336 name string 337 test *storagetest.ItemMarshalAndUnmarshalTest 338 }{{ 339 name: "zero values", 340 test: &storagetest.ItemMarshalAndUnmarshalTest{ 341 Item: &zeroValue, 342 Factory: func() storage.Item { return new(upload.NextTagID) }, 343 }, 344 }, { 345 name: "max value", 346 test: &storagetest.ItemMarshalAndUnmarshalTest{ 347 Item: &maxValue, 348 Factory: func() storage.Item { return new(upload.NextTagID) }, 349 }, 350 }, { 351 name: "invalid size", 352 test: &storagetest.ItemMarshalAndUnmarshalTest{ 353 Item: &storagetest.ItemStub{ 354 MarshalBuf: []byte{0xFF}, 355 UnmarshalBuf: []byte{0xFF}, 356 }, 357 Factory: func() storage.Item { return new(upload.NextTagID) }, 358 UnmarshalErr: upload.ErrNextTagIDUnmarshalInvalidSize, 359 }, 360 }} 361 362 for _, tc := range tests { 363 tc := tc 364 365 t.Run(fmt.Sprintf("%s marshal/unmarshal", tc.name), func(t *testing.T) { 366 t.Parallel() 367 368 storagetest.TestItemMarshalAndUnmarshal(t, tc.test) 369 }) 370 371 t.Run(fmt.Sprintf("%s clone", tc.name), func(t *testing.T) { 372 t.Parallel() 373 374 storagetest.TestItemClone(t, &storagetest.ItemCloneTest{ 375 Item: tc.test.Item, 376 CmpOpts: tc.test.CmpOpts, 377 }) 378 }) 379 } 380 } 381 382 func TestItemDirtyTagItem(t *testing.T) { 383 t.Parallel() 384 385 tests := []struct { 386 name string 387 test *storagetest.ItemMarshalAndUnmarshalTest 388 }{{ 389 name: "zero values", 390 test: &storagetest.ItemMarshalAndUnmarshalTest{ 391 Item: &upload.DirtyTagItem{}, 392 Factory: func() storage.Item { return new(upload.DirtyTagItem) }, 393 }, 394 }, { 395 name: "max value", 396 test: &storagetest.ItemMarshalAndUnmarshalTest{ 397 Item: &upload.DirtyTagItem{TagID: math.MaxUint64, Started: math.MaxInt64}, 398 Factory: func() storage.Item { return new(upload.DirtyTagItem) }, 399 }, 400 }, { 401 name: "invalid size", 402 test: &storagetest.ItemMarshalAndUnmarshalTest{ 403 Item: &storagetest.ItemStub{ 404 MarshalBuf: []byte{0xFF}, 405 UnmarshalBuf: []byte{0xFF}, 406 }, 407 Factory: func() storage.Item { return new(upload.DirtyTagItem) }, 408 UnmarshalErr: upload.ErrDirtyTagItemUnmarshalInvalidSize, 409 }, 410 }} 411 412 for _, tc := range tests { 413 tc := tc 414 415 t.Run(fmt.Sprintf("%s marshal/unmarshal", tc.name), func(t *testing.T) { 416 t.Parallel() 417 418 storagetest.TestItemMarshalAndUnmarshal(t, tc.test) 419 }) 420 421 t.Run(fmt.Sprintf("%s clone", tc.name), func(t *testing.T) { 422 t.Parallel() 423 424 storagetest.TestItemClone(t, &storagetest.ItemCloneTest{ 425 Item: tc.test.Item, 426 CmpOpts: tc.test.CmpOpts, 427 }) 428 }) 429 } 430 } 431 432 func newTestStorage(t *testing.T) transaction.Storage { 433 t.Helper() 434 435 storg := internal.NewInmemStorage() 436 return storg 437 } 438 439 func TestChunkPutter(t *testing.T) { 440 t.Parallel() 441 442 ts := newTestStorage(t) 443 444 tx, done := ts.NewTransaction(context.Background()) 445 defer done() 446 tag, err := upload.NextTag(tx.IndexStore()) 447 if err != nil { 448 t.Fatalf("failed creating tag: %v", err) 449 } 450 451 putter, err := upload.NewPutter(tx.IndexStore(), tag.TagID) 452 if err != nil { 453 t.Fatalf("failed creating putter: %v", err) 454 } 455 _ = tx.Commit() 456 457 for _, chunk := range chunktest.GenerateTestRandomChunks(10) { 458 t.Run(fmt.Sprintf("chunk %s", chunk.Address()), func(t *testing.T) { 459 t.Run("put new chunk", func(t *testing.T) { 460 err := ts.Run(context.Background(), func(s transaction.Store) error { 461 return putter.Put(context.Background(), s, chunk) 462 }) 463 if err != nil { 464 t.Fatalf("Put(...): unexpected error: %v", err) 465 } 466 }) 467 468 t.Run("put existing chunk", func(t *testing.T) { 469 err := ts.Run(context.Background(), func(s transaction.Store) error { 470 return putter.Put(context.Background(), s, chunk) 471 }) 472 if err != nil { 473 t.Fatalf("Put(...): unexpected error: %v", err) 474 } 475 }) 476 477 t.Run("verify internal state", func(t *testing.T) { 478 ui := &upload.UploadItem{ 479 Address: chunk.Address(), 480 BatchID: chunk.Stamp().BatchID(), 481 } 482 err := ts.IndexStore().Get(ui) 483 if err != nil { 484 t.Fatalf("Get(...): unexpected error: %v", err) 485 } 486 wantUI := &upload.UploadItem{ 487 Address: chunk.Address(), 488 BatchID: chunk.Stamp().BatchID(), 489 TagID: tag.TagID, 490 Uploaded: now().UnixNano(), 491 } 492 493 if diff := cmp.Diff(wantUI, ui); diff != "" { 494 t.Fatalf("Get(...): unexpected UploadItem (-want +have):\n%s", diff) 495 } 496 497 pi := &upload.PushItem{ 498 Timestamp: now().UnixNano(), 499 Address: chunk.Address(), 500 BatchID: chunk.Stamp().BatchID(), 501 } 502 err = ts.IndexStore().Get(pi) 503 if err != nil { 504 t.Fatalf("Get(...): unexpected error: %v", err) 505 } 506 wantPI := &upload.PushItem{ 507 Address: chunk.Address(), 508 BatchID: chunk.Stamp().BatchID(), 509 TagID: tag.TagID, 510 Timestamp: now().UnixNano(), 511 } 512 513 if diff := cmp.Diff(wantPI, pi); diff != "" { 514 t.Fatalf("Get(...): unexpected UploadItem (-want +have):\n%s", diff) 515 } 516 517 have, err := ts.ChunkStore().Get(context.Background(), chunk.Address()) 518 if err != nil { 519 t.Fatalf("Get(...): unexpected error: %v", err) 520 } 521 if want := chunk; !want.Equal(have) { 522 t.Fatalf("Get(...): chunk mismatch:\nwant: %x\nhave: %x", want, have) 523 } 524 }) 525 }) 526 } 527 528 t.Run("iterate all", func(t *testing.T) { 529 count := 0 530 err := ts.IndexStore().Iterate( 531 storage.Query{ 532 Factory: func() storage.Item { return new(upload.UploadItem) }, 533 }, 534 func(r storage.Result) (bool, error) { 535 address := swarm.NewAddress([]byte(r.ID[:32])) 536 synced := r.Entry.(*upload.UploadItem).Synced != 0 537 count++ 538 if synced { 539 t.Fatal("expected synced to be false") 540 } 541 has, err := ts.ChunkStore().Has(context.Background(), address) 542 if err != nil { 543 t.Fatalf("unexpected error in Has(...): %v", err) 544 } 545 if !has { 546 t.Fatalf("expected chunk to be present %s", address.String()) 547 } 548 return false, nil 549 }, 550 ) 551 if err != nil { 552 t.Fatalf("IterateAll(...): unexpected error %v", err) 553 } 554 if count != 10 { 555 t.Fatalf("unexpected count: want 10 have %d", count) 556 } 557 }) 558 559 t.Run("close with reference", func(t *testing.T) { 560 addr := swarm.RandAddress(t) 561 562 err := ts.Run(context.Background(), func(s transaction.Store) error { 563 return putter.Close(s.IndexStore(), addr) 564 }) 565 if err != nil { 566 t.Fatalf("Close(...): unexpected error %v", err) 567 } 568 569 var ti upload.TagItem 570 571 err = ts.Run(context.Background(), func(s transaction.Store) error { 572 ti, err = upload.TagInfo(s.IndexStore(), tag.TagID) 573 return err 574 }) 575 if err != nil { 576 t.Fatalf("TagInfo(...): unexpected error %v", err) 577 } 578 579 wantTI := upload.TagItem{ 580 TagID: tag.TagID, 581 Split: 20, 582 Seen: 10, 583 StartedAt: now().UnixNano(), 584 Address: addr, 585 } 586 if diff := cmp.Diff(wantTI, ti); diff != "" { 587 t.Fatalf("Get(...): unexpected TagItem (-want +have):\n%s", diff) 588 } 589 590 t.Run("iterate all tag items", func(t *testing.T) { 591 var tagItemsCount, uploaded, synced uint64 592 err := upload.IterateAllTagItems(ts.IndexStore(), func(ti *upload.TagItem) (bool, error) { 593 uploaded += ti.Split 594 synced += ti.Synced 595 tagItemsCount++ 596 return false, nil 597 }) 598 if err != nil { 599 t.Fatalf("IterateAllTagItems(...): unexpected error %v", err) 600 } 601 if tagItemsCount != 1 { 602 t.Fatalf("unexpected tagItemsCount: want 1 have %d", tagItemsCount) 603 } 604 if uploaded != 20 { 605 t.Fatalf("unexpected uploaded: want 20 have %d", uploaded) 606 } 607 if synced != 0 { 608 t.Fatalf("unexpected synced: want 0 have %d", synced) 609 } 610 }) 611 }) 612 613 t.Run("error after close", func(t *testing.T) { 614 err := ts.Run(context.Background(), func(s transaction.Store) error { 615 return putter.Put(context.Background(), s, chunktest.GenerateTestRandomChunk()) 616 }) 617 if !errors.Is(err, upload.ErrPutterAlreadyClosed) { 618 t.Fatalf("unexpected error, expected: %v, got: %v", upload.ErrPutterAlreadyClosed, err) 619 } 620 }) 621 622 t.Run("restart putter", func(t *testing.T) { 623 624 var putter internal.PutterCloserWithReference 625 626 err = ts.Run(context.Background(), func(s transaction.Store) error { 627 putter, err = upload.NewPutter(s.IndexStore(), tag.TagID) 628 return err 629 }) 630 if err != nil { 631 t.Fatalf("failed creating putter: %v", err) 632 } 633 634 for _, chunk := range chunktest.GenerateTestRandomChunks(5) { 635 if err := ts.Run(context.Background(), func(s transaction.Store) error { 636 return putter.Put(context.Background(), s, chunk) 637 }); err != nil { 638 t.Fatalf("Put(...): unexpected error: %v", err) 639 } 640 } 641 642 // close with different address 643 addr := swarm.RandAddress(t) 644 if err := ts.Run(context.Background(), func(s transaction.Store) error { 645 return putter.Close(s.IndexStore(), addr) 646 }); err != nil { 647 t.Fatalf("Close(...): unexpected error %v", err) 648 } 649 650 ti, err := upload.TagInfo(ts.IndexStore(), tag.TagID) 651 if err != nil { 652 t.Fatalf("TagInfo(...): unexpected error %v", err) 653 } 654 655 wantTI := upload.TagItem{ 656 TagID: tag.TagID, 657 Split: 25, 658 Seen: 10, 659 StartedAt: now().UnixNano(), 660 Address: addr, 661 } 662 663 if diff := cmp.Diff(wantTI, ti); diff != "" { 664 t.Fatalf("Get(...): unexpected TagItem (-want +have):\n%s", diff) 665 } 666 }) 667 } 668 669 func TestChunkReporter(t *testing.T) { 670 t.Parallel() 671 672 ts := newTestStorage(t) 673 674 var ( 675 tag upload.TagItem 676 putter internal.PutterCloserWithReference 677 err error 678 ) 679 if err := ts.Run(context.Background(), func(s transaction.Store) error { 680 tag, err = upload.NextTag(s.IndexStore()) 681 return err 682 }); err != nil { 683 t.Fatalf("failed creating tag: %v", err) 684 } 685 686 if err := ts.Run(context.Background(), func(s transaction.Store) error { 687 putter, err = upload.NewPutter(s.IndexStore(), tag.TagID) 688 return err 689 }); err != nil { 690 t.Fatalf("failed creating putter: %v", err) 691 } 692 693 for idx, chunk := range chunktest.GenerateTestRandomChunks(10) { 694 t.Run(fmt.Sprintf("chunk %s", chunk.Address()), func(t *testing.T) { 695 696 if err := ts.Run(context.Background(), func(s transaction.Store) error { 697 return putter.Put(context.Background(), s, chunk) 698 }); err != nil { 699 t.Fatalf("Put(...): unexpected error: %v", err) 700 } 701 702 report := func(ch swarm.Chunk, state int) { 703 t.Helper() 704 if err := ts.Run(context.Background(), func(s transaction.Store) error { 705 return upload.Report(context.Background(), s, ch, state) 706 }); err != nil { 707 t.Fatalf("Report(...): unexpected error: %v", err) 708 } 709 } 710 711 t.Run("mark sent", func(t *testing.T) { 712 report(chunk, storage.ChunkSent) 713 }) 714 715 if idx < 4 { 716 t.Run("mark stored", func(t *testing.T) { 717 report(chunk, storage.ChunkStored) 718 }) 719 } 720 721 if idx >= 4 && idx < 8 { 722 t.Run("mark synced", func(t *testing.T) { 723 report(chunk, storage.ChunkSynced) 724 }) 725 } 726 727 if idx >= 8 { 728 t.Run("mark could not sync", func(t *testing.T) { 729 report(chunk, storage.ChunkCouldNotSync) 730 }) 731 } 732 733 t.Run("verify internal state", func(t *testing.T) { 734 ti := &upload.TagItem{ 735 TagID: tag.TagID, 736 } 737 err := ts.IndexStore().Get(ti) 738 if err != nil { 739 t.Fatalf("Get(...): unexpected error: %v", err) 740 } 741 var synced, sent, stored uint64 742 sent = uint64(idx + 1) 743 if idx >= 8 { 744 synced, stored = 8, 4 745 } else { 746 synced, stored = uint64(idx+1), uint64(idx+1) 747 if idx >= 4 { 748 stored = 4 749 } 750 } 751 wantTI := &upload.TagItem{ 752 TagID: tag.TagID, 753 StartedAt: now().UnixNano(), 754 Sent: sent, 755 Synced: synced, 756 Stored: stored, 757 } 758 759 if diff := cmp.Diff(wantTI, ti); diff != "" { 760 t.Fatalf("Get(...): unexpected TagItem (-want +have):\n%s", diff) 761 } 762 763 ui := &upload.UploadItem{ 764 Address: chunk.Address(), 765 BatchID: chunk.Stamp().BatchID(), 766 } 767 has, err := ts.IndexStore().Has(ui) 768 if err != nil { 769 t.Fatalf("unexpected error: %v", err) 770 } 771 if has { 772 t.Fatalf("expected to not be found: %s", ui) 773 } 774 775 pi := &upload.PushItem{ 776 Timestamp: now().UnixNano(), 777 Address: chunk.Address(), 778 BatchID: chunk.Stamp().BatchID(), 779 } 780 has, err = ts.IndexStore().Has(pi) 781 if err != nil { 782 t.Fatalf("Has(...): unexpected error: %v", err) 783 } 784 if has { 785 t.Fatalf("Has(...): expected to not be found: %s", pi) 786 } 787 788 have, err := ts.ChunkStore().Has(context.Background(), chunk.Address()) 789 if err != nil { 790 t.Fatalf("Get(...): unexpected error: %v", err) 791 } 792 if have { 793 t.Fatalf("Get(...): chunk expected to not be found: %s", chunk.Address()) 794 } 795 }) 796 }) 797 } 798 799 t.Run("close with reference", func(t *testing.T) { 800 addr := swarm.RandAddress(t) 801 802 err := ts.Run(context.Background(), func(s transaction.Store) error { return putter.Close(s.IndexStore(), addr) }) 803 if err != nil { 804 t.Fatalf("Close(...): unexpected error %v", err) 805 } 806 807 var ti upload.TagItem 808 err = ts.Run(context.Background(), func(s transaction.Store) error { 809 ti, err = upload.TagInfo(s.IndexStore(), tag.TagID) 810 return err 811 }) 812 if err != nil { 813 t.Fatalf("TagInfo(...): unexpected error %v", err) 814 } 815 816 wantTI := upload.TagItem{ 817 TagID: tag.TagID, 818 Split: 10, 819 Seen: 0, 820 Stored: 4, 821 Sent: 10, 822 Synced: 8, 823 StartedAt: now().UnixNano(), 824 Address: addr, 825 } 826 if diff := cmp.Diff(wantTI, ti); diff != "" { 827 t.Fatalf("Get(...): unexpected TagItem (-want +have):\n%s", diff) 828 } 829 }) 830 } 831 832 func TestNextTagID(t *testing.T) { 833 t.Parallel() 834 835 ts := newTestStorage(t) 836 837 for i := 1; i < 4; i++ { 838 var tag upload.TagItem 839 var err error 840 err = ts.Run(context.Background(), func(s transaction.Store) error { 841 tag, err = upload.NextTag(s.IndexStore()) 842 return err 843 }) 844 if err != nil { 845 t.Fatalf("failed creating tag: %v", err) 846 } 847 848 if tag.TagID != uint64(i) { 849 t.Fatalf("incorrect tag ID returned, exp: %d found %d", i, tag.TagID) 850 } 851 } 852 853 var lastTag upload.NextTagID 854 err := ts.IndexStore().Get(&lastTag) 855 if err != nil { 856 t.Fatal(err) 857 } 858 859 if uint64(lastTag) != 3 { 860 t.Fatalf("incorrect value for last tag, exp 3 found %d", uint64(lastTag)) 861 } 862 } 863 864 func TestListTags(t *testing.T) { 865 t.Parallel() 866 867 ts := newTestStorage(t) 868 869 want := make([]upload.TagItem, 10) 870 for i := range want { 871 var tag upload.TagItem 872 var err error 873 err = ts.Run(context.Background(), func(s transaction.Store) error { 874 tag, err = upload.NextTag(s.IndexStore()) 875 return err 876 }) 877 if err != nil { 878 t.Fatalf("failed creating tag: %v", err) 879 } 880 want[i] = tag 881 } 882 883 have, err := upload.ListAllTags(ts.IndexStore()) 884 if err != nil { 885 t.Fatalf("upload.ListAllTags(): unexpected error: %v", err) 886 } 887 888 opts := cmpopts.SortSlices(func(i, j upload.TagItem) bool { return i.TagID < j.TagID }) 889 if diff := cmp.Diff(want, have, opts); diff != "" { 890 t.Fatalf("upload.ListAllTags(): mismatch (-want +have):\n%s", diff) 891 } 892 } 893 894 func TestIterate(t *testing.T) { 895 t.Parallel() 896 897 ts := newTestStorage(t) 898 899 t.Run("on empty storage does not call the callback fn", func(t *testing.T) { 900 err := upload.IteratePending(context.Background(), ts, func(chunk swarm.Chunk) (bool, error) { 901 t.Fatal("unexpected call") 902 return false, nil 903 }) 904 if err != nil { 905 t.Fatal(err) 906 } 907 }) 908 909 t.Run("iterates chunks", func(t *testing.T) { 910 var tag upload.TagItem 911 var err error 912 err = ts.Run(context.Background(), func(s transaction.Store) error { 913 tag, err = upload.NextTag(s.IndexStore()) 914 return err 915 }) 916 if err != nil { 917 t.Fatalf("failed creating tag: %v", err) 918 } 919 920 var putter internal.PutterCloserWithReference 921 err = ts.Run(context.Background(), func(s transaction.Store) error { 922 putter, err = upload.NewPutter(s.IndexStore(), tag.TagID) 923 return err 924 }) 925 if err != nil { 926 t.Fatalf("failed creating putter: %v", err) 927 } 928 929 chunk1, chunk2 := chunktest.GenerateTestRandomChunk(), chunktest.GenerateTestRandomChunk() 930 err = put(t, ts, putter, chunk1) 931 if err != nil { 932 t.Fatalf("session.Put(...): unexpected error: %v", err) 933 } 934 err = put(t, ts, putter, chunk2) 935 if err != nil { 936 t.Fatalf("session.Put(...): unexpected error: %v", err) 937 } 938 939 var count int 940 941 err = upload.IteratePending(context.Background(), ts, func(chunk swarm.Chunk) (bool, error) { 942 count++ 943 if !chunk.Equal(chunk1) && !chunk.Equal(chunk2) { 944 return true, fmt.Errorf("unknown chunk %s", chunk.Address()) 945 } 946 return false, nil 947 }) 948 if err != nil { 949 t.Fatalf("Iterate(...): unexpected error: %v", err) 950 } 951 952 if count != 0 { 953 t.Fatalf("expected to iterate 0 chunks, got: %v", count) 954 } 955 956 err = ts.Run(context.Background(), func(s transaction.Store) error { return putter.Close(s.IndexStore(), swarm.ZeroAddress) }) 957 if err != nil { 958 t.Fatalf("Close(...) error: %v", err) 959 } 960 961 err = upload.IteratePending(context.Background(), ts, func(chunk swarm.Chunk) (bool, error) { 962 count++ 963 if !chunk.Equal(chunk1) && !chunk.Equal(chunk2) { 964 return true, fmt.Errorf("unknown chunk %s", chunk.Address()) 965 } 966 return false, nil 967 }) 968 if err != nil { 969 t.Fatalf("Iterate(...): unexpected error: %v", err) 970 } 971 972 if count != 2 { 973 t.Fatalf("expected to iterate two chunks, got: %v", count) 974 } 975 }) 976 } 977 978 func TestDeleteTag(t *testing.T) { 979 t.Parallel() 980 981 ts := newTestStorage(t) 982 983 var tag upload.TagItem 984 var err error 985 err = ts.Run(context.Background(), func(s transaction.Store) error { 986 tag, err = upload.NextTag(s.IndexStore()) 987 return err 988 }) 989 if err != nil { 990 t.Fatalf("failed creating tag: %v", err) 991 } 992 993 err = ts.Run(context.Background(), func(s transaction.Store) error { 994 return upload.DeleteTag(s.IndexStore(), tag.TagID) 995 }) 996 if err != nil { 997 t.Fatalf("upload.DeleteTag(): unexpected error: %v", err) 998 } 999 1000 _, err = upload.TagInfo(ts.IndexStore(), tag.TagID) 1001 if !errors.Is(err, storage.ErrNotFound) { 1002 t.Fatalf("want: %v; have: %v", storage.ErrNotFound, err) 1003 } 1004 } 1005 1006 func TestBatchIDForChunk(t *testing.T) { 1007 t.Parallel() 1008 1009 ts := newTestStorage(t) 1010 1011 var tag upload.TagItem 1012 var err error 1013 err = ts.Run(context.Background(), func(s transaction.Store) error { 1014 tag, err = upload.NextTag(s.IndexStore()) 1015 return err 1016 }) 1017 if err != nil { 1018 t.Fatalf("failed creating tag: %v", err) 1019 } 1020 1021 var putter internal.PutterCloserWithReference 1022 err = ts.Run(context.Background(), func(s transaction.Store) error { 1023 putter, err = upload.NewPutter(s.IndexStore(), tag.TagID) 1024 return err 1025 }) 1026 if err != nil { 1027 t.Fatalf("failed creating putter: %v", err) 1028 } 1029 1030 chunk := chunktest.GenerateTestRandomChunk() 1031 if err := put(t, ts, putter, chunk); err != nil { 1032 t.Fatalf("Put(...): unexpected error: %v", err) 1033 } 1034 1035 batchID, err := upload.BatchIDForChunk(ts.IndexStore(), chunk.Address()) 1036 if err != nil { 1037 t.Fatalf("BatchIDForChunk(...): unexpected error: %v", err) 1038 } 1039 1040 if !bytes.Equal(batchID, chunk.Stamp().BatchID()) { 1041 t.Fatalf("BatchIDForChunk(...): want %x; got %x", chunk.Stamp().BatchID(), batchID) 1042 } 1043 } 1044 1045 func TestCleanup(t *testing.T) { 1046 t.Parallel() 1047 1048 t.Run("cleanup putter", func(t *testing.T) { 1049 t.Parallel() 1050 1051 ts := newTestStorage(t) 1052 1053 var tag upload.TagItem 1054 var err error 1055 err = ts.Run(context.Background(), func(s transaction.Store) error { 1056 tag, err = upload.NextTag(s.IndexStore()) 1057 return err 1058 }) 1059 if err != nil { 1060 t.Fatalf("failed creating tag: %v", err) 1061 } 1062 1063 var putter internal.PutterCloserWithReference 1064 err = ts.Run(context.Background(), func(s transaction.Store) error { 1065 putter, err = upload.NewPutter(s.IndexStore(), tag.TagID) 1066 return err 1067 }) 1068 if err != nil { 1069 t.Fatalf("failed creating putter: %v", err) 1070 } 1071 1072 chunk := chunktest.GenerateTestRandomChunk() 1073 err = put(t, ts, putter, chunk) 1074 if err != nil { 1075 t.Fatal("session.Put(...): unexpected error", err) 1076 } 1077 1078 err = putter.Cleanup(ts) 1079 if err != nil { 1080 t.Fatal("upload.Cleanup(...): unexpected error", err) 1081 } 1082 1083 count := 0 1084 _ = upload.IteratePending(context.Background(), ts, func(chunk swarm.Chunk) (bool, error) { 1085 count++ 1086 return false, nil 1087 }) 1088 if count != 0 { 1089 t.Fatalf("expected to iterate 0 chunks, got: %v", count) 1090 } 1091 1092 if _, err := ts.ChunkStore().Get(context.Background(), chunk.Address()); !errors.Is(err, storage.ErrNotFound) { 1093 t.Fatalf("expected chunk not found error, got: %v", err) 1094 } 1095 }) 1096 1097 t.Run("cleanup dirty", func(t *testing.T) { 1098 t.Parallel() 1099 1100 ts := newTestStorage(t) 1101 1102 var tag upload.TagItem 1103 var err error 1104 err = ts.Run(context.Background(), func(s transaction.Store) error { 1105 tag, err = upload.NextTag(s.IndexStore()) 1106 return err 1107 }) 1108 if err != nil { 1109 t.Fatalf("failed creating tag: %v", err) 1110 } 1111 1112 var putter internal.PutterCloserWithReference 1113 err = ts.Run(context.Background(), func(s transaction.Store) error { 1114 putter, err = upload.NewPutter(s.IndexStore(), tag.TagID) 1115 return err 1116 }) 1117 if err != nil { 1118 t.Fatalf("failed creating putter: %v", err) 1119 } 1120 1121 chunk := chunktest.GenerateTestRandomChunk() 1122 err = put(t, ts, putter, chunk) 1123 if err != nil { 1124 t.Fatal("session.Put(...): unexpected error", err) 1125 } 1126 1127 err = upload.CleanupDirty(ts) 1128 if err != nil { 1129 t.Fatal("upload.Cleanup(...): unexpected error", err) 1130 } 1131 1132 count := 0 1133 _ = upload.IteratePending(context.Background(), ts, func(chunk swarm.Chunk) (bool, error) { 1134 count++ 1135 return false, nil 1136 }) 1137 if count != 0 { 1138 t.Fatalf("expected to iterate 0 chunks, got: %v", count) 1139 } 1140 1141 if _, err := ts.ChunkStore().Get(context.Background(), chunk.Address()); !errors.Is(err, storage.ErrNotFound) { 1142 t.Fatalf("expected chunk not found error, got: %v", err) 1143 } 1144 }) 1145 } 1146 1147 func put(t *testing.T, ts transaction.Storage, putter internal.PutterCloserWithReference, ch swarm.Chunk) error { 1148 t.Helper() 1149 return ts.Run(context.Background(), func(s transaction.Store) error { 1150 return putter.Put(context.Background(), s, ch) 1151 }) 1152 }