github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/uploadstore_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 storer_test 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "testing" 12 "time" 13 14 storage "github.com/ethersphere/bee/v2/pkg/storage" 15 chunktesting "github.com/ethersphere/bee/v2/pkg/storage/testing" 16 storer "github.com/ethersphere/bee/v2/pkg/storer" 17 "github.com/ethersphere/bee/v2/pkg/swarm" 18 "github.com/google/go-cmp/cmp" 19 ) 20 21 func testUploadStore(t *testing.T, newStorer func() (*storer.DB, error)) { 22 t.Helper() 23 24 t.Run("new session", func(t *testing.T) { 25 t.Parallel() 26 27 lstore, err := newStorer() 28 if err != nil { 29 t.Fatal(err) 30 } 31 32 for i := 1; i < 5; i++ { 33 tag, err := lstore.NewSession() 34 if err != nil { 35 t.Fatalf("NewSession(): unexpected error: %v", err) 36 } 37 if tag.TagID != uint64(i) { 38 t.Fatalf("incorrect id generated: want %d have %d", i, tag.TagID) 39 } 40 } 41 }) 42 43 t.Run("no tag", func(t *testing.T) { 44 t.Parallel() 45 46 lstore, err := newStorer() 47 if err != nil { 48 t.Fatal(err) 49 } 50 51 _, err = lstore.Upload(context.TODO(), false, 0) 52 if err == nil { 53 t.Fatal("expected error on Upload with no tag") 54 } 55 }) 56 57 for _, tc := range []struct { 58 chunks []swarm.Chunk 59 pin bool 60 fail bool 61 duplicate bool 62 }{ 63 { 64 chunks: chunktesting.GenerateTestRandomChunks(10), 65 }, 66 { 67 chunks: chunktesting.GenerateTestRandomChunks(20), 68 fail: true, 69 }, 70 { 71 chunks: chunktesting.GenerateTestRandomChunks(30), 72 }, 73 { 74 chunks: chunktesting.GenerateTestRandomChunks(10), 75 pin: true, 76 }, 77 { 78 chunks: chunktesting.GenerateTestRandomChunks(20), 79 pin: true, 80 fail: true, 81 }, 82 { 83 chunks: chunktesting.GenerateTestRandomChunks(30), 84 pin: true, 85 }, 86 { 87 chunks: chunktesting.GenerateTestRandomChunks(10), 88 pin: true, 89 duplicate: true, 90 }, 91 } { 92 tc := tc 93 testName := fmt.Sprintf("upload_%d_chunks", len(tc.chunks)) 94 if tc.pin { 95 testName += "_with_pin" 96 } 97 if tc.fail { 98 testName += "_rollback" 99 } 100 t.Run(testName, func(t *testing.T) { 101 t.Parallel() 102 103 lstore, err := newStorer() 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 tag, err := lstore.NewSession() 109 if err != nil { 110 t.Fatalf("NewSession(): unexpected error: %v", err) 111 } 112 113 session, err := lstore.Upload(context.TODO(), tc.pin, tag.TagID) 114 if err != nil { 115 t.Fatalf("Upload(...): unexpected error: %v", err) 116 } 117 118 for _, ch := range tc.chunks { 119 err := session.Put(context.TODO(), ch) 120 if err != nil { 121 t.Fatalf("session.Put(...): unexpected error: %v", err) 122 } 123 } 124 125 if tc.fail { 126 err := session.Cleanup() 127 if err != nil { 128 t.Fatalf("session.Cleanup(): unexpected error: %v", err) 129 } 130 } else { 131 err := session.Done(tc.chunks[0].Address()) 132 if err != nil { 133 t.Fatalf("session.Done(...): unexpected error: %v", err) 134 } 135 136 // duplicate pin 137 if tc.pin && tc.duplicate { 138 err := session.Done(tc.chunks[0].Address()) 139 if err != nil { 140 t.Fatalf("session.Done(...): unexpected error: %v", err) 141 } 142 } 143 } 144 verifySessionInfo(t, lstore.Storage(), tag.TagID, tc.chunks, !tc.fail) 145 if tc.pin { 146 verifyPinCollection(t, lstore.Storage(), tc.chunks[0], tc.chunks, !tc.fail) 147 } 148 }) 149 } 150 151 t.Run("get session info", func(t *testing.T) { 152 t.Parallel() 153 154 lstore, err := newStorer() 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 verify := func(t *testing.T, info storer.SessionInfo, id, split, seen uint64, addr swarm.Address) { 160 t.Helper() 161 162 if info.TagID != id { 163 t.Fatalf("unexpected TagID in session: want %d have %d", id, info.TagID) 164 } 165 166 if info.Split != split { 167 t.Fatalf("unexpected split count in session: want %d have %d", split, info.Split) 168 } 169 170 if info.Seen != seen { 171 t.Fatalf("unexpected seen count in session: want %d have %d", seen, info.Seen) 172 } 173 174 if !info.Address.Equal(addr) { 175 t.Fatalf("unexpected swarm reference: want %s have %s", addr, info.Address) 176 } 177 } 178 179 t.Run("done", func(t *testing.T) { 180 tag, err := lstore.NewSession() 181 if err != nil { 182 t.Fatalf("NewSession(): unexpected error: %v", err) 183 } 184 185 session, err := lstore.Upload(context.TODO(), false, tag.TagID) 186 if err != nil { 187 t.Fatalf("Upload(...): unexpected error: %v", err) 188 } 189 190 sessionInfo, err := lstore.Session(tag.TagID) 191 if err != nil { 192 t.Fatalf("Session(...): unexpected error: %v", err) 193 } 194 195 verify(t, sessionInfo, tag.TagID, 0, 0, swarm.ZeroAddress) 196 197 chunks := chunktesting.GenerateTestRandomChunks(10) 198 199 for _, ch := range chunks { 200 for i := 0; i < 2; i++ { 201 err := session.Put(context.TODO(), ch) 202 if err != nil { 203 t.Fatalf("session.Put(...): unexpected error: %v", err) 204 } 205 } 206 } 207 208 err = session.Done(chunks[0].Address()) 209 if err != nil { 210 t.Fatalf("session.Done(...): unexpected error: %v", err) 211 } 212 213 sessionInfo, err = lstore.Session(tag.TagID) 214 if err != nil { 215 t.Fatalf("Session(...): unexpected error: %v", err) 216 } 217 218 verify(t, sessionInfo, tag.TagID, 20, 10, chunks[0].Address()) 219 }) 220 221 t.Run("cleanup", func(t *testing.T) { 222 tag, err := lstore.NewSession() 223 if err != nil { 224 t.Fatalf("NewSession(): unexpected error: %v", err) 225 } 226 227 session, err := lstore.Upload(context.TODO(), false, tag.TagID) 228 if err != nil { 229 t.Fatalf("Upload(...): unexpected error: %v", err) 230 } 231 232 sessionInfo, err := lstore.Session(tag.TagID) 233 if err != nil { 234 t.Fatalf("Session(...): unexpected error: %v", err) 235 } 236 237 verify(t, sessionInfo, tag.TagID, 0, 0, swarm.ZeroAddress) 238 239 chunks := chunktesting.GenerateTestRandomChunks(10) 240 241 for _, ch := range chunks { 242 err := session.Put(context.TODO(), ch) 243 if err != nil { 244 t.Fatalf("session.Put(...): unexpected error: %v", err) 245 } 246 } 247 248 err = session.Cleanup() 249 if err != nil { 250 t.Fatalf("session.Cleanup(): unexpected error: %v", err) 251 } 252 253 got, err := lstore.Session(tag.TagID) 254 if err != nil { 255 t.Fatalf("Session(...): unexpected error: %v", err) 256 } 257 258 // All updates to tag should be reverted 259 if diff := cmp.Diff(tag, got); diff != "" { 260 t.Fatalf("tag mismatch (-want +have):\n%s", diff) 261 } 262 }) 263 }) 264 } 265 266 func testListDeleteSessions(t *testing.T, newStorer func() (*storer.DB, error)) { 267 t.Helper() 268 269 t.Run("list sessions", func(t *testing.T) { 270 t.Parallel() 271 272 lstore, err := newStorer() 273 if err != nil { 274 t.Fatal(err) 275 } 276 277 for i := 0; i < 10; i++ { 278 _, err := lstore.NewSession() 279 if err != nil { 280 t.Fatalf("NewSession(): unexpected error: %v", err) 281 } 282 } 283 284 sessions, err := lstore.ListSessions(1, 3) 285 if err != nil { 286 t.Fatalf("ListSession(): unexpected error: %v", err) 287 } 288 289 if len(sessions) != 3 { 290 t.Fatalf("unexpected number of sessions: want %d have %d", 3, len(sessions)) 291 } 292 293 for idx, session := range sessions { 294 if session.TagID != uint64(2+idx) { 295 t.Fatalf("unexpected tag in session list: want %d have %d", 2+idx, session.TagID) 296 } 297 } 298 }) 299 300 t.Run("delete sessions", func(t *testing.T) { 301 t.Parallel() 302 303 lstore, err := newStorer() 304 if err != nil { 305 t.Fatal(err) 306 } 307 308 tag, err := lstore.NewSession() 309 if err != nil { 310 t.Fatalf("NewSession(): unexpected error: %v", err) 311 } 312 313 err = lstore.DeleteSession(tag.TagID) 314 if err != nil { 315 t.Fatalf("DeleteSession(...): unexpected error: %v", err) 316 } 317 318 _, err = lstore.Session(tag.TagID) 319 if !errors.Is(err, storage.ErrNotFound) { 320 t.Fatalf("Session(...): expected error: %v, got: %v", storage.ErrNotFound, err) 321 } 322 }) 323 } 324 325 func TestListDeleteSessions(t *testing.T) { 326 t.Parallel() 327 328 t.Run("inmem", func(t *testing.T) { 329 t.Parallel() 330 331 testListDeleteSessions(t, func() (*storer.DB, error) { 332 return storer.New(context.Background(), "", dbTestOps(swarm.RandAddress(t), 0, nil, nil, time.Second)) 333 }) 334 }) 335 336 t.Run("disk", func(t *testing.T) { 337 t.Parallel() 338 339 testListDeleteSessions(t, diskStorer(t, dbTestOps(swarm.RandAddress(t), 0, nil, nil, time.Second))) 340 }) 341 } 342 343 func TestUploadStore(t *testing.T) { 344 t.Parallel() 345 346 t.Run("inmem", func(t *testing.T) { 347 t.Parallel() 348 349 testUploadStore(t, func() (*storer.DB, error) { 350 return storer.New(context.Background(), "", dbTestOps(swarm.RandAddress(t), 0, nil, nil, time.Second)) 351 }) 352 }) 353 t.Run("disk", func(t *testing.T) { 354 t.Parallel() 355 356 testUploadStore(t, diskStorer(t, dbTestOps(swarm.RandAddress(t), 0, nil, nil, time.Second))) 357 }) 358 } 359 360 func testReporter(t *testing.T, newStorer func() (*storer.DB, error)) { 361 t.Helper() 362 363 chunks := chunktesting.GenerateTestRandomChunks(3) 364 365 lstore, err := newStorer() 366 if err != nil { 367 t.Fatal(err) 368 } 369 370 session, err := lstore.NewSession() 371 if err != nil { 372 t.Fatal(err) 373 } 374 375 putter, err := lstore.Upload(context.Background(), true, session.TagID) 376 if err != nil { 377 t.Fatal(err) 378 } 379 380 for _, ch := range chunks { 381 err = putter.Put(context.Background(), ch) 382 if err != nil { 383 t.Fatal(err) 384 } 385 } 386 387 root := chunktesting.GenerateTestRandomChunk() 388 389 err = putter.Done(root.Address()) 390 if err != nil { 391 t.Fatal(err) 392 } 393 394 t.Run("report", func(t *testing.T) { 395 t.Run("commit", func(t *testing.T) { 396 err := lstore.Report(context.Background(), chunks[0], storage.ChunkSynced) 397 if err != nil { 398 t.Fatalf("Report(...): unexpected error %v", err) 399 } 400 401 wantTI := storer.SessionInfo{ 402 TagID: session.TagID, 403 Split: 3, 404 Seen: 0, 405 Sent: 0, 406 Synced: 1, 407 Stored: 0, 408 StartedAt: session.StartedAt, 409 Address: root.Address(), 410 } 411 412 gotTI, err := lstore.Session(session.TagID) 413 if err != nil { 414 t.Fatalf("Session(...): unexpected error: %v", err) 415 } 416 417 if diff := cmp.Diff(wantTI, gotTI); diff != "" { 418 t.Fatalf("unexpected tag item (-want +have):\n%s", diff) 419 } 420 421 has, err := lstore.Storage().ChunkStore().Has(context.Background(), chunks[0].Address()) 422 if err != nil { 423 t.Fatalf("ChunkStore.Has(...): unexpected error: %v", err) 424 } 425 if !has { 426 t.Fatalf("expected chunk %s to not be found", chunks[0].Address()) 427 } 428 }) 429 }) 430 } 431 432 func TestReporter(t *testing.T) { 433 t.Parallel() 434 435 t.Run("inmem", func(t *testing.T) { 436 t.Parallel() 437 438 testReporter(t, func() (*storer.DB, error) { 439 440 opts := dbTestOps(swarm.RandAddress(t), 0, nil, nil, time.Second) 441 442 return storer.New(context.Background(), "", opts) 443 }) 444 }) 445 t.Run("disk", func(t *testing.T) { 446 t.Parallel() 447 448 opts := dbTestOps(swarm.RandAddress(t), 0, nil, nil, time.Second) 449 450 testReporter(t, diskStorer(t, opts)) 451 }) 452 }