189 lines
5.0 KiB
Go
189 lines
5.0 KiB
Go
package orm
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
"github.com/cosmos/cosmos-sdk/codec/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/stretchr/testify/require"
|
|
"pgregory.net/rapid"
|
|
|
|
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
|
)
|
|
|
|
func TestPrimaryKeyTable(t *testing.T) {
|
|
rapid.Check(t, rapid.Run(&primaryKeyMachine{}))
|
|
}
|
|
|
|
// primaryKeyMachine is a state machine model of the PrimaryKeyTable. The state
|
|
// is modelled as a map of strings to TableModels.
|
|
type primaryKeyMachine struct {
|
|
store sdk.KVStore
|
|
table *PrimaryKeyTable
|
|
state map[string]*testdata.TableModel
|
|
}
|
|
|
|
// stateKeys gets all the keys in the model map
|
|
func (m *primaryKeyMachine) stateKeys() []string {
|
|
keys := make([]string, len(m.state))
|
|
|
|
i := 0
|
|
for k := range m.state {
|
|
keys[i] = k
|
|
i++
|
|
}
|
|
|
|
return keys
|
|
}
|
|
|
|
// Generate a TableModel that has a 50% chance of being a part of the existing
|
|
// state
|
|
func (m *primaryKeyMachine) genTableModel() *rapid.Generator {
|
|
genStateTableModel := rapid.Custom(func(t *rapid.T) *testdata.TableModel {
|
|
pk := rapid.SampledFrom(m.stateKeys()).Draw(t, "key").(string)
|
|
return m.state[pk]
|
|
})
|
|
|
|
if len(m.stateKeys()) == 0 {
|
|
return genTableModel
|
|
} else {
|
|
return rapid.OneOf(genTableModel, genStateTableModel)
|
|
}
|
|
}
|
|
|
|
// Init creates a new instance of the state machine model by building the real
|
|
// table and making the empty model map
|
|
func (m *primaryKeyMachine) Init(t *rapid.T) {
|
|
// Create context
|
|
ctx := NewMockContext()
|
|
m.store = ctx.KVStore(sdk.NewKVStoreKey("test"))
|
|
|
|
// Create primary key table
|
|
interfaceRegistry := types.NewInterfaceRegistry()
|
|
cdc := codec.NewProtoCodec(interfaceRegistry)
|
|
table, err := NewPrimaryKeyTable(
|
|
[2]byte{0x1},
|
|
&testdata.TableModel{},
|
|
cdc,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
m.table = table
|
|
|
|
// Create model state
|
|
m.state = make(map[string]*testdata.TableModel)
|
|
}
|
|
|
|
// Check that the real values match the state values.
|
|
func (m *primaryKeyMachine) Check(t *rapid.T) {
|
|
for i := range m.state {
|
|
has := m.table.Has(m.store, []byte(i))
|
|
require.Equal(t, true, has)
|
|
}
|
|
}
|
|
|
|
// Create is one of the model commands. It adds an object to the table, creating
|
|
// an error if it already exists.
|
|
func (m *primaryKeyMachine) Create(t *rapid.T) {
|
|
g := genTableModel.Draw(t, "g").(*testdata.TableModel)
|
|
pk := string(PrimaryKey(g))
|
|
|
|
t.Logf("pk: %v", pk)
|
|
t.Logf("m.state: %v", m.state)
|
|
|
|
err := m.table.Create(m.store, g)
|
|
|
|
if m.state[pk] != nil {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
m.state[pk] = g
|
|
}
|
|
}
|
|
|
|
// Update is one of the model commands. It updates the value at a given primary
|
|
// key and fails if that primary key doesn't already exist in the table.
|
|
func (m *primaryKeyMachine) Update(t *rapid.T) {
|
|
tm := m.genTableModel().Draw(t, "tm").(*testdata.TableModel)
|
|
|
|
newName := rapid.StringN(1, 100, 150).Draw(t, "newName").(string)
|
|
tm.Name = newName
|
|
|
|
// Perform the real Update
|
|
err := m.table.Update(m.store, tm)
|
|
|
|
if m.state[string(PrimaryKey(tm))] == nil {
|
|
// If there's no value in the model, we expect an error
|
|
require.Error(t, err)
|
|
} else {
|
|
// If we have a value in the model, expect no error
|
|
require.NoError(t, err)
|
|
|
|
// Update the model with the new value
|
|
m.state[string(PrimaryKey(tm))] = tm
|
|
}
|
|
}
|
|
|
|
// Set is one of the model commands. It sets the value at a key in the table
|
|
// whether it exists or not.
|
|
func (m *primaryKeyMachine) Set(t *rapid.T) {
|
|
g := genTableModel.Draw(t, "g").(*testdata.TableModel)
|
|
pk := string(PrimaryKey(g))
|
|
|
|
err := m.table.Set(m.store, g)
|
|
|
|
require.NoError(t, err)
|
|
m.state[pk] = g
|
|
}
|
|
|
|
// Delete is one of the model commands. It removes the object with the given
|
|
// primary key from the table and returns an error if that primary key doesn't
|
|
// already exist in the table.
|
|
func (m *primaryKeyMachine) Delete(t *rapid.T) {
|
|
tm := m.genTableModel().Draw(t, "tm").(*testdata.TableModel)
|
|
|
|
// Perform the real Delete
|
|
err := m.table.Delete(m.store, tm)
|
|
|
|
if m.state[string(PrimaryKey(tm))] == nil {
|
|
// If there's no value in the model, we expect an error
|
|
require.Error(t, err)
|
|
} else {
|
|
// If we have a value in the model, expect no error
|
|
require.NoError(t, err)
|
|
|
|
// Delete the value from the model
|
|
delete(m.state, string(PrimaryKey(tm)))
|
|
}
|
|
}
|
|
|
|
// Has is one of the model commands. It checks whether a key already exists in
|
|
// the table.
|
|
func (m *primaryKeyMachine) Has(t *rapid.T) {
|
|
pk := PrimaryKey(m.genTableModel().Draw(t, "g").(*testdata.TableModel))
|
|
|
|
realHas := m.table.Has(m.store, pk)
|
|
modelHas := m.state[string(pk)] != nil
|
|
|
|
require.Equal(t, realHas, modelHas)
|
|
}
|
|
|
|
// GetOne is one of the model commands. It fetches an object from the table by
|
|
// its primary key and returns an error if that primary key isn't in the table.
|
|
func (m *primaryKeyMachine) GetOne(t *rapid.T) {
|
|
pk := PrimaryKey(m.genTableModel().Draw(t, "tm").(*testdata.TableModel))
|
|
|
|
var tm testdata.TableModel
|
|
|
|
err := m.table.GetOne(m.store, pk, &tm)
|
|
t.Logf("tm: %v", tm)
|
|
|
|
if m.state[string(pk)] == nil {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, *m.state[string(pk)], tm)
|
|
}
|
|
}
|