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  }