diff --git a/stack/checkpoint_test.go b/stack/checkpoint_test.go new file mode 100644 index 000000000..bec2914d0 --- /dev/null +++ b/stack/checkpoint_test.go @@ -0,0 +1,97 @@ +package stack + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/state" +) + +func TestCheckpointer(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + good := writerHand{"foo", []byte{1, 2}, []byte("bar")} + bad := FailHandler{Err: errors.New("no go")} + + app := New( + Checkpoint{OnCheck: true}, + writerMid{"bing", []byte{1, 2}, []byte("bang")}, + Checkpoint{OnDeliver: true}, + ).Use( + NewDispatcher( + WrapHandler(good), + WrapHandler(bad), + )) + + basecoin.TxMapper.RegisterImplementation(RawTx{}, good.Name(), byte(80)) + + mid := state.Model{ + Key: []byte{'b', 'i', 'n', 'g', 0, 1, 2}, + Value: []byte("bang"), + } + end := state.Model{ + Key: []byte{'f', 'o', 'o', 0, 1, 2}, + Value: []byte("bar"), + } + + cases := []struct { + // tx to send down the line + tx basecoin.Tx + // expect no error? + valid bool + // models to check afterwards + toGetCheck []state.Model + // models to check afterwards + toGetDeliver []state.Model + }{ + // everything writen on success + { + tx: NewRawTx([]byte{45, 67}), + valid: true, + toGetCheck: []state.Model{mid, end}, + toGetDeliver: []state.Model{mid, end}, + }, + // mostly reverted on failure + { + tx: NewFailTx(), + valid: false, + toGetCheck: []state.Model{}, + toGetDeliver: []state.Model{mid}, + }, + } + + for i, tc := range cases { + ctx := NewContext("foo", 100, log.NewNopLogger()) + + store := state.NewMemKVStore() + _, err := app.CheckTx(ctx, store, tc.tx) + if tc.valid { + require.Nil(err, "%+v", err) + } else { + require.NotNil(err) + } + for _, m := range tc.toGetCheck { + val := store.Get(m.Key) + assert.EqualValues(m.Value, val, "%d: %#v", i, m) + } + + store = state.NewMemKVStore() + _, err = app.DeliverTx(ctx, store, tc.tx) + if tc.valid { + require.Nil(err, "%+v", err) + } else { + require.NotNil(err) + } + for _, m := range tc.toGetDeliver { + val := store.Get(m.Key) + assert.EqualValues(m.Value, val, "%d: %#v", i, m) + } + + } +} diff --git a/stack/helpers.go b/stack/helpers.go index eaee30575..b38e86bc6 100644 --- a/stack/helpers.go +++ b/stack/helpers.go @@ -20,9 +20,11 @@ const ( const ( ByteRawTx = 0xF0 ByteCheckTx = 0xF1 + ByteFailTx = 0xF2 TypeRawTx = NameOK + "/raw" // this will just say a-ok to RawTx TypeCheckTx = NameCheck + "/tx" + TypeFailTx = NameFail + "/tx" rawMaxSize = 2000 * 1000 ) @@ -30,7 +32,8 @@ const ( func init() { basecoin.TxMapper. RegisterImplementation(RawTx{}, TypeRawTx, ByteRawTx). - RegisterImplementation(CheckTx{}, TypeCheckTx, ByteCheckTx) + RegisterImplementation(CheckTx{}, TypeCheckTx, ByteCheckTx). + RegisterImplementation(FailTx{}, TypeFailTx, ByteFailTx) } // RawTx just contains bytes that can be hex-ified @@ -72,6 +75,22 @@ func (CheckTx) ValidateBasic() error { return nil } +// FailTx just gets routed to filaure +type FailTx struct{} + +var _ basecoin.TxInner = FailTx{} + +func NewFailTx() basecoin.Tx { + return FailTx{}.Wrap() +} + +func (f FailTx) Wrap() basecoin.Tx { + return basecoin.Tx{f} +} +func (r FailTx) ValidateBasic() error { + return nil +} + // OKHandler just used to return okay to everything type OKHandler struct { Log string diff --git a/stack/prefixstore.go b/stack/prefixstore.go index c12d14e55..5c6a4e7a0 100644 --- a/stack/prefixstore.go +++ b/stack/prefixstore.go @@ -83,7 +83,7 @@ func (p prefixStore) Commit(sub state.SimpleDB) error { } // commit the wrapped data, don't worry about the prefix here - p.store.Commit(ps) + p.store.Commit(ps.store) return nil }