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 }