From 1f31fbeea89275ea5b75ba65bb3de2ad240b715f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 16:04:54 +0100 Subject: [PATCH] Start writing test scaffold to test server --- mock/app.go | 120 +++++++++++++++++++++++++++++++++++++++++++ mock/helpers.go | 27 ++++++++++ mock/tx.go | 76 +++++++++++++++++++++++++++ server/init.go | 4 -- server/init_test.go | 7 +++ server/start_test.go | 15 ++++++ 6 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 mock/app.go create mode 100644 mock/helpers.go create mode 100644 mock/tx.go create mode 100644 server/init_test.go create mode 100644 server/start_test.go diff --git a/mock/app.go b/mock/app.go new file mode 100644 index 0000000000..554879f042 --- /dev/null +++ b/mock/app.go @@ -0,0 +1,120 @@ +package mock + +import ( + "encoding/json" + "fmt" + + abci "github.com/tendermint/abci/types" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" + + bam "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewApp creates a simple mock kvstore app for testing. +// It should work similar to a real app. +// Make sure rootDir is empty before running the test, +// in order to guarantee consistent results +func NewApp(logger log.Logger, rootDir string) (abci.Application, error) { + db, err := dbm.NewGoLevelDB("mock", rootDir) + if err != nil { + return nil, err + } + + // Capabilities key to access the main KVStore. + capKeyMainStore := sdk.NewKVStoreKey("main") + + // Create BaseApp. + baseApp := bam.NewBaseApp("kvstore", logger, db) + + // Set mounts for BaseApp's MultiStore. + baseApp.MountStoresIAVL(capKeyMainStore) + + // Set Tx decoder + baseApp.SetTxDecoder(decodeTx) + + baseApp.SetInitChainer(InitChainer(capKeyMainStore)) + + // Set a handler Route. + baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore)) + + // Load latest version. + if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil { + return nil, err + } + + return baseApp, nil +} + +// KVStoreHandler is a simple handler that takes kvstoreTx and writes +// them to the db +func KVStoreHandler(storeKey sdk.StoreKey) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + dTx, ok := msg.(kvstoreTx) + if !ok { + panic("KVStoreHandler should only receive kvstoreTx") + } + + // tx is already unmarshalled + key := dTx.key + value := dTx.value + + store := ctx.KVStore(storeKey) + store.Set(key, value) + + return sdk.Result{ + Code: 0, + Log: fmt.Sprintf("set %s=%s", key, value), + } + } +} + +type KV struct { + Key string `json:"key"` + Value string `json:"value"` +} + +type GenesisJSON struct { + Values []KV `json:"values"` +} + +// InitChainer returns a function that can initialize the chain +// with key/value pairs +func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain { + return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + stateJSON := req.AppStateBytes + + genesisState := new(GenesisJSON) + err := json.Unmarshal(stateJSON, genesisState) + if err != nil { + panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 + // return sdk.ErrGenesisParse("").TraceCause(err, "") + } + + for _, val := range genesisState.Values { + store := ctx.KVStore(key) + store.Set([]byte(val.Key), []byte(val.Value)) + } + return abci.ResponseInitChain{} + } +} + +// GenInitOptions can be passed into InitCmd, +// returns a static string of a few key-values that can be parsed +// by InitChainer +func GenInitOptions(args []string) (json.RawMessage, error) { + opts := []byte(`{ + "values": [ + { + "key": "hello", + "value": "goodbye" + }, + { + "key": "foo", + "value": "bar" + } + ] +}`) + return opts, nil +} diff --git a/mock/helpers.go b/mock/helpers.go new file mode 100644 index 0000000000..4b6f70ba75 --- /dev/null +++ b/mock/helpers.go @@ -0,0 +1,27 @@ +package mock + +import ( + "io/ioutil" + "os" + + abci "github.com/tendermint/abci/types" + "github.com/tendermint/tmlibs/log" +) + +// SetupApp returns an application as well as a clean-up function +// to be used to quickly setup a test case with an app +func SetupApp() (abci.Application, func(), error) { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). + With("module", "mock") + rootDir, err := ioutil.TempDir("mock-sdk", "") + if err != nil { + return nil, nil, err + } + + cleanup := func() { + os.RemoveAll(rootDir) + } + + app, err := NewApp(logger, rootDir) + return app, cleanup, err +} diff --git a/mock/tx.go b/mock/tx.go new file mode 100644 index 0000000000..077c090e60 --- /dev/null +++ b/mock/tx.go @@ -0,0 +1,76 @@ +package mock + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + crypto "github.com/tendermint/go-crypto" +) + +// An sdk.Tx which is its own sdk.Msg. +type kvstoreTx struct { + key []byte + value []byte + bytes []byte +} + +func (tx kvstoreTx) Get(key interface{}) (value interface{}) { + switch k := key.(type) { + case string: + switch k { + case "key": + return tx.key + case "value": + return tx.value + } + } + return nil +} + +func (tx kvstoreTx) Type() string { + return "kvstore" +} + +func (tx kvstoreTx) GetMsg() sdk.Msg { + return tx +} + +func (tx kvstoreTx) GetSignBytes() []byte { + return tx.bytes +} + +// Should the app be calling this? Or only handlers? +func (tx kvstoreTx) ValidateBasic() sdk.Error { + return nil +} + +func (tx kvstoreTx) GetSigners() []crypto.Address { + return nil +} + +func (tx kvstoreTx) GetSignatures() []sdk.StdSignature { + return nil +} + +func (tx kvstoreTx) GetFeePayer() crypto.Address { + return nil +} + +// takes raw transaction bytes and decodes them into an sdk.Tx. An sdk.Tx has +// all the signatures and can be used to authenticate. +func decodeTx(txBytes []byte) (sdk.Tx, sdk.Error) { + var tx sdk.Tx + + split := bytes.Split(txBytes, []byte("=")) + if len(split) == 1 { + k := split[0] + tx = kvstoreTx{k, k, txBytes} + } else if len(split) == 2 { + k, v := split[0], split[1] + tx = kvstoreTx{k, v, txBytes} + } else { + return nil, sdk.ErrTxParse("too many =") + } + + return tx, nil +} diff --git a/server/init.go b/server/init.go index 9006149c6e..b8071b0e56 100644 --- a/server/init.go +++ b/server/init.go @@ -62,10 +62,6 @@ func GenerateCoinKey() (crypto.Address, string, error) { return nil, "", err } - // debug - bz, err := json.Marshal(info.PubKey) - fmt.Printf("PubKey: %s\n", string(bz)) - addr := info.PubKey.Address() return addr, secret, nil } diff --git a/server/init_test.go b/server/init_test.go new file mode 100644 index 0000000000..3d7e5671d5 --- /dev/null +++ b/server/init_test.go @@ -0,0 +1,7 @@ +package server + +import "testing" + +func TestInit(t *testing.T) { + +} diff --git a/server/start_test.go b/server/start_test.go new file mode 100644 index 0000000000..10c012ecf6 --- /dev/null +++ b/server/start_test.go @@ -0,0 +1,15 @@ +package server + +import ( + "testing" +) + +func TestStart(t *testing.T) { + // app, cleanup, err := mock.SetupApp() + // if cleanup != nil { + // defer cleanup() + // } + + // require.NoError(t, err) + // // TODO: init and start +}