412 lines
12 KiB
Go
412 lines
12 KiB
Go
package orm
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
"github.com/cosmos/cosmos-sdk/codec/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/x/group/errors"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
|
)
|
|
|
|
// Testing ORM with arbitrary metadata length
|
|
const metadataLen = 10
|
|
|
|
func TestKeeperEndToEndWithAutoUInt64Table(t *testing.T) {
|
|
interfaceRegistry := types.NewInterfaceRegistry()
|
|
cdc := codec.NewProtoCodec(interfaceRegistry)
|
|
|
|
ctx := NewMockContext()
|
|
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
k := NewTestKeeper(cdc)
|
|
|
|
tm := testdata.TableModel{
|
|
Id: 1,
|
|
Name: "name",
|
|
Number: 123,
|
|
Metadata: []byte("metadata"),
|
|
}
|
|
// when stored
|
|
rowID, err := k.autoUInt64Table.Create(store, &tm)
|
|
require.NoError(t, err)
|
|
// then we should find it
|
|
exists := k.autoUInt64Table.Has(store, rowID)
|
|
require.True(t, exists)
|
|
|
|
// and load it
|
|
var loaded testdata.TableModel
|
|
|
|
binKey, err := k.autoUInt64Table.GetOne(store, rowID, &loaded)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
|
|
require.Equal(t, tm, loaded)
|
|
|
|
// and exists in MultiKeyIndex
|
|
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("metadata"))
|
|
require.NoError(t, err)
|
|
require.True(t, exists)
|
|
|
|
// and when loaded
|
|
it, err := k.autoUInt64TableModelByMetadataIndex.Get(store, []byte("metadata"))
|
|
require.NoError(t, err)
|
|
|
|
// then
|
|
binKey, loaded = first(t, it)
|
|
assert.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
|
|
assert.Equal(t, tm, loaded)
|
|
|
|
// when updated
|
|
tm.Metadata = []byte("new-metadata")
|
|
err = k.autoUInt64Table.Update(store, rowID, &tm)
|
|
require.NoError(t, err)
|
|
|
|
binKey, err = k.autoUInt64Table.GetOne(store, rowID, &loaded)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, rowID, binary.BigEndian.Uint64(binKey))
|
|
require.Equal(t, tm, loaded)
|
|
|
|
// then indexes are updated, too
|
|
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("new-metadata"))
|
|
require.NoError(t, err)
|
|
require.True(t, exists)
|
|
|
|
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("metadata"))
|
|
require.NoError(t, err)
|
|
require.False(t, exists)
|
|
|
|
// when deleted
|
|
err = k.autoUInt64Table.Delete(store, rowID)
|
|
require.NoError(t, err)
|
|
|
|
exists = k.autoUInt64Table.Has(store, rowID)
|
|
require.False(t, exists)
|
|
|
|
// and also removed from secondary MultiKeyIndex
|
|
exists, err = k.autoUInt64TableModelByMetadataIndex.Has(store, []byte("new-metadata"))
|
|
require.NoError(t, err)
|
|
require.False(t, exists)
|
|
}
|
|
|
|
func TestKeeperEndToEndWithPrimaryKeyTable(t *testing.T) {
|
|
interfaceRegistry := types.NewInterfaceRegistry()
|
|
cdc := codec.NewProtoCodec(interfaceRegistry)
|
|
|
|
ctx := NewMockContext()
|
|
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
k := NewTestKeeper(cdc)
|
|
|
|
tm := testdata.TableModel{
|
|
Id: 1,
|
|
Name: "name",
|
|
Number: 123,
|
|
Metadata: []byte("metadata"),
|
|
}
|
|
// when stored
|
|
err := k.primaryKeyTable.Create(store, &tm)
|
|
require.NoError(t, err)
|
|
// then we should find it by primary key
|
|
primaryKey := PrimaryKey(&tm)
|
|
exists := k.primaryKeyTable.Has(store, primaryKey)
|
|
require.True(t, exists)
|
|
|
|
// and load it by primary key
|
|
var loaded testdata.TableModel
|
|
err = k.primaryKeyTable.GetOne(store, primaryKey, &loaded)
|
|
require.NoError(t, err)
|
|
|
|
// then values should match expectations
|
|
require.Equal(t, tm, loaded)
|
|
|
|
// and then the data should exists in MultiKeyIndex
|
|
exists, err = k.primaryKeyTableModelByNumberIndex.Has(store, tm.Number)
|
|
require.NoError(t, err)
|
|
require.True(t, exists)
|
|
|
|
// and when loaded from MultiKeyIndex
|
|
it, err := k.primaryKeyTableModelByNumberIndex.Get(store, tm.Number)
|
|
require.NoError(t, err)
|
|
|
|
// then values should match as before
|
|
_, err = First(it, &loaded)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tm, loaded)
|
|
|
|
// and when we create another entry with the same primary key
|
|
err = k.primaryKeyTable.Create(store, &tm)
|
|
// then it should fail as the primary key must be unique
|
|
require.True(t, errors.ErrORMUniqueConstraint.Is(err), err)
|
|
|
|
// and when entity updated with new primary key
|
|
updatedMember := &testdata.TableModel{
|
|
Id: 2,
|
|
Name: tm.Name,
|
|
Number: tm.Number,
|
|
Metadata: tm.Metadata,
|
|
}
|
|
// then it should fail as the primary key is immutable
|
|
err = k.primaryKeyTable.Update(store, updatedMember)
|
|
require.Error(t, err)
|
|
|
|
// and when entity updated with non primary key attribute modified
|
|
updatedMember = &testdata.TableModel{
|
|
Id: 1,
|
|
Name: "new name",
|
|
Number: tm.Number,
|
|
Metadata: tm.Metadata,
|
|
}
|
|
// then it should not fail
|
|
err = k.primaryKeyTable.Update(store, updatedMember)
|
|
require.NoError(t, err)
|
|
|
|
// and when entity deleted
|
|
err = k.primaryKeyTable.Delete(store, &tm)
|
|
require.NoError(t, err)
|
|
|
|
// it is removed from primaryKeyTable
|
|
exists = k.primaryKeyTable.Has(store, primaryKey)
|
|
require.False(t, exists)
|
|
|
|
// and removed from secondary MultiKeyIndex
|
|
exists, err = k.primaryKeyTableModelByNumberIndex.Has(store, tm.Number)
|
|
require.NoError(t, err)
|
|
require.False(t, exists)
|
|
}
|
|
|
|
func TestGasCostsPrimaryKeyTable(t *testing.T) {
|
|
interfaceRegistry := types.NewInterfaceRegistry()
|
|
cdc := codec.NewProtoCodec(interfaceRegistry)
|
|
|
|
ctx := NewMockContext()
|
|
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
k := NewTestKeeper(cdc)
|
|
|
|
tm := testdata.TableModel{
|
|
Id: 1,
|
|
Name: "name",
|
|
Number: 123,
|
|
Metadata: []byte("metadata"),
|
|
}
|
|
rowID, err := k.autoUInt64Table.Create(store, &tm)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(1), rowID)
|
|
|
|
gCtx := NewGasCountingMockContext()
|
|
err = k.primaryKeyTable.Create(gCtx.KVStore(store), &tm)
|
|
require.NoError(t, err)
|
|
t.Logf("gas consumed on create: %d", gCtx.GasConsumed())
|
|
|
|
// get by primary key
|
|
gCtx.ResetGasMeter()
|
|
var loaded testdata.TableModel
|
|
err = k.primaryKeyTable.GetOne(gCtx.KVStore(store), PrimaryKey(&tm), &loaded)
|
|
require.NoError(t, err)
|
|
t.Logf("gas consumed on get by primary key: %d", gCtx.GasConsumed())
|
|
|
|
// get by secondary index
|
|
gCtx.ResetGasMeter()
|
|
// and when loaded from MultiKeyIndex
|
|
it, err := k.primaryKeyTableModelByNumberIndex.Get(gCtx.KVStore(store), tm.Number)
|
|
require.NoError(t, err)
|
|
var loadedSlice []testdata.TableModel
|
|
_, err = ReadAll(it, &loadedSlice)
|
|
require.NoError(t, err)
|
|
t.Logf("gas consumed on get by multi index key: %d", gCtx.GasConsumed())
|
|
|
|
// delete
|
|
gCtx.ResetGasMeter()
|
|
err = k.primaryKeyTable.Delete(gCtx.KVStore(store), &tm)
|
|
require.NoError(t, err)
|
|
t.Logf("gas consumed on delete by primary key: %d", gCtx.GasConsumed())
|
|
|
|
// with 3 elements
|
|
var tms []testdata.TableModel
|
|
for i := 1; i < 4; i++ {
|
|
gCtx.ResetGasMeter()
|
|
tm := testdata.TableModel{
|
|
Id: uint64(i),
|
|
Name: fmt.Sprintf("name%d", i),
|
|
Number: 123,
|
|
Metadata: []byte("metadata"),
|
|
}
|
|
err = k.primaryKeyTable.Create(gCtx.KVStore(store), &tm)
|
|
require.NoError(t, err)
|
|
t.Logf("%d: gas consumed on create: %d", i, gCtx.GasConsumed())
|
|
tms = append(tms, tm)
|
|
}
|
|
|
|
for i := 1; i < 4; i++ {
|
|
gCtx.ResetGasMeter()
|
|
tm := testdata.TableModel{
|
|
Id: uint64(i),
|
|
Name: fmt.Sprintf("name%d", i),
|
|
Number: 123,
|
|
Metadata: []byte("metadata"),
|
|
}
|
|
err = k.primaryKeyTable.GetOne(gCtx.KVStore(store), PrimaryKey(&tm), &loaded)
|
|
require.NoError(t, err)
|
|
t.Logf("%d: gas consumed on get by primary key: %d", i, gCtx.GasConsumed())
|
|
}
|
|
|
|
// get by secondary index
|
|
gCtx.ResetGasMeter()
|
|
// and when loaded from MultiKeyIndex
|
|
it, err = k.primaryKeyTableModelByNumberIndex.Get(gCtx.KVStore(store), tm.Number)
|
|
require.NoError(t, err)
|
|
_, err = ReadAll(it, &loadedSlice)
|
|
require.NoError(t, err)
|
|
require.Len(t, loadedSlice, 3)
|
|
t.Logf("gas consumed on get by multi index key: %d", gCtx.GasConsumed())
|
|
|
|
// delete
|
|
for i, m := range tms {
|
|
gCtx.ResetGasMeter()
|
|
|
|
err = k.primaryKeyTable.Delete(gCtx.KVStore(store), &m)
|
|
require.NoError(t, err)
|
|
t.Logf("%d: gas consumed on delete: %d", i, gCtx.GasConsumed())
|
|
}
|
|
}
|
|
|
|
func TestExportImportStateAutoUInt64Table(t *testing.T) {
|
|
interfaceRegistry := types.NewInterfaceRegistry()
|
|
cdc := codec.NewProtoCodec(interfaceRegistry)
|
|
|
|
ctx := NewMockContext()
|
|
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
k := NewTestKeeper(cdc)
|
|
|
|
testRecordsNum := 10
|
|
for i := 1; i <= testRecordsNum; i++ {
|
|
tm := testdata.TableModel{
|
|
Id: uint64(i),
|
|
Name: fmt.Sprintf("my test %d", i),
|
|
Metadata: bytes.Repeat([]byte{byte(i)}, metadataLen),
|
|
}
|
|
|
|
rowID, err := k.autoUInt64Table.Create(store, &tm)
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint64(i), rowID)
|
|
}
|
|
var tms []*testdata.TableModel
|
|
seqVal, err := k.autoUInt64Table.Export(store, &tms)
|
|
require.NoError(t, err)
|
|
require.Equal(t, seqVal, uint64(testRecordsNum))
|
|
|
|
// when a new db seeded
|
|
ctx = NewMockContext()
|
|
store = ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
err = k.autoUInt64Table.Import(store, tms, seqVal)
|
|
require.NoError(t, err)
|
|
|
|
// then all data is set again
|
|
for i := 1; i <= testRecordsNum; i++ {
|
|
require.True(t, k.autoUInt64Table.Has(store, uint64(i)))
|
|
var loaded testdata.TableModel
|
|
rowID, err := k.autoUInt64Table.GetOne(store, uint64(i), &loaded)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, RowID(EncodeSequence(uint64(i))), rowID)
|
|
assert.Equal(t, fmt.Sprintf("my test %d", i), loaded.Name)
|
|
exp := bytes.Repeat([]byte{byte(i)}, metadataLen)
|
|
assert.Equal(t, exp, loaded.Metadata)
|
|
|
|
// and also the indexes
|
|
exists, err := k.autoUInt64TableModelByMetadataIndex.Has(store, exp)
|
|
require.NoError(t, err)
|
|
require.True(t, exists)
|
|
|
|
it, err := k.autoUInt64TableModelByMetadataIndex.Get(store, exp)
|
|
require.NoError(t, err)
|
|
var all []testdata.TableModel
|
|
ReadAll(it, &all)
|
|
require.Len(t, all, 1)
|
|
assert.Equal(t, loaded, all[0])
|
|
}
|
|
require.Equal(t, uint64(testRecordsNum), k.autoUInt64Table.Sequence().CurVal(store))
|
|
}
|
|
|
|
func TestExportImportStatePrimaryKeyTable(t *testing.T) {
|
|
interfaceRegistry := types.NewInterfaceRegistry()
|
|
cdc := codec.NewProtoCodec(interfaceRegistry)
|
|
|
|
ctx := NewMockContext()
|
|
store := ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
k := NewTestKeeper(cdc)
|
|
|
|
testRecordsNum := 10
|
|
testRecords := make([]testdata.TableModel, testRecordsNum)
|
|
for i := 1; i <= testRecordsNum; i++ {
|
|
tm := testdata.TableModel{
|
|
Id: uint64(i),
|
|
Name: fmt.Sprintf("my test %d", i),
|
|
Number: uint64(i - 1),
|
|
Metadata: bytes.Repeat([]byte{byte(i)}, metadataLen),
|
|
}
|
|
|
|
err := k.primaryKeyTable.Create(store, &tm)
|
|
require.NoError(t, err)
|
|
testRecords[i-1] = tm
|
|
}
|
|
var tms []*testdata.TableModel
|
|
_, err := k.primaryKeyTable.Export(store, &tms)
|
|
require.NoError(t, err)
|
|
|
|
// when a new db seeded
|
|
ctx = NewMockContext()
|
|
store = ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
err = k.primaryKeyTable.Import(store, tms, 0)
|
|
require.NoError(t, err)
|
|
|
|
// then all data is set again
|
|
it, err := k.primaryKeyTable.PrefixScan(store, nil, nil)
|
|
require.NoError(t, err)
|
|
var loaded []testdata.TableModel
|
|
keys, err := ReadAll(it, &loaded)
|
|
require.NoError(t, err)
|
|
for i := range keys {
|
|
assert.Equal(t, PrimaryKey(&testRecords[i]), keys[i].Bytes())
|
|
}
|
|
assert.Equal(t, testRecords, loaded)
|
|
|
|
// all indexes setup
|
|
for _, v := range testRecords {
|
|
assertIndex(t, store, k.primaryKeyTableModelByNameIndex, v, v.Name)
|
|
assertIndex(t, store, k.primaryKeyTableModelByNumberIndex, v, v.Number)
|
|
assertIndex(t, store, k.primaryKeyTableModelByMetadataIndex, v, v.Metadata)
|
|
}
|
|
}
|
|
|
|
func assertIndex(t *testing.T, store sdk.KVStore, index Index, v testdata.TableModel, searchKey interface{}) {
|
|
it, err := index.Get(store, searchKey)
|
|
require.NoError(t, err)
|
|
|
|
var loaded []testdata.TableModel
|
|
keys, err := ReadAll(it, &loaded)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []RowID{PrimaryKey(&v)}, keys)
|
|
assert.Equal(t, []testdata.TableModel{v}, loaded)
|
|
}
|
|
|
|
func first(t *testing.T, it Iterator) ([]byte, testdata.TableModel) {
|
|
var loaded testdata.TableModel
|
|
key, err := First(it, &loaded)
|
|
require.NoError(t, err)
|
|
return key, loaded
|
|
}
|