github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/migration/reserveRepair_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 migration_test
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"testing"
    11  
    12  	"github.com/ethersphere/bee/v2/pkg/log"
    13  	"github.com/ethersphere/bee/v2/pkg/storage"
    14  	chunktest "github.com/ethersphere/bee/v2/pkg/storage/testing"
    15  	"github.com/ethersphere/bee/v2/pkg/storer/internal"
    16  	"github.com/ethersphere/bee/v2/pkg/storer/internal/chunkstamp"
    17  	"github.com/ethersphere/bee/v2/pkg/storer/internal/reserve"
    18  	"github.com/ethersphere/bee/v2/pkg/storer/internal/transaction"
    19  	localmigration "github.com/ethersphere/bee/v2/pkg/storer/migration"
    20  	"github.com/ethersphere/bee/v2/pkg/swarm"
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  func TestReserveRepair(t *testing.T) {
    25  	t.Parallel()
    26  
    27  	store := internal.NewInmemStorage()
    28  	baseAddr := swarm.RandAddress(t)
    29  	stepFn := localmigration.ReserveRepairer(store, func(_ swarm.Chunk) swarm.ChunkType {
    30  		return swarm.ChunkTypeContentAddressed
    31  	}, log.Noop)
    32  
    33  	var chunksPO = make([][]swarm.Chunk, 5)
    34  	var chunksPerPO uint64 = 2
    35  
    36  	for i := uint8(0); i < swarm.MaxBins; i++ {
    37  		err := store.Run(context.Background(), func(s transaction.Store) error {
    38  			return s.IndexStore().Put(&reserve.BinItem{Bin: i, BinID: 10})
    39  		})
    40  		assert.NoError(t, err)
    41  	}
    42  
    43  	for b := 0; b < 5; b++ {
    44  		for i := uint64(0); i < chunksPerPO; i++ {
    45  			ch := chunktest.GenerateTestRandomChunkAt(t, baseAddr, b)
    46  			stampHash, err := ch.Stamp().Hash()
    47  			if err != nil {
    48  				t.Fatal(err)
    49  			}
    50  			cb := &reserve.ChunkBinItem{
    51  				Bin: uint8(b),
    52  				// Assign 0 binID to all items to see if migration fixes this
    53  				BinID:     0,
    54  				Address:   ch.Address(),
    55  				BatchID:   ch.Stamp().BatchID(),
    56  				ChunkType: swarm.ChunkTypeContentAddressed,
    57  				StampHash: stampHash,
    58  			}
    59  			err = store.Run(context.Background(), func(s transaction.Store) error {
    60  				return s.IndexStore().Put(cb)
    61  			})
    62  			if err != nil {
    63  				t.Fatal(err)
    64  			}
    65  
    66  			br := &reserve.BatchRadiusItem{
    67  				BatchID:   ch.Stamp().BatchID(),
    68  				Bin:       uint8(b),
    69  				Address:   ch.Address(),
    70  				BinID:     0,
    71  				StampHash: stampHash,
    72  			}
    73  			err = store.Run(context.Background(), func(s transaction.Store) error {
    74  				return s.IndexStore().Put(br)
    75  			})
    76  			if err != nil {
    77  				t.Fatal(err)
    78  			}
    79  
    80  			if b < 2 {
    81  				// simulate missing chunkstore entries
    82  				continue
    83  			}
    84  
    85  			err = store.Run(context.Background(), func(s transaction.Store) error {
    86  				err := chunkstamp.Store(s.IndexStore(), "reserve", ch)
    87  				if err != nil {
    88  					return err
    89  				}
    90  				return s.ChunkStore().Put(context.Background(), ch)
    91  			})
    92  			if err != nil {
    93  				t.Fatal(err)
    94  			}
    95  
    96  			chunksPO[b] = append(chunksPO[b], ch)
    97  		}
    98  	}
    99  
   100  	assert.NoError(t, stepFn())
   101  
   102  	binIDs := make(map[uint8][]uint64)
   103  	cbCount := 0
   104  	err := store.IndexStore().Iterate(
   105  		storage.Query{Factory: func() storage.Item { return &reserve.ChunkBinItem{} }},
   106  		func(res storage.Result) (stop bool, err error) {
   107  			cb := res.Entry.(*reserve.ChunkBinItem)
   108  			if cb.ChunkType != swarm.ChunkTypeContentAddressed {
   109  				return false, errors.New("chunk type should be content addressed")
   110  			}
   111  			binIDs[cb.Bin] = append(binIDs[cb.Bin], cb.BinID)
   112  			cbCount++
   113  			return false, nil
   114  		},
   115  	)
   116  	assert.NoError(t, err)
   117  
   118  	for b := 0; b < 5; b++ {
   119  		if b < 2 {
   120  			if _, found := binIDs[uint8(b)]; found {
   121  				t.Fatalf("bin %d should not have any binIDs", b)
   122  			}
   123  			continue
   124  		}
   125  		assert.Len(t, binIDs[uint8(b)], 2)
   126  		for idx, binID := range binIDs[uint8(b)] {
   127  			assert.Equal(t, uint64(idx+1), binID)
   128  
   129  			item := &reserve.BinItem{Bin: uint8(b)}
   130  			_ = store.IndexStore().Get(item)
   131  			assert.Equal(t, item.BinID, uint64(2))
   132  		}
   133  	}
   134  
   135  	brCount := 0
   136  	err = store.IndexStore().Iterate(
   137  		storage.Query{Factory: func() storage.Item { return &reserve.BatchRadiusItem{} }},
   138  		func(res storage.Result) (stop bool, err error) {
   139  			br := res.Entry.(*reserve.BatchRadiusItem)
   140  			if br.Bin < 2 {
   141  				return false, errors.New("bin should be >= 2")
   142  			}
   143  			brCount++
   144  			return false, nil
   145  		},
   146  	)
   147  	assert.NoError(t, err)
   148  
   149  	assert.Equal(t, cbCount, brCount)
   150  
   151  	has, err := store.IndexStore().Has(&reserve.EpochItem{})
   152  	if has {
   153  		t.Fatal("epoch item should be deleted")
   154  	}
   155  	assert.NoError(t, err)
   156  }