github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/internal/chunkstamp/chunkstamp_test.go (about)

     1  // Copyright 2023 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 chunkstamp_test
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"testing"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/postage"
    14  	"github.com/ethersphere/bee/v2/pkg/storer/internal/transaction"
    15  
    16  	storage "github.com/ethersphere/bee/v2/pkg/storage"
    17  	"github.com/ethersphere/bee/v2/pkg/storage/storagetest"
    18  	chunktest "github.com/ethersphere/bee/v2/pkg/storage/testing"
    19  	"github.com/ethersphere/bee/v2/pkg/storer/internal"
    20  	"github.com/ethersphere/bee/v2/pkg/storer/internal/chunkstamp"
    21  	"github.com/ethersphere/bee/v2/pkg/swarm"
    22  	"github.com/google/go-cmp/cmp"
    23  )
    24  
    25  func TestChunkStampItem(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	minAddress := swarm.NewAddress(storagetest.MinAddressBytes[:])
    29  	minStamp := postage.NewStamp(make([]byte, 32), make([]byte, 8), make([]byte, 8), make([]byte, 65))
    30  	rndChunk := chunktest.GenerateTestRandomChunk()
    31  
    32  	tests := []struct {
    33  		name string
    34  		test *storagetest.ItemMarshalAndUnmarshalTest
    35  	}{{
    36  		name: "zero namespace",
    37  		test: &storagetest.ItemMarshalAndUnmarshalTest{
    38  			Item:       new(chunkstamp.Item),
    39  			Factory:    func() storage.Item { return new(chunkstamp.Item) },
    40  			MarshalErr: chunkstamp.ErrMarshalInvalidChunkStampItemNamespace,
    41  			CmpOpts:    []cmp.Option{cmp.AllowUnexported(chunkstamp.Item{})},
    42  		},
    43  	}, {
    44  		name: "zero address",
    45  		test: &storagetest.ItemMarshalAndUnmarshalTest{
    46  			Item: new(chunkstamp.Item).
    47  				WithNamespace("test_namespace").
    48  				WithAddress(swarm.ZeroAddress),
    49  			Factory:    func() storage.Item { return new(chunkstamp.Item) },
    50  			MarshalErr: chunkstamp.ErrMarshalInvalidChunkStampItemAddress,
    51  			CmpOpts:    []cmp.Option{cmp.AllowUnexported(chunkstamp.Item{})},
    52  		},
    53  	}, {
    54  		name: "nil stamp",
    55  		test: &storagetest.ItemMarshalAndUnmarshalTest{
    56  			Item: new(chunkstamp.Item).
    57  				WithNamespace("test_namespace").
    58  				WithAddress(minAddress),
    59  			Factory:    func() storage.Item { return new(chunkstamp.Item) },
    60  			MarshalErr: chunkstamp.ErrMarshalInvalidChunkStampItemStamp,
    61  			CmpOpts:    []cmp.Option{cmp.AllowUnexported(chunkstamp.Item{})},
    62  		},
    63  	}, {
    64  		name: "zero stamp",
    65  		test: &storagetest.ItemMarshalAndUnmarshalTest{
    66  			Item: new(chunkstamp.Item).
    67  				WithNamespace("test_namespace").
    68  				WithAddress(minAddress).
    69  				WithStamp(new(postage.Stamp)),
    70  			Factory:    func() storage.Item { return new(chunkstamp.Item) },
    71  			MarshalErr: postage.ErrInvalidBatchID,
    72  			CmpOpts:    []cmp.Option{cmp.AllowUnexported(postage.Stamp{}, chunkstamp.Item{})},
    73  		},
    74  	}, {
    75  		name: "min values",
    76  		test: &storagetest.ItemMarshalAndUnmarshalTest{
    77  			Item: new(chunkstamp.Item).
    78  				WithNamespace("test_namespace").
    79  				WithAddress(minAddress).
    80  				WithStamp(minStamp),
    81  			Factory: func() storage.Item { return new(chunkstamp.Item).WithAddress(minAddress) },
    82  			CmpOpts: []cmp.Option{cmp.AllowUnexported(postage.Stamp{}, chunkstamp.Item{})},
    83  		},
    84  	}, {
    85  		name: "valid values",
    86  		test: &storagetest.ItemMarshalAndUnmarshalTest{
    87  			Item: new(chunkstamp.Item).
    88  				WithNamespace("test_namespace").
    89  				WithAddress(rndChunk.Address()).
    90  				WithStamp(rndChunk.Stamp()),
    91  			Factory: func() storage.Item { return new(chunkstamp.Item).WithAddress(rndChunk.Address()) },
    92  			CmpOpts: []cmp.Option{cmp.AllowUnexported(postage.Stamp{}, chunkstamp.Item{})},
    93  		},
    94  	}, {
    95  		name: "nil address on unmarshal",
    96  		test: &storagetest.ItemMarshalAndUnmarshalTest{
    97  			Item: new(chunkstamp.Item).
    98  				WithNamespace("test_namespace").
    99  				WithAddress(minAddress).
   100  				WithStamp(rndChunk.Stamp()),
   101  			Factory:      func() storage.Item { return new(chunkstamp.Item) },
   102  			UnmarshalErr: chunkstamp.ErrUnmarshalInvalidChunkStampItemAddress,
   103  			CmpOpts:      []cmp.Option{cmp.AllowUnexported(postage.Stamp{}, chunkstamp.Item{})},
   104  		},
   105  	}, {
   106  		name: "invalid size",
   107  		test: &storagetest.ItemMarshalAndUnmarshalTest{
   108  			Item: &storagetest.ItemStub{
   109  				MarshalBuf:   []byte{0xFF},
   110  				UnmarshalBuf: []byte{0xFF},
   111  			},
   112  			Factory:      func() storage.Item { return new(chunkstamp.Item).WithAddress(rndChunk.Address()) },
   113  			UnmarshalErr: chunkstamp.ErrUnmarshalInvalidChunkStampItemSize,
   114  			CmpOpts:      []cmp.Option{cmp.AllowUnexported(chunkstamp.Item{})},
   115  		},
   116  	}}
   117  
   118  	for _, tc := range tests {
   119  		tc := tc
   120  
   121  		t.Run(fmt.Sprintf("%s marshal/unmarshal", tc.name), func(t *testing.T) {
   122  			t.Parallel()
   123  
   124  			storagetest.TestItemMarshalAndUnmarshal(t, tc.test)
   125  		})
   126  
   127  		t.Run(fmt.Sprintf("%s clone", tc.name), func(t *testing.T) {
   128  			t.Parallel()
   129  
   130  			storagetest.TestItemClone(t, &storagetest.ItemCloneTest{
   131  				Item:    tc.test.Item,
   132  				CmpOpts: tc.test.CmpOpts,
   133  			})
   134  		})
   135  	}
   136  }
   137  
   138  func TestStoreLoadDelete(t *testing.T) {
   139  	t.Parallel()
   140  
   141  	ts := internal.NewInmemStorage()
   142  
   143  	for i, chunk := range chunktest.GenerateTestRandomChunks(10) {
   144  		ns := fmt.Sprintf("namespace_%d", i)
   145  		t.Run(ns, func(t *testing.T) {
   146  			t.Run("store new chunk stamp", func(t *testing.T) {
   147  				want := new(chunkstamp.Item).
   148  					WithNamespace(ns).
   149  					WithAddress(chunk.Address()).
   150  					WithStamp(chunk.Stamp())
   151  
   152  				have := want.Clone()
   153  
   154  				if err := ts.IndexStore().Get(have); !errors.Is(err, storage.ErrNotFound) {
   155  					t.Fatalf("Get(...): unexpected error: have: %v; want: %v", err, storage.ErrNotFound)
   156  				}
   157  
   158  				if err := ts.Run(context.Background(), func(s transaction.Store) error {
   159  					return chunkstamp.Store(s.IndexStore(), ns, chunk)
   160  				}); err != nil {
   161  					t.Fatalf("Store(...): unexpected error: %v", err)
   162  				}
   163  
   164  				if err := ts.IndexStore().Get(have); err != nil {
   165  					t.Fatalf("Get(...): unexpected error: %v", err)
   166  				}
   167  
   168  				opts := cmp.AllowUnexported(
   169  					chunkstamp.Item{},
   170  					postage.Stamp{},
   171  				)
   172  				if diff := cmp.Diff(want, have, opts); diff != "" {
   173  					t.Fatalf("Get(...): mismatch (-want +have):\n%s", diff)
   174  				}
   175  			})
   176  
   177  			t.Run("load stored chunk stamp", func(t *testing.T) {
   178  				want := chunk.Stamp()
   179  
   180  				have, err := chunkstamp.Load(ts.IndexStore(), ns, chunk.Address())
   181  				if err != nil {
   182  					t.Fatalf("Load(...): unexpected error: %v", err)
   183  				}
   184  
   185  				if diff := cmp.Diff(want, have, cmp.AllowUnexported(postage.Stamp{})); diff != "" {
   186  					t.Fatalf("Load(...): mismatch (-want +have):\n%s", diff)
   187  				}
   188  			})
   189  
   190  			t.Run("load stored chunk stamp with batch id", func(t *testing.T) {
   191  				want := chunk.Stamp()
   192  
   193  				have, err := chunkstamp.LoadWithBatchID(ts.IndexStore(), ns, chunk.Address(), chunk.Stamp().BatchID())
   194  				if err != nil {
   195  					t.Fatalf("LoadWithBatchID(...): unexpected error: %v", err)
   196  				}
   197  
   198  				if diff := cmp.Diff(want, have, cmp.AllowUnexported(postage.Stamp{})); diff != "" {
   199  					t.Fatalf("LoadWithBatchID(...): mismatch (-want +have):\n%s", diff)
   200  				}
   201  			})
   202  
   203  			t.Run("delete stored stamp", func(t *testing.T) {
   204  				if i%2 == 0 {
   205  					if err := ts.Run(context.Background(), func(s transaction.Store) error {
   206  						return chunkstamp.Delete(s.IndexStore(), ns, chunk.Address(), chunk.Stamp().BatchID())
   207  					}); err != nil {
   208  						t.Fatalf("Delete(...): unexpected error: %v", err)
   209  					}
   210  				} else {
   211  					if err := ts.Run(context.Background(), func(s transaction.Store) error {
   212  						return chunkstamp.DeleteWithStamp(s.IndexStore(), ns, chunk.Address(), chunk.Stamp())
   213  					}); err != nil {
   214  						t.Fatalf("DeleteWithStamp(...): unexpected error: %v", err)
   215  					}
   216  				}
   217  
   218  				have, err := chunkstamp.LoadWithBatchID(ts.IndexStore(), ns, chunk.Address(), chunk.Stamp().BatchID())
   219  				if !errors.Is(err, storage.ErrNotFound) {
   220  					t.Fatalf("Load(...): unexpected error: %v", err)
   221  				}
   222  				if have != nil {
   223  					t.Fatalf("Load(...): loaded stamp should be nil")
   224  				}
   225  			})
   226  
   227  			t.Run("delete all stored stamp index", func(t *testing.T) {
   228  
   229  				if err := ts.Run(context.Background(), func(s transaction.Store) error {
   230  					return chunkstamp.Store(s.IndexStore(), ns, chunk)
   231  				}); err != nil {
   232  					t.Fatalf("Store(...): unexpected error: %v", err)
   233  				}
   234  
   235  				if err := ts.Run(context.Background(), func(s transaction.Store) error {
   236  					return chunkstamp.DeleteAll(s.IndexStore(), ns, chunk.Address())
   237  				}); err != nil {
   238  					t.Fatalf("DeleteAll(...): unexpected error: %v", err)
   239  				}
   240  
   241  				have, err := chunkstamp.Load(ts.IndexStore(), ns, chunk.Address())
   242  				if !errors.Is(err, storage.ErrNotFound) {
   243  					t.Fatalf("Load(...): unexpected error: %v", err)
   244  				}
   245  				if have != nil {
   246  					t.Fatalf("Load(...): unexpected item %v", have)
   247  				}
   248  
   249  				cnt := 0
   250  				err = ts.IndexStore().Iterate(
   251  					storage.Query{
   252  						Factory: func() storage.Item {
   253  							return new(chunkstamp.Item)
   254  						},
   255  					},
   256  					func(result storage.Result) (bool, error) {
   257  						cnt++
   258  						return false, nil
   259  					},
   260  				)
   261  				if err != nil {
   262  					t.Fatalf("IndexStore().Iterate(...): unexpected error: %v", err)
   263  				}
   264  				if want, have := 0, cnt; want != have {
   265  					t.Fatalf("IndexStore().Iterate(...): chunk count mismatch:\nwant: %d\nhave: %d", want, have)
   266  				}
   267  			})
   268  		})
   269  	}
   270  }