github.com/Finschia/finschia-sdk@v0.48.1/snapshots/manager_test.go (about)

     1  package snapshots_test
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/Finschia/finschia-sdk/snapshots"
    11  	"github.com/Finschia/finschia-sdk/snapshots/types"
    12  )
    13  
    14  func TestManager_List(t *testing.T) {
    15  	store := setupStore(t)
    16  	manager := snapshots.NewManager(store, nil)
    17  
    18  	mgrList, err := manager.List()
    19  	require.NoError(t, err)
    20  	storeList, err := store.List()
    21  	require.NoError(t, err)
    22  
    23  	require.NotEmpty(t, storeList)
    24  	assert.Equal(t, storeList, mgrList)
    25  
    26  	// list should not block or error on busy managers
    27  	manager = setupBusyManager(t)
    28  	list, err := manager.List()
    29  	require.NoError(t, err)
    30  	assert.Equal(t, []*types.Snapshot{}, list)
    31  }
    32  
    33  func TestManager_LoadChunk(t *testing.T) {
    34  	store := setupStore(t)
    35  	manager := snapshots.NewManager(store, nil)
    36  
    37  	// Existing chunk should return body
    38  	chunk, err := manager.LoadChunk(2, 1, 1)
    39  	require.NoError(t, err)
    40  	assert.Equal(t, []byte{2, 1, 1}, chunk)
    41  
    42  	// Missing chunk should return nil
    43  	chunk, err = manager.LoadChunk(2, 1, 9)
    44  	require.NoError(t, err)
    45  	assert.Nil(t, chunk)
    46  
    47  	// LoadChunk should not block or error on busy managers
    48  	manager = setupBusyManager(t)
    49  	chunk, err = manager.LoadChunk(2, 1, 0)
    50  	require.NoError(t, err)
    51  	assert.Nil(t, chunk)
    52  }
    53  
    54  func TestManager_Take(t *testing.T) {
    55  	store := setupStore(t)
    56  	items := [][]byte{
    57  		{1, 2, 3},
    58  		{4, 5, 6},
    59  		{7, 8, 9},
    60  	}
    61  	snapshotter := &mockSnapshotter{
    62  		items: items,
    63  	}
    64  	expectChunks := snapshotItems(items)
    65  	manager := snapshots.NewManager(store, snapshotter)
    66  
    67  	// nil manager should return error
    68  	_, err := (*snapshots.Manager)(nil).Create(1)
    69  	require.Error(t, err)
    70  
    71  	// creating a snapshot at a lower height than the latest should error
    72  	_, err = manager.Create(3)
    73  	require.Error(t, err)
    74  
    75  	// creating a snapshot at a higher height should be fine, and should return it
    76  	snapshot, err := manager.Create(5)
    77  	require.NoError(t, err)
    78  	assert.Equal(t, &types.Snapshot{
    79  		Height: 5,
    80  		Format: snapshotter.SnapshotFormat(),
    81  		Chunks: 1,
    82  		Hash:   []uint8{0x14, 0x38, 0x97, 0x96, 0xba, 0xe4, 0x81, 0xaf, 0x6c, 0xac, 0xff, 0xa5, 0xb8, 0x7e, 0x63, 0x4b, 0xac, 0x69, 0x3f, 0x38, 0x90, 0x5c, 0x7d, 0x57, 0xb3, 0xf, 0x69, 0x73, 0xb3, 0xa0, 0xe0, 0xad},
    83  		Metadata: types.Metadata{
    84  			ChunkHashes: checksums(expectChunks),
    85  		},
    86  	}, snapshot)
    87  
    88  	storeSnapshot, chunks, err := store.Load(snapshot.Height, snapshot.Format)
    89  	require.NoError(t, err)
    90  	assert.Equal(t, snapshot, storeSnapshot)
    91  	assert.Equal(t, expectChunks, readChunks(chunks))
    92  
    93  	// creating a snapshot while a different snapshot is being created should error
    94  	manager = setupBusyManager(t)
    95  	_, err = manager.Create(9)
    96  	require.Error(t, err)
    97  }
    98  
    99  func TestManager_Prune(t *testing.T) {
   100  	store := setupStore(t)
   101  	manager := snapshots.NewManager(store, nil)
   102  
   103  	pruned, err := manager.Prune(2)
   104  	require.NoError(t, err)
   105  	assert.EqualValues(t, 1, pruned)
   106  
   107  	list, err := manager.List()
   108  	require.NoError(t, err)
   109  	assert.Len(t, list, 3)
   110  
   111  	// Prune should error while a snapshot is being taken
   112  	manager = setupBusyManager(t)
   113  	_, err = manager.Prune(2)
   114  	require.Error(t, err)
   115  }
   116  
   117  func TestManager_Restore(t *testing.T) {
   118  	store := setupStore(t)
   119  	target := &mockSnapshotter{}
   120  	manager := snapshots.NewManager(store, target)
   121  
   122  	expectItems := [][]byte{
   123  		{1, 2, 3},
   124  		{4, 5, 6},
   125  		{7, 8, 9},
   126  	}
   127  
   128  	chunks := snapshotItems(expectItems)
   129  
   130  	// Restore errors on invalid format
   131  	err := manager.Restore(types.Snapshot{
   132  		Height:   3,
   133  		Format:   0,
   134  		Hash:     []byte{1, 2, 3},
   135  		Chunks:   uint32(len(chunks)),
   136  		Metadata: types.Metadata{ChunkHashes: checksums(chunks)},
   137  	})
   138  	require.Error(t, err)
   139  	require.ErrorIs(t, err, types.ErrUnknownFormat)
   140  
   141  	// Restore errors on no chunks
   142  	err = manager.Restore(types.Snapshot{Height: 3, Format: 1, Hash: []byte{1, 2, 3}})
   143  	require.Error(t, err)
   144  
   145  	// Restore errors on chunk and chunkhashes mismatch
   146  	err = manager.Restore(types.Snapshot{
   147  		Height:   3,
   148  		Format:   1,
   149  		Hash:     []byte{1, 2, 3},
   150  		Chunks:   4,
   151  		Metadata: types.Metadata{ChunkHashes: checksums(chunks)},
   152  	})
   153  	require.Error(t, err)
   154  
   155  	// Starting a restore works
   156  	err = manager.Restore(types.Snapshot{
   157  		Height:   3,
   158  		Format:   1,
   159  		Hash:     []byte{1, 2, 3},
   160  		Chunks:   1,
   161  		Metadata: types.Metadata{ChunkHashes: checksums(chunks)},
   162  	})
   163  	require.NoError(t, err)
   164  
   165  	// While the restore is in progress, any other operations fail
   166  	_, err = manager.Create(4)
   167  	require.Error(t, err)
   168  
   169  	_, err = manager.Prune(1)
   170  	require.Error(t, err)
   171  
   172  	// Feeding an invalid chunk should error due to invalid checksum, but not abort restoration.
   173  	_, err = manager.RestoreChunk([]byte{9, 9, 9})
   174  	require.Error(t, err)
   175  	require.True(t, errors.Is(err, types.ErrChunkHashMismatch))
   176  
   177  	// Feeding the chunks should work
   178  	for i, chunk := range chunks {
   179  		done, err := manager.RestoreChunk(chunk)
   180  		require.NoError(t, err)
   181  		if i == len(chunks)-1 {
   182  			assert.True(t, done)
   183  		} else {
   184  			assert.False(t, done)
   185  		}
   186  	}
   187  
   188  	assert.Equal(t, expectItems, target.items)
   189  
   190  	// Starting a new restore should fail now, because the target already has contents.
   191  	err = manager.Restore(types.Snapshot{
   192  		Height:   3,
   193  		Format:   1,
   194  		Hash:     []byte{1, 2, 3},
   195  		Chunks:   3,
   196  		Metadata: types.Metadata{ChunkHashes: checksums(chunks)},
   197  	})
   198  	require.Error(t, err)
   199  
   200  	// But if we clear out the target we should be able to start a new restore. This time we'll
   201  	// fail it with a checksum error. That error should stop the operation, so that we can do
   202  	// a prune operation right after.
   203  	target.items = nil
   204  	err = manager.Restore(types.Snapshot{
   205  		Height:   3,
   206  		Format:   1,
   207  		Hash:     []byte{1, 2, 3},
   208  		Chunks:   1,
   209  		Metadata: types.Metadata{ChunkHashes: checksums(chunks)},
   210  	})
   211  	require.NoError(t, err)
   212  }