cosmos-sdk/snapshots/store_test.go

335 lines
9.0 KiB
Go
Raw Normal View History

package snapshots_test
import (
"bytes"
"errors"
"io"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
db "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/snapshots"
"github.com/cosmos/cosmos-sdk/snapshots/types"
"github.com/cosmos/cosmos-sdk/testutil"
)
func setupStore(t *testing.T) *snapshots.Store {
refactor: move from io/ioutil to io and os package (#10341) ## Description The `io/ioutil` package has been deprecated in Go 1.16 (See https://golang.org/doc/go1.16#ioutil). Since cosmos-sdk has upgraded to Go 1.17 (#9987), this PR replaces the existing `io/ioutil` functions with their new definitions in `io` and `os` packages. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-10-13 00:38:22 -07:00
// os.MkdirTemp() is used instead of testing.T.TempDir()
// see https://github.com/cosmos/cosmos-sdk/pull/8475 for
// this change's rationale.
refactor: move from io/ioutil to io and os package (#10341) ## Description The `io/ioutil` package has been deprecated in Go 1.16 (See https://golang.org/doc/go1.16#ioutil). Since cosmos-sdk has upgraded to Go 1.17 (#9987), this PR replaces the existing `io/ioutil` functions with their new definitions in `io` and `os` packages. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-10-13 00:38:22 -07:00
tempdir, err := os.MkdirTemp("", "")
require.NoError(t, err)
t.Cleanup(func() { _ = os.RemoveAll(tempdir) })
store, err := snapshots.NewStore(db.NewMemDB(), tempdir)
require.NoError(t, err)
_, err = store.Save(1, 1, makeChunks([][]byte{
{1, 1, 0}, {1, 1, 1},
}))
require.NoError(t, err)
_, err = store.Save(2, 1, makeChunks([][]byte{
{2, 1, 0}, {2, 1, 1},
}))
require.NoError(t, err)
_, err = store.Save(2, 2, makeChunks([][]byte{
{2, 2, 0}, {2, 2, 1}, {2, 2, 2},
}))
require.NoError(t, err)
_, err = store.Save(3, 2, makeChunks([][]byte{
{3, 2, 0}, {3, 2, 1}, {3, 2, 2},
}))
require.NoError(t, err)
return store
}
func TestNewStore(t *testing.T) {
tempdir := t.TempDir()
_, err := snapshots.NewStore(db.NewMemDB(), tempdir)
require.NoError(t, err)
}
func TestNewStore_ErrNoDir(t *testing.T) {
_, err := snapshots.NewStore(db.NewMemDB(), "")
require.Error(t, err)
}
func TestNewStore_ErrDirFailure(t *testing.T) {
notADir := filepath.Join(testutil.TempFile(t).Name(), "subdir")
_, err := snapshots.NewStore(db.NewMemDB(), notADir)
require.Error(t, err)
}
func TestStore_Delete(t *testing.T) {
store := setupStore(t)
// Deleting a snapshot should remove it
err := store.Delete(2, 2)
require.NoError(t, err)
snapshot, err := store.Get(2, 2)
require.NoError(t, err)
assert.Nil(t, snapshot)
snapshots, err := store.List()
require.NoError(t, err)
assert.Len(t, snapshots, 3)
// Deleting it again should not error
err = store.Delete(2, 2)
require.NoError(t, err)
// Deleting a snapshot being saved should error
ch := make(chan io.ReadCloser)
go store.Save(9, 1, ch)
time.Sleep(10 * time.Millisecond)
err = store.Delete(9, 1)
require.Error(t, err)
// But after it's saved it should work
close(ch)
time.Sleep(10 * time.Millisecond)
err = store.Delete(9, 1)
require.NoError(t, err)
}
func TestStore_Get(t *testing.T) {
store := setupStore(t)
// Loading a missing snapshot should return nil
snapshot, err := store.Get(9, 9)
require.NoError(t, err)
assert.Nil(t, snapshot)
// Loading a snapshot should returns its metadata
snapshot, err = store.Get(2, 1)
require.NoError(t, err)
assert.Equal(t, &types.Snapshot{
Height: 2,
Format: 1,
Chunks: 2,
Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}),
Metadata: types.Metadata{
ChunkHashes: checksums([][]byte{
{2, 1, 0}, {2, 1, 1}}),
},
}, snapshot)
}
func TestStore_GetLatest(t *testing.T) {
store := setupStore(t)
// Loading a missing snapshot should return nil
snapshot, err := store.GetLatest()
require.NoError(t, err)
assert.Equal(t, &types.Snapshot{
Height: 3,
Format: 2,
Chunks: 3,
Hash: hash([][]byte{
{3, 2, 0},
{3, 2, 1},
{3, 2, 2},
}),
Metadata: types.Metadata{
ChunkHashes: checksums([][]byte{
{3, 2, 0},
{3, 2, 1},
{3, 2, 2},
}),
},
}, snapshot)
}
func TestStore_List(t *testing.T) {
store := setupStore(t)
snapshots, err := store.List()
require.NoError(t, err)
require.Equal(t, []*types.Snapshot{
{Height: 3, Format: 2, Chunks: 3, Hash: hash([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}}),
Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}})},
},
{Height: 2, Format: 2, Chunks: 3, Hash: hash([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}}),
Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}})},
},
{Height: 2, Format: 1, Chunks: 2, Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}),
Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 1, 0}, {2, 1, 1}})},
},
{Height: 1, Format: 1, Chunks: 2, Hash: hash([][]byte{{1, 1, 0}, {1, 1, 1}}),
Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{1, 1, 0}, {1, 1, 1}})},
},
}, snapshots)
}
func TestStore_Load(t *testing.T) {
store := setupStore(t)
// Loading a missing snapshot should return nil
snapshot, chunks, err := store.Load(9, 9)
require.NoError(t, err)
assert.Nil(t, snapshot)
assert.Nil(t, chunks)
// Loading a snapshot should returns its metadata and chunks
snapshot, chunks, err = store.Load(2, 1)
require.NoError(t, err)
assert.Equal(t, &types.Snapshot{
Height: 2,
Format: 1,
Chunks: 2,
Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}),
Metadata: types.Metadata{
ChunkHashes: checksums([][]byte{
{2, 1, 0}, {2, 1, 1}}),
},
}, snapshot)
for i := uint32(0); i < snapshot.Chunks; i++ {
reader, ok := <-chunks
require.True(t, ok)
refactor: move from io/ioutil to io and os package (#10341) ## Description The `io/ioutil` package has been deprecated in Go 1.16 (See https://golang.org/doc/go1.16#ioutil). Since cosmos-sdk has upgraded to Go 1.17 (#9987), this PR replaces the existing `io/ioutil` functions with their new definitions in `io` and `os` packages. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-10-13 00:38:22 -07:00
chunk, err := io.ReadAll(reader)
require.NoError(t, err)
err = reader.Close()
require.NoError(t, err)
assert.Equal(t, []byte{2, 1, byte(i)}, chunk)
}
assert.Empty(t, chunks)
}
func TestStore_LoadChunk(t *testing.T) {
store := setupStore(t)
// Loading a missing snapshot should return nil
chunk, err := store.LoadChunk(9, 9, 0)
require.NoError(t, err)
assert.Nil(t, chunk)
// Loading a missing chunk index should return nil
chunk, err = store.LoadChunk(2, 1, 2)
require.NoError(t, err)
require.Nil(t, chunk)
// Loading a chunk should returns a content reader
chunk, err = store.LoadChunk(2, 1, 0)
require.NoError(t, err)
require.NotNil(t, chunk)
refactor: move from io/ioutil to io and os package (#10341) ## Description The `io/ioutil` package has been deprecated in Go 1.16 (See https://golang.org/doc/go1.16#ioutil). Since cosmos-sdk has upgraded to Go 1.17 (#9987), this PR replaces the existing `io/ioutil` functions with their new definitions in `io` and `os` packages. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-10-13 00:38:22 -07:00
body, err := io.ReadAll(chunk)
require.NoError(t, err)
assert.Equal(t, []byte{2, 1, 0}, body)
err = chunk.Close()
require.NoError(t, err)
}
func TestStore_Prune(t *testing.T) {
store := setupStore(t)
// Pruning too many snapshots should be fine
pruned, err := store.Prune(4)
require.NoError(t, err)
assert.EqualValues(t, 0, pruned)
snapshots, err := store.List()
require.NoError(t, err)
assert.Len(t, snapshots, 4)
// Pruning until the last two heights should leave three snapshots (for two heights)
pruned, err = store.Prune(2)
require.NoError(t, err)
assert.EqualValues(t, 1, pruned)
snapshots, err = store.List()
require.NoError(t, err)
require.Equal(t, []*types.Snapshot{
{Height: 3, Format: 2, Chunks: 3, Hash: hash([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}}),
Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}})},
},
{Height: 2, Format: 2, Chunks: 3, Hash: hash([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}}),
Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}})},
},
{Height: 2, Format: 1, Chunks: 2, Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}),
Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 1, 0}, {2, 1, 1}})},
},
}, snapshots)
// Pruning all heights should also be fine
pruned, err = store.Prune(0)
require.NoError(t, err)
assert.EqualValues(t, 3, pruned)
snapshots, err = store.List()
require.NoError(t, err)
assert.Empty(t, snapshots)
}
func TestStore_Save(t *testing.T) {
store := setupStore(t)
// Saving a snapshot should work
snapshot, err := store.Save(4, 1, makeChunks([][]byte{{1}, {2}}))
require.NoError(t, err)
assert.Equal(t, &types.Snapshot{
Height: 4,
Format: 1,
Chunks: 2,
Hash: hash([][]byte{{1}, {2}}),
Metadata: types.Metadata{
ChunkHashes: checksums([][]byte{{1}, {2}}),
},
}, snapshot)
loaded, err := store.Get(snapshot.Height, snapshot.Format)
require.NoError(t, err)
assert.Equal(t, snapshot, loaded)
// Saving an existing snapshot should error
_, err = store.Save(4, 1, makeChunks([][]byte{{1}, {2}}))
require.Error(t, err)
// Saving at height 0 should error
_, err = store.Save(0, 1, makeChunks([][]byte{{1}, {2}}))
require.Error(t, err)
// Saving at format 0 should be fine
_, err = store.Save(1, 0, makeChunks([][]byte{{1}, {2}}))
require.NoError(t, err)
// Saving a snapshot with no chunks should be fine, as should loading it
_, err = store.Save(5, 1, makeChunks([][]byte{}))
require.NoError(t, err)
snapshot, chunks, err := store.Load(5, 1)
require.NoError(t, err)
assert.Equal(t, &types.Snapshot{Height: 5, Format: 1, Hash: hash([][]byte{}), Metadata: types.Metadata{ChunkHashes: [][]byte{}}}, snapshot)
assert.Empty(t, chunks)
// Saving a snapshot should error if a chunk reader returns an error, and it should empty out
// the channel
someErr := errors.New("boom")
pr, pw := io.Pipe()
err = pw.CloseWithError(someErr)
require.NoError(t, err)
ch := make(chan io.ReadCloser, 2)
ch <- pr
refactor: move from io/ioutil to io and os package (#10341) ## Description The `io/ioutil` package has been deprecated in Go 1.16 (See https://golang.org/doc/go1.16#ioutil). Since cosmos-sdk has upgraded to Go 1.17 (#9987), this PR replaces the existing `io/ioutil` functions with their new definitions in `io` and `os` packages. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
2021-10-13 00:38:22 -07:00
ch <- io.NopCloser(bytes.NewBuffer([]byte{0xff}))
close(ch)
_, err = store.Save(6, 1, ch)
require.Error(t, err)
require.True(t, errors.Is(err, someErr))
assert.Empty(t, ch)
// Saving a snapshot should error if a snapshot is already in progress for the same height,
// regardless of format. However, a different height should succeed.
ch = make(chan io.ReadCloser)
go store.Save(7, 1, ch)
time.Sleep(10 * time.Millisecond)
_, err = store.Save(7, 2, makeChunks(nil))
require.Error(t, err)
_, err = store.Save(8, 1, makeChunks(nil))
require.NoError(t, err)
close(ch)
}