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 }