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

     1  // Copyright 2024 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 transaction_test
     6  
     7  import (
     8  	"context"
     9  	"io/fs"
    10  	"os"
    11  	"path/filepath"
    12  	"testing"
    13  
    14  	"github.com/ethersphere/bee/v2/pkg/sharky"
    15  	"github.com/ethersphere/bee/v2/pkg/storage"
    16  	"github.com/ethersphere/bee/v2/pkg/storage/leveldbstore"
    17  	test "github.com/ethersphere/bee/v2/pkg/storage/testing"
    18  	"github.com/ethersphere/bee/v2/pkg/storer/internal/cache"
    19  	"github.com/ethersphere/bee/v2/pkg/storer/internal/transaction"
    20  	"github.com/ethersphere/bee/v2/pkg/swarm"
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  type dirFS struct {
    25  	basedir string
    26  }
    27  
    28  func (d *dirFS) Open(path string) (fs.File, error) {
    29  	return os.OpenFile(filepath.Join(d.basedir, path), os.O_RDWR|os.O_CREATE, 0644)
    30  }
    31  
    32  func Test_TransactionStorage(t *testing.T) {
    33  	t.Parallel()
    34  
    35  	sharkyStore, err := sharky.New(&dirFS{basedir: t.TempDir()}, 32, swarm.SocMaxChunkSize)
    36  	assert.NoError(t, err)
    37  
    38  	store, err := leveldbstore.New("", nil)
    39  	assert.NoError(t, err)
    40  
    41  	st := transaction.NewStorage(sharkyStore, store)
    42  	t.Cleanup(func() {
    43  		assert.NoError(t, st.Close())
    44  	})
    45  
    46  	t.Run("put", func(t *testing.T) {
    47  		t.Parallel()
    48  
    49  		tx, done := st.NewTransaction(context.Background())
    50  		defer done()
    51  
    52  		ch1 := test.GenerateTestRandomChunk()
    53  		ch2 := test.GenerateTestRandomChunk()
    54  
    55  		assert.NoError(t, tx.IndexStore().Put(&cache.CacheEntryItem{Address: ch1.Address(), AccessTimestamp: 1}))
    56  		assert.NoError(t, tx.ChunkStore().Put(context.Background(), ch1))
    57  		assert.NoError(t, tx.IndexStore().Put(&cache.CacheEntryItem{Address: ch2.Address(), AccessTimestamp: 1}))
    58  		assert.NoError(t, tx.ChunkStore().Put(context.Background(), ch2))
    59  		assert.NoError(t, tx.Commit())
    60  
    61  		item := cache.CacheEntryItem{Address: ch1.Address()}
    62  		assert.NoError(t, st.IndexStore().Get(&item))
    63  		assert.Equal(t, item, cache.CacheEntryItem{Address: ch1.Address(), AccessTimestamp: 1})
    64  
    65  		ch1_get, err := st.ChunkStore().Get(context.Background(), ch1.Address())
    66  		assert.NoError(t, err)
    67  		assert.Equal(t, ch1.Data(), ch1_get.Data())
    68  		assert.Equal(t, ch1.Address(), ch1_get.Address())
    69  
    70  		item = cache.CacheEntryItem{Address: ch2.Address()}
    71  		assert.NoError(t, st.IndexStore().Get(&item))
    72  		assert.Equal(t, item, cache.CacheEntryItem{Address: ch2.Address(), AccessTimestamp: 1})
    73  
    74  		ch2_get, err := st.ChunkStore().Get(context.Background(), ch1.Address())
    75  		assert.NoError(t, err)
    76  		assert.Equal(t, ch1.Data(), ch2_get.Data())
    77  		assert.Equal(t, ch1.Address(), ch2_get.Address())
    78  	})
    79  
    80  	t.Run("put-forget commit", func(t *testing.T) {
    81  		t.Parallel()
    82  
    83  		tx, done := st.NewTransaction(context.Background())
    84  
    85  		ch1 := test.GenerateTestRandomChunk()
    86  		ch2 := test.GenerateTestRandomChunk()
    87  
    88  		assert.NoError(t, tx.IndexStore().Put(&cache.CacheEntryItem{Address: ch1.Address(), AccessTimestamp: 1}))
    89  		assert.NoError(t, tx.ChunkStore().Put(context.Background(), ch1))
    90  		assert.NoError(t, tx.IndexStore().Put(&cache.CacheEntryItem{Address: ch2.Address(), AccessTimestamp: 1}))
    91  		assert.NoError(t, tx.ChunkStore().Put(context.Background(), ch2))
    92  
    93  		done()
    94  
    95  		assert.ErrorIs(t, st.IndexStore().Get(&cache.CacheEntryItem{Address: ch1.Address()}), storage.ErrNotFound)
    96  		assert.ErrorIs(t, st.IndexStore().Get(&cache.CacheEntryItem{Address: ch2.Address()}), storage.ErrNotFound)
    97  		_, err := st.ChunkStore().Get(context.Background(), ch1.Address())
    98  		assert.ErrorIs(t, err, storage.ErrNotFound)
    99  		_, err = st.ChunkStore().Get(context.Background(), ch2.Address())
   100  		assert.ErrorIs(t, err, storage.ErrNotFound)
   101  	})
   102  
   103  	t.Run("put-delete", func(t *testing.T) {
   104  		t.Parallel()
   105  
   106  		ch1 := test.GenerateTestRandomChunk()
   107  		ch2 := test.GenerateTestRandomChunk()
   108  
   109  		_ = st.Run(context.Background(), func(s transaction.Store) error {
   110  			assert.NoError(t, s.IndexStore().Put(&cache.CacheEntryItem{Address: ch1.Address(), AccessTimestamp: 1}))
   111  			assert.NoError(t, s.ChunkStore().Put(context.Background(), ch1))
   112  			assert.NoError(t, s.IndexStore().Put(&cache.CacheEntryItem{Address: ch2.Address(), AccessTimestamp: 1}))
   113  			assert.NoError(t, s.ChunkStore().Put(context.Background(), ch2))
   114  			return nil
   115  		})
   116  
   117  		item := cache.CacheEntryItem{Address: ch1.Address()}
   118  		assert.NoError(t, st.IndexStore().Get(&item))
   119  		assert.Equal(t, item, cache.CacheEntryItem{Address: ch1.Address(), AccessTimestamp: 1})
   120  
   121  		ch1_get, err := st.ChunkStore().Get(context.Background(), ch1.Address())
   122  		assert.NoError(t, err)
   123  		assert.Equal(t, ch1.Data(), ch1_get.Data())
   124  		assert.Equal(t, ch1.Address(), ch1_get.Address())
   125  
   126  		item = cache.CacheEntryItem{Address: ch2.Address()}
   127  		assert.NoError(t, st.IndexStore().Get(&item))
   128  		assert.Equal(t, item, cache.CacheEntryItem{Address: ch2.Address(), AccessTimestamp: 1})
   129  
   130  		ch2_get, err := st.ChunkStore().Get(context.Background(), ch1.Address())
   131  		assert.NoError(t, err)
   132  		assert.Equal(t, ch1.Data(), ch2_get.Data())
   133  		assert.Equal(t, ch1.Address(), ch2_get.Address())
   134  
   135  		_ = st.Run(context.Background(), func(s transaction.Store) error {
   136  			assert.NoError(t, s.IndexStore().Delete(&cache.CacheEntryItem{Address: ch1.Address(), AccessTimestamp: 1}))
   137  			assert.NoError(t, s.ChunkStore().Delete(context.Background(), ch1.Address()))
   138  			assert.NoError(t, s.IndexStore().Delete(&cache.CacheEntryItem{Address: ch2.Address(), AccessTimestamp: 1}))
   139  			assert.NoError(t, s.ChunkStore().Delete(context.Background(), ch2.Address()))
   140  			return nil
   141  		})
   142  
   143  		assert.ErrorIs(t, st.IndexStore().Get(&cache.CacheEntryItem{Address: ch1.Address()}), storage.ErrNotFound)
   144  		assert.ErrorIs(t, st.IndexStore().Get(&cache.CacheEntryItem{Address: ch2.Address()}), storage.ErrNotFound)
   145  		_, err = st.ChunkStore().Get(context.Background(), ch1.Address())
   146  		assert.ErrorIs(t, err, storage.ErrNotFound)
   147  		_, err = st.ChunkStore().Get(context.Background(), ch2.Address())
   148  		assert.ErrorIs(t, err, storage.ErrNotFound)
   149  	})
   150  
   151  	t.Run("put-delete-chunk", func(t *testing.T) {
   152  		t.Parallel()
   153  
   154  		ch1 := test.GenerateTestRandomChunk()
   155  
   156  		_ = st.Run(context.Background(), func(s transaction.Store) error {
   157  			assert.NoError(t, s.ChunkStore().Put(context.Background(), ch1))
   158  			assert.NoError(t, s.ChunkStore().Put(context.Background(), ch1))
   159  			assert.NoError(t, s.ChunkStore().Delete(context.Background(), ch1.Address()))
   160  			return nil
   161  		})
   162  
   163  		has, err := st.ChunkStore().Has(context.Background(), ch1.Address())
   164  		assert.NoError(t, err)
   165  		if !has {
   166  			t.Fatal("should have chunk")
   167  		}
   168  	})
   169  
   170  	t.Run("put-delete-chunk-twice", func(t *testing.T) {
   171  		t.Parallel()
   172  
   173  		ch1 := test.GenerateTestRandomChunk()
   174  
   175  		_ = st.Run(context.Background(), func(s transaction.Store) error {
   176  			assert.NoError(t, s.ChunkStore().Put(context.Background(), ch1))
   177  			assert.NoError(t, s.ChunkStore().Put(context.Background(), ch1))
   178  			assert.NoError(t, s.ChunkStore().Delete(context.Background(), ch1.Address()))
   179  			assert.NoError(t, s.ChunkStore().Delete(context.Background(), ch1.Address()))
   180  			return nil
   181  		})
   182  
   183  		has, err := st.ChunkStore().Has(context.Background(), ch1.Address())
   184  		assert.NoError(t, err)
   185  		if !has {
   186  			t.Fatal("should NOT have chunk")
   187  		}
   188  	})
   189  }