From 3cb63349b2aca6a81b02c92113def911880f291d Mon Sep 17 00:00:00 2001 From: rhuairahrighairigh Date: Tue, 25 Jun 2019 13:01:02 +0100 Subject: [PATCH 1/5] add gaia cli tests --- cli_test/README.md | 51 ++ cli_test/cli_test.go | 1093 ++++++++++++++++++++++++++++++++++++++ cli_test/doc.go | 3 + cli_test/test_helpers.go | 685 ++++++++++++++++++++++++ 4 files changed, 1832 insertions(+) create mode 100644 cli_test/README.md create mode 100644 cli_test/cli_test.go create mode 100644 cli_test/doc.go create mode 100644 cli_test/test_helpers.go diff --git a/cli_test/README.md b/cli_test/README.md new file mode 100644 index 00000000..37fd41ce --- /dev/null +++ b/cli_test/README.md @@ -0,0 +1,51 @@ +# Gaia CLI Integration tests + +The gaia cli integration tests live in this folder. You can run the full suite by running: + +```bash +$ go test -v -p 4 ./cmd/gaia/cli_test/... +# OR! +$ make test_cli +``` +> NOTE: While the full suite runs in parallel, some of the tests can take up to a minute to complete + +### Test Structure + +This integration suite [uses a thin wrapper](https://godoc.org/github.com/cosmos/cosmos-sdk/tests) over the [`os/exec`](https://golang.org/pkg/os/exec/) package. This allows the integration test to run against built binaries (both `gaiad` and `gaiacli` are used) while being written in golang. This allows tests to take advantage of the various golang code we have for operations like marshal/unmarshal, crypto, etc... + +> NOTE: The tests will use whatever `gaiad` or `gaiacli` binaries are available in your `$PATH`. You can check which binary will be run by the suite by running `which gaiad` or `which gaiacli`. If you have your `$GOPATH` properly setup they should be in `$GOPATH/bin/gaia*`. This will ensure that your test uses the latest binary you have built + +Tests generally follow this structure: + +```go +func TestMyNewCommand(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + // Your test code goes here... + + f.Cleanup() +} +``` + +This boilerplate above: +- Ensures the tests run in parallel. Because the tests are calling out to `os/exec` for many operations these tests can take a long time to run. +- Creates `.gaiad` and `.gaiacli` folders in a new temp folder. +- Uses `gaiacli` to create 2 accounts for use in testing: `foo` and `bar` +- Creates a genesis file with coins (`1000footoken,1000feetoken,150stake`) controlled by the `foo` key +- Generates an initial bonding transaction (`gentx`) to make the `foo` key a validator at genesis +- Starts `gaiad` and stops it once the test exits +- Cleans up test state on a successful run + +### Notes when adding/running tests + +- Because the tests run against a built binary, you should make sure you build every time the code changes and you want to test again, otherwise you will be testing against an older version. If you are adding new tests this can easily lead to confusing test results. +- The [`test_helpers.go`](./test_helpers.go) file is organized according to the format of `gaiacli` and `gaiad` commands. There are comments with section headers describing the different areas. Helper functions to call CLI functionality are generally named after the command (e.g. `gaiacli query staking validator` would be `QueryStakingValidator`). Try to keep functions grouped by their position in the command tree. +- Test state that is needed by `tx` and `query` commands (`home`, `chain_id`, etc...) is stored on the `Fixtures` object. This makes constructing your new tests almost trivial. +- Sometimes if you exit a test early there can be still running `gaiad` and `gaiacli` processes that will interrupt subsequent runs. Still running `gaiacli` processes will block access to the keybase while still running `gaiad` processes will block ports and prevent new tests from spinning up. You can ensure new tests spin up clean by running `pkill -9 gaiad && pkill -9 gaiacli` before each test run. +- Most `query` and `tx` commands take a variadic `flags` argument. This pattern allows for the creation of a general function which is easily modified by adding flags. See the `TxSend` function and its use for a good example. +- `Tx*` functions follow a general pattern and return `(success bool, stdout string, stderr string)`. This allows for easy testing of multiple different flag configurations. See `TestGaiaCLICreateValidator` or `TestGaiaCLISubmitProposal` for a good example of the pattern. diff --git a/cli_test/cli_test.go b/cli_test/cli_test.go new file mode 100644 index 00000000..8a36a390 --- /dev/null +++ b/cli_test/cli_test.go @@ -0,0 +1,1093 @@ +// +build cli_test + +package clitest + +import ( + "encoding/base64" + "errors" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/tests" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/gov" +) + +func TestGaiaCLIKeysAddMultisig(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // key names order does not matter + f.KeysAdd("msig1", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBar, keyBaz)) + f.KeysAdd("msig2", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBaz, keyBar)) + require.Equal(t, f.KeysShow("msig1").Address, f.KeysShow("msig2").Address) + + f.KeysAdd("msig3", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBar, keyBaz), + "--nosort") + f.KeysAdd("msig4", "--multisig-threshold=2", + fmt.Sprintf("--multisig=%s,%s", keyBaz, keyBar), + "--nosort") + require.NotEqual(t, f.KeysShow("msig3").Address, f.KeysShow("msig4").Address) +} + +func TestGaiaCLIKeysAddRecover(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + exitSuccess, _, _ := f.KeysAddRecover("empty-mnemonic", "") + require.False(t, exitSuccess) + + exitSuccess, _, _ = f.KeysAddRecover("test-recover", "dentist task convince chimney quality leave banana trade firm crawl eternal easily") + require.True(t, exitSuccess) + require.Equal(t, "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4", f.KeyAddress("test-recover").String()) +} + +func TestGaiaCLIKeysAddRecoverHDPath(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + f.KeysAddRecoverHDPath("test-recoverHD1", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 0, 0) + require.Equal(t, "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4", f.KeyAddress("test-recoverHD1").String()) + + f.KeysAddRecoverHDPath("test-recoverH2", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 5) + require.Equal(t, "cosmos1pdfav2cjhry9k79nu6r8kgknnjtq6a7rykmafy", f.KeyAddress("test-recoverH2").String()) + + f.KeysAddRecoverHDPath("test-recoverH3", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 17) + require.Equal(t, "cosmos1909k354n6wl8ujzu6kmh49w4d02ax7qvlkv4sn", f.KeyAddress("test-recoverH3").String()) + + f.KeysAddRecoverHDPath("test-recoverH4", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 2, 17) + require.Equal(t, "cosmos1v9plmhvyhgxk3th9ydacm7j4z357s3nhtwsjat", f.KeyAddress("test-recoverH4").String()) +} + +func TestGaiaCLIMinimumFees(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server with minimum fees + minGasPrice, _ := sdk.NewDecFromStr("0.000006") + fees := fmt.Sprintf( + "--minimum-gas-prices=%s,%s", + sdk.NewDecCoinFromDec(feeDenom, minGasPrice), + sdk.NewDecCoinFromDec(fee2Denom, minGasPrice), + ) + proc := f.GDStart(fees) + defer proc.Stop(false) + + barAddr := f.KeyAddress(keyBar) + + // Send a transaction that will get rejected + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), "-y") + require.False(f.T, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure tx w/ correct fees pass + txFees := fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)) + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fee2Denom, 10), txFees, "-y") + require.True(f.T, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure tx w/ improper fees fails + txFees = fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 1)) + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10), txFees, "-y") + require.False(f.T, success) + + // Cleanup testing directories + f.Cleanup() +} + +func TestGaiaCLIGasPrices(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server with minimum fees + minGasPrice, _ := sdk.NewDecFromStr("0.000006") + proc := f.GDStart(fmt.Sprintf("--minimum-gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice))) + defer proc.Stop(false) + + barAddr := f.KeyAddress(keyBar) + + // insufficient gas prices (tx fails) + badGasPrice, _ := sdk.NewDecFromStr("0.000003") + success, _, _ := f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 50), + fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, badGasPrice)), "-y") + require.False(t, success) + + // wait for a block confirmation + tests.WaitForNextNBlocksTM(1, f.Port) + + // sufficient gas prices (tx passes) + success, _, _ = f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 50), + fmt.Sprintf("--gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)), "-y") + require.True(t, success) + + // wait for a block confirmation + tests.WaitForNextNBlocksTM(1, f.Port) + + f.Cleanup() +} + +func TestGaiaCLIFeesDeduction(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server with minimum fees + minGasPrice, _ := sdk.NewDecFromStr("0.000006") + proc := f.GDStart(fmt.Sprintf("--minimum-gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice))) + defer proc.Stop(false) + + // Save key addresses for later use + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + fooAcc := f.QueryAccount(fooAddr) + fooAmt := fooAcc.GetCoins().AmountOf(fooDenom) + + // test simulation + success, _, _ := f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 1000), + fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)), "--dry-run") + require.True(t, success) + + // Wait for a block + tests.WaitForNextNBlocksTM(1, f.Port) + + // ensure state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, fooAmt.Int64(), fooAcc.GetCoins().AmountOf(fooDenom).Int64()) + + // insufficient funds (coins + fees) tx fails + largeCoins := sdk.TokensFromTendermintPower(10000000) + success, _, _ = f.TxSend( + keyFoo, barAddr, sdk.NewCoin(fooDenom, largeCoins), + fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)), "-y") + require.False(t, success) + + // Wait for a block + tests.WaitForNextNBlocksTM(1, f.Port) + + // ensure state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, fooAmt.Int64(), fooAcc.GetCoins().AmountOf(fooDenom).Int64()) + + // test success (transfer = coins + fees) + success, _, _ = f.TxSend( + keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 500), + fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2)), "-y") + require.True(t, success) + + f.Cleanup() +} + +func TestGaiaCLISend(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + // Save key addresses for later use + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Send some tokens from one account to the other + sendTokens := sdk.TokensFromTendermintPower(10) + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + barAcc := f.QueryAccount(barAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) + + // Test --dry-run + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--dry-run") + require.True(t, success) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) + + // test autosequencing + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + barAcc = f.QueryAccount(barAddr) + require.Equal(t, sendTokens.MulRaw(2), barAcc.GetCoins().AmountOf(denom)) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens.MulRaw(2)), fooAcc.GetCoins().AmountOf(denom)) + + // test memo + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--memo='testmemo'", "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + barAcc = f.QueryAccount(barAddr) + require.Equal(t, sendTokens.MulRaw(3), barAcc.GetCoins().AmountOf(denom)) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens.MulRaw(3)), fooAcc.GetCoins().AmountOf(denom)) + + f.Cleanup() +} + +func TestGaiaCLIConfirmTx(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + // Save key addresses for later use + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // send some tokens from one account to the other + sendTokens := sdk.TokensFromTendermintPower(10) + f.txSendWithConfirm(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "Y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // ensure account balances match expected + barAcc := f.QueryAccount(barAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + + // send some tokens from one account to the other (cancelling confirmation) + f.txSendWithConfirm(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "n") + tests.WaitForNextNBlocksTM(1, f.Port) + + // ensure account balances match expected + barAcc = f.QueryAccount(barAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) +} + +func TestGaiaCLIGasAuto(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Test failure with auto gas disabled and very little gas set by hand + sendTokens := sdk.TokensFromTendermintPower(10) + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=10", "-y") + require.False(t, success) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Test failure with negative gas + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=-100", "-y") + require.False(t, success) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Test failure with 0 gas + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=0", "-y") + require.False(t, success) + + // Check state didn't change + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Enable auto gas + success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "-y") + require.NotEmpty(t, stderr) + require.True(t, success) + cdc := app.MakeCodec() + sendResp := sdk.TxResponse{} + err := cdc.UnmarshalJSON([]byte(stdout), &sendResp) + require.Nil(t, err) + require.True(t, sendResp.GasWanted >= sendResp.GasUsed) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Check state has changed accordingly + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) + + f.Cleanup() +} + +func TestGaiaCLICreateValidator(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + barAddr := f.KeyAddress(keyBar) + barVal := sdk.ValAddress(barAddr) + + consPubKey := sdk.MustBech32ifyConsPub(ed25519.GenPrivKey().PubKey()) + + sendTokens := sdk.TokensFromTendermintPower(10) + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + barAcc := f.QueryAccount(barAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + + // Generate a create validator transaction and ensure correctness + success, stdout, stderr := f.TxStakingCreateValidator(barAddr.String(), consPubKey, sdk.NewInt64Coin(denom, 2), "--generate-only") + + require.True(f.T, success) + require.Empty(f.T, stderr) + msg := unmarshalStdTx(f.T, stdout) + require.NotZero(t, msg.Fee.Gas) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Test --dry-run + newValTokens := sdk.TokensFromTendermintPower(2) + success, _, _ = f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens), "--dry-run") + require.True(t, success) + + // Create the validator + f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure funds were deducted properly + barAcc = f.QueryAccount(barAddr) + require.Equal(t, sendTokens.Sub(newValTokens), barAcc.GetCoins().AmountOf(denom)) + + // Ensure that validator state is as expected + validator := f.QueryStakingValidator(barVal) + require.Equal(t, validator.OperatorAddress, barVal) + require.True(sdk.IntEq(t, newValTokens, validator.Tokens)) + + // Query delegations to the validator + validatorDelegations := f.QueryStakingDelegationsTo(barVal) + require.Len(t, validatorDelegations, 1) + require.NotZero(t, validatorDelegations[0].Shares) + + // unbond a single share + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(1)) + success = f.TxStakingUnbond(keyBar, unbondAmt.String(), barVal, "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure bonded staking is correct + remainingTokens := newValTokens.Sub(unbondAmt.Amount) + validator = f.QueryStakingValidator(barVal) + require.Equal(t, remainingTokens, validator.Tokens) + + // Get unbonding delegations from the validator + validatorUbds := f.QueryStakingUnbondingDelegationsFrom(barVal) + require.Len(t, validatorUbds, 1) + require.Len(t, validatorUbds[0].Entries, 1) + require.Equal(t, remainingTokens.String(), validatorUbds[0].Entries[0].Balance.String()) + + f.Cleanup() +} + +func TestGaiaCLISubmitProposal(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + f.QueryGovParamDeposit() + f.QueryGovParamVoting() + f.QueryGovParamTallying() + + fooAddr := f.KeyAddress(keyFoo) + + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(sdk.DefaultBondDenom)) + + proposalsQuery := f.QueryGovProposals() + require.Empty(t, proposalsQuery) + + // Test submit generate only for submit proposal + proposalTokens := sdk.TokensFromTendermintPower(5) + success, stdout, stderr := f.TxGovSubmitProposal( + fooAddr.String(), "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--generate-only", "-y") + require.True(t, success) + require.Empty(t, stderr) + msg := unmarshalStdTx(t, stdout) + require.NotZero(t, msg.Fee.Gas) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Test --dry-run + success, _, _ = f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--dry-run") + require.True(t, success) + + // Create the proposal + f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure transaction tags can be queried + txs := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("proposer:%s", fooAddr)) + require.Len(t, txs, 1) + + // Ensure deposit was deducted + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(proposalTokens), fooAcc.GetCoins().AmountOf(denom)) + + // Ensure propsal is directly queryable + proposal1 := f.QueryGovProposal(1) + require.Equal(t, uint64(1), proposal1.ProposalID) + require.Equal(t, gov.StatusDepositPeriod, proposal1.Status) + + // Ensure query proposals returns properly + proposalsQuery = f.QueryGovProposals() + require.Equal(t, uint64(1), proposalsQuery[0].ProposalID) + + // Query the deposits on the proposal + deposit := f.QueryGovDeposit(1, fooAddr) + require.Equal(t, proposalTokens, deposit.Amount.AmountOf(denom)) + + // Test deposit generate only + depositTokens := sdk.TokensFromTendermintPower(10) + success, stdout, stderr = f.TxGovDeposit(1, fooAddr.String(), sdk.NewCoin(denom, depositTokens), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + msg = unmarshalStdTx(t, stdout) + require.NotZero(t, msg.Fee.Gas) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Run the deposit transaction + f.TxGovDeposit(1, keyFoo, sdk.NewCoin(denom, depositTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // test query deposit + deposits := f.QueryGovDeposits(1) + require.Len(t, deposits, 1) + require.Equal(t, proposalTokens.Add(depositTokens), deposits[0].Amount.AmountOf(denom)) + + // Ensure querying the deposit returns the proper amount + deposit = f.QueryGovDeposit(1, fooAddr) + require.Equal(t, proposalTokens.Add(depositTokens), deposit.Amount.AmountOf(denom)) + + // Ensure tags are set on the transaction + txs = f.QueryTxs(1, 50, "action:deposit", fmt.Sprintf("depositor:%s", fooAddr)) + require.Len(t, txs, 1) + + // Ensure account has expected amount of funds + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, startTokens.Sub(proposalTokens.Add(depositTokens)), fooAcc.GetCoins().AmountOf(denom)) + + // Fetch the proposal and ensure it is now in the voting period + proposal1 = f.QueryGovProposal(1) + require.Equal(t, uint64(1), proposal1.ProposalID) + require.Equal(t, gov.StatusVotingPeriod, proposal1.Status) + + // Test vote generate only + success, stdout, stderr = f.TxGovVote(1, gov.OptionYes, fooAddr.String(), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + msg = unmarshalStdTx(t, stdout) + require.NotZero(t, msg.Fee.Gas) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Vote on the proposal + f.TxGovVote(1, gov.OptionYes, keyFoo, "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Query the vote + vote := f.QueryGovVote(1, fooAddr) + require.Equal(t, uint64(1), vote.ProposalID) + require.Equal(t, gov.OptionYes, vote.Option) + + // Query the votes + votes := f.QueryGovVotes(1) + require.Len(t, votes, 1) + require.Equal(t, uint64(1), votes[0].ProposalID) + require.Equal(t, gov.OptionYes, votes[0].Option) + + // Ensure tags are applied to voting transaction properly + txs = f.QueryTxs(1, 50, "action:vote", fmt.Sprintf("voter:%s", fooAddr)) + require.Len(t, txs, 1) + + // Ensure no proposals in deposit period + proposalsQuery = f.QueryGovProposals("--status=DepositPeriod") + require.Empty(t, proposalsQuery) + + // Ensure the proposal returns as in the voting period + proposalsQuery = f.QueryGovProposals("--status=VotingPeriod") + require.Equal(t, uint64(1), proposalsQuery[0].ProposalID) + + // submit a second test proposal + f.TxGovSubmitProposal(keyFoo, "Text", "Apples", "test", sdk.NewCoin(denom, proposalTokens), "-y") + tests.WaitForNextNBlocksTM(1, f.Port) + + // Test limit on proposals query + proposalsQuery = f.QueryGovProposals("--limit=1") + require.Equal(t, uint64(2), proposalsQuery[0].ProposalID) + + f.Cleanup() +} + +func TestGaiaCLIQueryTxPagination(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + accFoo := f.QueryAccount(fooAddr) + seq := accFoo.GetSequence() + + for i := 1; i <= 30; i++ { + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, int64(i)), fmt.Sprintf("--sequence=%d", seq), "-y") + require.True(t, success) + seq++ + } + + // perPage = 15, 2 pages + txsPage1 := f.QueryTxs(1, 15, fmt.Sprintf("sender:%s", fooAddr)) + require.Len(t, txsPage1, 15) + txsPage2 := f.QueryTxs(2, 15, fmt.Sprintf("sender:%s", fooAddr)) + require.Len(t, txsPage2, 15) + require.NotEqual(t, txsPage1, txsPage2) + txsPage3 := f.QueryTxs(3, 15, fmt.Sprintf("sender:%s", fooAddr)) + require.Len(t, txsPage3, 15) + require.Equal(t, txsPage2, txsPage3) + + // perPage = 16, 2 pages + txsPage1 = f.QueryTxs(1, 16, fmt.Sprintf("sender:%s", fooAddr)) + require.Len(t, txsPage1, 16) + txsPage2 = f.QueryTxs(2, 16, fmt.Sprintf("sender:%s", fooAddr)) + require.Len(t, txsPage2, 14) + require.NotEqual(t, txsPage1, txsPage2) + + // perPage = 50 + txsPageFull := f.QueryTxs(1, 50, fmt.Sprintf("sender:%s", fooAddr)) + require.Len(t, txsPageFull, 30) + require.Equal(t, txsPageFull, append(txsPage1, txsPage2...)) + + // perPage = 0 + f.QueryTxsInvalid(errors.New("ERROR: page must greater than 0"), 0, 50, fmt.Sprintf("sender:%s", fooAddr)) + + // limit = 0 + f.QueryTxsInvalid(errors.New("ERROR: limit must greater than 0"), 1, 0, fmt.Sprintf("sender:%s", fooAddr)) +} + +func TestGaiaCLIValidateSignatures(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + // generate sendTx with default gas + success, stdout, stderr := f.TxSend(fooAddr.String(), barAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + + // write unsigned tx to file + unsignedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(unsignedTxFile.Name()) + + // validate we can successfully sign + success, stdout, stderr = f.TxSign(keyFoo, unsignedTxFile.Name()) + require.True(t, success) + require.Empty(t, stderr) + stdTx := unmarshalStdTx(t, stdout) + require.Equal(t, len(stdTx.Msgs), 1) + require.Equal(t, 1, len(stdTx.GetSignatures())) + require.Equal(t, fooAddr.String(), stdTx.GetSigners()[0].String()) + + // write signed tx to file + signedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(signedTxFile.Name()) + + // validate signatures + success, _, _ = f.TxSign(keyFoo, signedTxFile.Name(), "--validate-signatures") + require.True(t, success) + + // modify the transaction + stdTx.Memo = "MODIFIED-ORIGINAL-TX-BAD" + bz := marshalStdTx(t, stdTx) + modSignedTxFile := WriteToNewTempFile(t, string(bz)) + defer os.Remove(modSignedTxFile.Name()) + + // validate signature validation failure due to different transaction sig bytes + success, _, _ = f.TxSign(keyFoo, modSignedTxFile.Name(), "--validate-signatures") + require.False(t, success) + + f.Cleanup() +} + +func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + fooAddr := f.KeyAddress(keyFoo) + barAddr := f.KeyAddress(keyBar) + + // Test generate sendTx with default gas + sendTokens := sdk.TokensFromTendermintPower(10) + success, stdout, stderr := f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + msg := unmarshalStdTx(t, stdout) + require.Equal(t, msg.Fee.Gas, uint64(client.DefaultGasLimit)) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Test generate sendTx with --gas=$amount + success, stdout, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--gas=100", "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + msg = unmarshalStdTx(t, stdout) + require.Equal(t, msg.Fee.Gas, uint64(100)) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 0, len(msg.GetSignatures())) + + // Test generate sendTx, estimate gas + success, stdout, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "--generate-only") + require.True(t, success) + require.NotEmpty(t, stderr) + msg = unmarshalStdTx(t, stdout) + require.True(t, msg.Fee.Gas > 0) + require.Equal(t, len(msg.Msgs), 1) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(unsignedTxFile.Name()) + + // Test sign --validate-signatures + success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--validate-signatures") + require.False(t, success) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout) + + // Test sign + success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name()) + require.True(t, success) + msg = unmarshalStdTx(t, stdout) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 1, len(msg.GetSignatures())) + require.Equal(t, fooAddr.String(), msg.GetSigners()[0].String()) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(signedTxFile.Name()) + + // Test sign --validate-signatures + success, stdout, _ = f.TxSign(keyFoo, signedTxFile.Name(), "--validate-signatures") + require.True(t, success) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t\t\t[OK]\n\n", fooAddr.String(), + fooAddr.String()), stdout) + + // Ensure foo has right amount of funds + fooAcc := f.QueryAccount(fooAddr) + startTokens := sdk.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) + + // Test broadcast + success, stdout, _ = f.TxBroadcast(signedTxFile.Name()) + require.True(t, success) + + var result sdk.TxResponse + + // Unmarshal the response and ensure that gas was properly used + require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result)) + require.Equal(t, msg.Fee.Gas, uint64(result.GasUsed)) + require.Equal(t, msg.Fee.Gas, uint64(result.GasWanted)) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account state + barAcc := f.QueryAccount(barAddr) + fooAcc = f.QueryAccount(fooAddr) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) + + f.Cleanup() +} + +func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server with minimum fees + proc := f.GDStart() + defer proc.Stop(false) + + fooBarBazAddr := f.KeyAddress(keyFooBarBaz) + barAddr := f.KeyAddress(keyBar) + + // Send some tokens from one account to the other + success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Test generate sendTx with multisig + success, stdout, _ := f.TxSend(fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") + require.True(t, success) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(unsignedTxFile.Name()) + + // Sign with foo's key + success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y") + require.True(t, success) + + // Write the output to disk + fooSignatureFile := WriteToNewTempFile(t, stdout) + defer os.Remove(fooSignatureFile.Name()) + + // Multisign, not enough signatures + success, stdout, _ = f.TxMultisign(unsignedTxFile.Name(), keyFooBarBaz, []string{fooSignatureFile.Name()}) + require.True(t, success) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(signedTxFile.Name()) + + // Validate the multisignature + success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures") + require.False(t, success) + + // Broadcast the transaction + success, _, _ = f.TxBroadcast(signedTxFile.Name()) + require.False(t, success) +} + +func TestGaiaCLIEncode(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + cdc := app.MakeCodec() + + // Build a testing transaction and write it to disk + barAddr := f.KeyAddress(keyBar) + keyAddr := f.KeyAddress(keyFoo) + + sendTokens := sdk.TokensFromTendermintPower(10) + success, stdout, stderr := f.TxSend(keyAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only", "--memo", "deadbeef") + require.True(t, success) + require.Empty(t, stderr) + + // Write it to disk + jsonTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(jsonTxFile.Name()) + + // Run the encode command, and trim the extras from the stdout capture + success, base64Encoded, _ := f.TxEncode(jsonTxFile.Name()) + require.True(t, success) + trimmedBase64 := strings.Trim(base64Encoded, "\"\n") + + // Decode the base64 + decodedBytes, err := base64.StdEncoding.DecodeString(trimmedBase64) + require.Nil(t, err) + + // Check that the transaction decodes as epxceted + var decodedTx auth.StdTx + require.Nil(t, cdc.UnmarshalBinaryLengthPrefixed(decodedBytes, &decodedTx)) + require.Equal(t, "deadbeef", decodedTx.Memo) +} + +func TestGaiaCLIMultisignSortSignatures(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server with minimum fees + proc := f.GDStart() + defer proc.Stop(false) + + fooBarBazAddr := f.KeyAddress(keyFooBarBaz) + barAddr := f.KeyAddress(keyBar) + + // Send some tokens from one account to the other + success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + fooBarBazAcc := f.QueryAccount(fooBarBazAddr) + require.Equal(t, int64(10), fooBarBazAcc.GetCoins().AmountOf(denom).Int64()) + + // Test generate sendTx with multisig + success, stdout, _ := f.TxSend(fooBarBazAddr.String(), barAddr, sdk.NewInt64Coin(denom, 5), "--generate-only") + require.True(t, success) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(unsignedTxFile.Name()) + + // Sign with foo's key + success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String()) + require.True(t, success) + + // Write the output to disk + fooSignatureFile := WriteToNewTempFile(t, stdout) + defer os.Remove(fooSignatureFile.Name()) + + // Sign with baz's key + success, stdout, _ = f.TxSign(keyBaz, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String()) + require.True(t, success) + + // Write the output to disk + bazSignatureFile := WriteToNewTempFile(t, stdout) + defer os.Remove(bazSignatureFile.Name()) + + // Multisign, keys in different order + success, stdout, _ = f.TxMultisign(unsignedTxFile.Name(), keyFooBarBaz, []string{ + bazSignatureFile.Name(), fooSignatureFile.Name()}) + require.True(t, success) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(signedTxFile.Name()) + + // Validate the multisignature + success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures") + require.True(t, success) + + // Broadcast the transaction + success, _, _ = f.TxBroadcast(signedTxFile.Name()) + require.True(t, success) +} + +func TestGaiaCLIMultisign(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server with minimum fees + proc := f.GDStart() + defer proc.Stop(false) + + fooBarBazAddr := f.KeyAddress(keyFooBarBaz) + bazAddr := f.KeyAddress(keyBaz) + + // Send some tokens from one account to the other + success, _, _ := f.TxSend(keyFoo, fooBarBazAddr, sdk.NewInt64Coin(denom, 10), "-y") + require.True(t, success) + tests.WaitForNextNBlocksTM(1, f.Port) + + // Ensure account balances match expected + fooBarBazAcc := f.QueryAccount(fooBarBazAddr) + require.Equal(t, int64(10), fooBarBazAcc.GetCoins().AmountOf(denom).Int64()) + + // Test generate sendTx with multisig + success, stdout, stderr := f.TxSend(fooBarBazAddr.String(), bazAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") + require.True(t, success) + require.Empty(t, stderr) + + // Write the output to disk + unsignedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(unsignedTxFile.Name()) + + // Sign with foo's key + success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y") + require.True(t, success) + + // Write the output to disk + fooSignatureFile := WriteToNewTempFile(t, stdout) + defer os.Remove(fooSignatureFile.Name()) + + // Sign with bar's key + success, stdout, _ = f.TxSign(keyBar, unsignedTxFile.Name(), "--multisig", fooBarBazAddr.String(), "-y") + require.True(t, success) + + // Write the output to disk + barSignatureFile := WriteToNewTempFile(t, stdout) + defer os.Remove(barSignatureFile.Name()) + + // Multisign + success, stdout, _ = f.TxMultisign(unsignedTxFile.Name(), keyFooBarBaz, []string{ + fooSignatureFile.Name(), barSignatureFile.Name()}) + require.True(t, success) + + // Write the output to disk + signedTxFile := WriteToNewTempFile(t, stdout) + defer os.Remove(signedTxFile.Name()) + + // Validate the multisignature + success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures", "-y") + require.True(t, success) + + // Broadcast the transaction + success, _, _ = f.TxBroadcast(signedTxFile.Name()) + require.True(t, success) +} + +func TestGaiaCLIConfig(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + node := fmt.Sprintf("%s:%s", f.RPCAddr, f.Port) + + // Set available configuration options + f.CLIConfig("broadcast-mode", "block") + f.CLIConfig("node", node) + f.CLIConfig("output", "text") + f.CLIConfig("trust-node", "true") + f.CLIConfig("chain-id", f.ChainID) + f.CLIConfig("trace", "false") + f.CLIConfig("indent", "true") + + config, err := ioutil.ReadFile(path.Join(f.GCLIHome, "config", "config.toml")) + require.NoError(t, err) + expectedConfig := fmt.Sprintf(`broadcast-mode = "block" +chain-id = "%s" +indent = true +node = "%s" +output = "text" +trace = false +trust-node = true +`, f.ChainID, node) + require.Equal(t, expectedConfig, string(config)) + + f.Cleanup() +} + +func TestGaiadCollectGentxs(t *testing.T) { + t.Parallel() + f := NewFixtures(t) + + // Initialise temporary directories + gentxDir, err := ioutil.TempDir("", "") + gentxDoc := filepath.Join(gentxDir, "gentx.json") + require.NoError(t, err) + + // Reset testing path + f.UnsafeResetAll() + + // Initialize keys + f.KeysAdd(keyFoo) + + // Configure json output + f.CLIConfig("output", "json") + + // Run init + f.GDInit(keyFoo) + + // Add account to genesis.json + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + + // Write gentx file + f.GenTx(keyFoo, fmt.Sprintf("--output-document=%s", gentxDoc)) + + // Collect gentxs from a custom directory + f.CollectGenTxs(fmt.Sprintf("--gentx-dir=%s", gentxDir)) + + f.Cleanup(gentxDir) +} + +func TestGaiadAddGenesisAccount(t *testing.T) { + t.Parallel() + f := NewFixtures(t) + + // Reset testing path + f.UnsafeResetAll() + + // Initialize keys + f.KeysDelete(keyFoo) + f.KeysDelete(keyBar) + f.KeysDelete(keyBaz) + f.KeysAdd(keyFoo) + f.KeysAdd(keyBar) + f.KeysAdd(keyBaz) + + // Configure json output + f.CLIConfig("output", "json") + + // Run init + f.GDInit(keyFoo) + + // Add account to genesis.json + bazCoins := sdk.Coins{ + sdk.NewInt64Coin("acoin", 1000000), + sdk.NewInt64Coin("bcoin", 1000000), + } + + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + f.AddGenesisAccount(f.KeyAddress(keyBar), bazCoins) + genesisState := f.GenesisState() + require.Equal(t, genesisState.Accounts[0].Address, f.KeyAddress(keyFoo)) + require.Equal(t, genesisState.Accounts[1].Address, f.KeyAddress(keyBar)) + require.True(t, genesisState.Accounts[0].Coins.IsEqual(startCoins)) + require.True(t, genesisState.Accounts[1].Coins.IsEqual(bazCoins)) +} + +func TestSlashingGetParams(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + params := f.QuerySlashingParams() + require.Equal(t, time.Duration(120000000000), params.MaxEvidenceAge) + require.Equal(t, int64(100), params.SignedBlocksWindow) + require.Equal(t, sdk.NewDecWithPrec(5, 1), params.MinSignedPerWindow) + + sinfo := f.QuerySigningInfo(f.GDTendermint("show-validator")) + require.Equal(t, int64(0), sinfo.StartHeight) + require.False(t, sinfo.Tombstoned) +} + +func TestValidateGenesis(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + f.ValidateGenesis() +} diff --git a/cli_test/doc.go b/cli_test/doc.go new file mode 100644 index 00000000..bcf9c5e4 --- /dev/null +++ b/cli_test/doc.go @@ -0,0 +1,3 @@ +package clitest + +// package clitest runs integration tests which make use of CLI commands. diff --git a/cli_test/test_helpers.go b/cli_test/test_helpers.go new file mode 100644 index 00000000..a05e967e --- /dev/null +++ b/cli_test/test_helpers.go @@ -0,0 +1,685 @@ +package clitest + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + cmn "github.com/tendermint/tendermint/libs/common" + + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + appInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/tests" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +const ( + denom = "stake" + keyFoo = "foo" + keyBar = "bar" + fooDenom = "footoken" + feeDenom = "feetoken" + fee2Denom = "fee2token" + keyBaz = "baz" + keyVesting = "vesting" + keyFooBarBaz = "foobarbaz" +) + +var ( + startCoins = sdk.Coins{ + sdk.NewCoin(feeDenom, sdk.TokensFromTendermintPower(1000000)), + sdk.NewCoin(fee2Denom, sdk.TokensFromTendermintPower(1000000)), + sdk.NewCoin(fooDenom, sdk.TokensFromTendermintPower(1000)), + sdk.NewCoin(denom, sdk.TokensFromTendermintPower(150)), + } + + vestingCoins = sdk.Coins{ + sdk.NewCoin(feeDenom, sdk.TokensFromTendermintPower(500000)), + } +) + +//___________________________________________________________________________________ +// Fixtures + +// Fixtures is used to setup the testing environment +type Fixtures struct { + ChainID string + RPCAddr string + Port string + GDHome string + GCLIHome string + P2PAddr string + T *testing.T +} + +// NewFixtures creates a new instance of Fixtures with many vars set +func NewFixtures(t *testing.T) *Fixtures { + tmpDir, err := ioutil.TempDir("", "gaia_integration_"+t.Name()+"_") + require.NoError(t, err) + + servAddr, port, err := server.FreeTCPAddr() + require.NoError(t, err) + + p2pAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + + return &Fixtures{ + T: t, + GDHome: filepath.Join(tmpDir, ".gaiad"), + GCLIHome: filepath.Join(tmpDir, ".gaiacli"), + RPCAddr: servAddr, + P2PAddr: p2pAddr, + Port: port, + } +} + +// GenesisFile returns the path of the genesis file +func (f Fixtures) GenesisFile() string { + return filepath.Join(f.GDHome, "config", "genesis.json") +} + +// GenesisFile returns the application's genesis state +func (f Fixtures) GenesisState() app.GenesisState { + cdc := codec.New() + genDoc, err := appInit.LoadGenesisDoc(cdc, f.GenesisFile()) + require.NoError(f.T, err) + + var appState app.GenesisState + require.NoError(f.T, cdc.UnmarshalJSON(genDoc.AppState, &appState)) + return appState +} + +// InitFixtures is called at the beginning of a test and initializes a chain +// with 1 validator. +func InitFixtures(t *testing.T) (f *Fixtures) { + f = NewFixtures(t) + + // reset test state + f.UnsafeResetAll() + + // ensure keystore has foo and bar keys + f.KeysDelete(keyFoo) + f.KeysDelete(keyBar) + f.KeysDelete(keyBar) + f.KeysDelete(keyFooBarBaz) + f.KeysAdd(keyFoo) + f.KeysAdd(keyBar) + f.KeysAdd(keyBaz) + f.KeysAdd(keyVesting) + f.KeysAdd(keyFooBarBaz, "--multisig-threshold=2", fmt.Sprintf( + "--multisig=%s,%s,%s", keyFoo, keyBar, keyBaz)) + + // ensure that CLI output is in JSON format + f.CLIConfig("output", "json") + + // NOTE: GDInit sets the ChainID + f.GDInit(keyFoo) + + f.CLIConfig("chain-id", f.ChainID) + f.CLIConfig("broadcast-mode", "block") + + // start an account with tokens + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + f.AddGenesisAccount( + f.KeyAddress(keyVesting), startCoins, + fmt.Sprintf("--vesting-amount=%s", vestingCoins), + fmt.Sprintf("--vesting-start-time=%d", time.Now().UTC().UnixNano()), + fmt.Sprintf("--vesting-end-time=%d", time.Now().Add(60*time.Second).UTC().UnixNano()), + ) + + f.GenTx(keyFoo) + f.CollectGenTxs() + + return +} + +// Cleanup is meant to be run at the end of a test to clean up an remaining test state +func (f *Fixtures) Cleanup(dirs ...string) { + clean := append(dirs, f.GDHome, f.GCLIHome) + for _, d := range clean { + err := os.RemoveAll(d) + require.NoError(f.T, err) + } +} + +// Flags returns the flags necessary for making most CLI calls +func (f *Fixtures) Flags() string { + return fmt.Sprintf("--home=%s --node=%s", f.GCLIHome, f.RPCAddr) +} + +//___________________________________________________________________________________ +// gaiad + +// UnsafeResetAll is gaiad unsafe-reset-all +func (f *Fixtures) UnsafeResetAll(flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiad --home=%s unsafe-reset-all", f.GDHome) + executeWrite(f.T, addFlags(cmd, flags)) + err := os.RemoveAll(filepath.Join(f.GDHome, "config", "gentx")) + require.NoError(f.T, err) +} + +// GDInit is gaiad init +// NOTE: GDInit sets the ChainID for the Fixtures instance +func (f *Fixtures) GDInit(moniker string, flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiad init -o --home=%s %s", f.GDHome, moniker) + _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), app.DefaultKeyPass) + + var chainID string + var initRes map[string]json.RawMessage + + err := json.Unmarshal([]byte(stderr), &initRes) + require.NoError(f.T, err) + + err = json.Unmarshal(initRes["chain_id"], &chainID) + require.NoError(f.T, err) + + f.ChainID = chainID +} + +// AddGenesisAccount is gaiad add-genesis-account +func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiad add-genesis-account %s %s --home=%s", address, coins, f.GDHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags)) +} + +// GenTx is gaiad gentx +func (f *Fixtures) GenTx(name string, flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiad gentx --name=%s --home=%s --home-client=%s", name, f.GDHome, f.GCLIHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// CollectGenTxs is gaiad collect-gentxs +func (f *Fixtures) CollectGenTxs(flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiad collect-gentxs --home=%s", f.GDHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// GDStart runs gaiad start with the appropriate flags and returns a process +func (f *Fixtures) GDStart(flags ...string) *tests.Process { + cmd := fmt.Sprintf("../../../build/gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GDHome, f.RPCAddr, f.P2PAddr) + proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags)) + tests.WaitForTMStart(f.Port) + tests.WaitForNextNBlocksTM(1, f.Port) + return proc +} + +// GDTendermint returns the results of gaiad tendermint [query] +func (f *Fixtures) GDTendermint(query string) string { + cmd := fmt.Sprintf("../../../build/gaiad tendermint %s --home=%s", query, f.GDHome) + success, stdout, stderr := executeWriteRetStdStreams(f.T, cmd) + require.Empty(f.T, stderr) + require.True(f.T, success) + return strings.TrimSpace(stdout) +} + +// ValidateGenesis runs gaiad validate-genesis +func (f *Fixtures) ValidateGenesis() { + cmd := fmt.Sprintf("../../../build/gaiad validate-genesis --home=%s", f.GDHome) + executeWriteCheckErr(f.T, cmd) +} + +//___________________________________________________________________________________ +// gaiacli keys + +// KeysDelete is gaiacli keys delete +func (f *Fixtures) KeysDelete(name string, flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiacli keys delete --home=%s %s", f.GCLIHome, name) + executeWrite(f.T, addFlags(cmd, append(append(flags, "-y"), "-f"))) +} + +// KeysAdd is gaiacli keys add +func (f *Fixtures) KeysAdd(name string, flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s %s", f.GCLIHome, name) + executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// KeysAddRecover prepares gaiacli keys add --recover +func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) (exitSuccess bool, stdout, stderr string) { + cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s", f.GCLIHome, name) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) +} + +// KeysAddRecoverHDPath prepares gaiacli keys add --recover --account --index +func (f *Fixtures) KeysAddRecoverHDPath(name, mnemonic string, account uint32, index uint32, flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s --account %d --index %d", f.GCLIHome, name, account, index) + executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) +} + +// KeysShow is gaiacli keys show +func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { + cmd := fmt.Sprintf("../../../build/gaiacli keys show --home=%s %s", f.GCLIHome, name) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var ko keys.KeyOutput + err := clientkeys.UnmarshalJSON([]byte(out), &ko) + require.NoError(f.T, err) + return ko +} + +// KeyAddress returns the SDK account address from the key +func (f *Fixtures) KeyAddress(name string) sdk.AccAddress { + ko := f.KeysShow(name) + accAddr, err := sdk.AccAddressFromBech32(ko.Address) + require.NoError(f.T, err) + return accAddr +} + +//___________________________________________________________________________________ +// gaiacli config + +// CLIConfig is gaiacli config +func (f *Fixtures) CLIConfig(key, value string, flags ...string) { + cmd := fmt.Sprintf("../../../build/gaiacli config --home=%s %s %s", f.GCLIHome, key, value) + executeWriteCheckErr(f.T, addFlags(cmd, flags)) +} + +//___________________________________________________________________________________ +// gaiacli tx send/sign/broadcast + +// TxSend is gaiacli tx send +func (f *Fixtures) TxSend(from string, to sdk.AccAddress, amount sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +func (f *Fixtures) txSendWithConfirm( + from string, to sdk.AccAddress, amount sdk.Coin, confirm string, flags ...string, +) (bool, string, string) { + + cmd := fmt.Sprintf("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), confirm, app.DefaultKeyPass) +} + +// TxSign is gaiacli tx sign +func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx sign %v --from=%s %v", f.Flags(), signer, fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// TxBroadcast is gaiacli tx broadcast +func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx broadcast %v %v", f.Flags(), fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// TxEncode is gaiacli tx encode +func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx encode %v %v", f.Flags(), fileName) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// TxMultisign is gaiacli tx multisign +func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string, + flags ...string) (bool, string, string) { + + cmd := fmt.Sprintf("../../../build/gaiacli tx multisign %v %s %s %s", f.Flags(), + fileName, name, strings.Join(signaturesFiles, " "), + ) + return executeWriteRetStdStreams(f.T, cmd) +} + +//___________________________________________________________________________________ +// gaiacli tx staking + +// TxStakingCreateValidator is gaiacli tx staking create-validator +func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx staking create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey) + cmd += fmt.Sprintf(" --amount=%v --moniker=%v --commission-rate=%v", amount, from, "0.05") + cmd += fmt.Sprintf(" --commission-max-rate=%v --commission-max-change-rate=%v", "0.20", "0.10") + cmd += fmt.Sprintf(" --min-self-delegation=%v", "1") + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// TxStakingUnbond is gaiacli tx staking unbond +func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool { + cmd := fmt.Sprintf("../../../build/gaiacli tx staking unbond %s %v --from=%s %v", validator, shares, from, f.Flags()) + return executeWrite(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +//___________________________________________________________________________________ +// gaiacli tx gov + +// TxGovSubmitProposal is gaiacli tx gov submit-proposal +func (f *Fixtures) TxGovSubmitProposal(from, typ, title, description string, deposit sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx gov submit-proposal %v --from=%s --type=%s", f.Flags(), from, typ) + cmd += fmt.Sprintf(" --title=%s --description=%s --deposit=%s", title, description, deposit) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// TxGovDeposit is gaiacli tx gov deposit +func (f *Fixtures) TxGovDeposit(proposalID int, from string, amount sdk.Coin, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx gov deposit %d %s --from=%s %v", proposalID, amount, from, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +// TxGovVote is gaiacli tx gov vote +func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("../../../build/gaiacli tx gov vote %d %s --from=%s %v", proposalID, option, from, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) +} + +//___________________________________________________________________________________ +// gaiacli query account + +// QueryAccount is gaiacli query account +func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.BaseAccount { + cmd := fmt.Sprintf("../../../build/gaiacli query account %s %v", address, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var initRes map[string]json.RawMessage + err := json.Unmarshal([]byte(out), &initRes) + require.NoError(f.T, err, "out %v, err %v", out, err) + value := initRes["value"] + var acc auth.BaseAccount + cdc := codec.New() + codec.RegisterCrypto(cdc) + err = cdc.UnmarshalJSON(value, &acc) + require.NoError(f.T, err, "value %v, err %v", string(value), err) + return acc +} + +//___________________________________________________________________________________ +// gaiacli query txs + +// QueryTxs is gaiacli query txs +func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse { + cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var txs []sdk.TxResponse + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &txs) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return txs +} + +// QueryTxsInvalid query txs with wrong parameters and compare expected error +func (f *Fixtures) QueryTxsInvalid(expectedErr error, page, limit int, tags ...string) { + cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + _, err := tests.ExecuteT(f.T, cmd, "") + require.EqualError(f.T, expectedErr, err) +} + +//___________________________________________________________________________________ +// gaiacli query staking + +// QueryStakingValidator is gaiacli query staking validator +func (f *Fixtures) QueryStakingValidator(valAddr sdk.ValAddress, flags ...string) staking.Validator { + cmd := fmt.Sprintf("../../../build/gaiacli query staking validator %s %v", valAddr, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var validator staking.Validator + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &validator) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return validator +} + +// QueryStakingUnbondingDelegationsFrom is gaiacli query staking unbonding-delegations-from +func (f *Fixtures) QueryStakingUnbondingDelegationsFrom(valAddr sdk.ValAddress, flags ...string) []staking.UnbondingDelegation { + cmd := fmt.Sprintf("../../../build/gaiacli query staking unbonding-delegations-from %s %v", valAddr, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var ubds []staking.UnbondingDelegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &ubds) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return ubds +} + +// QueryStakingDelegationsTo is gaiacli query staking delegations-to +func (f *Fixtures) QueryStakingDelegationsTo(valAddr sdk.ValAddress, flags ...string) []staking.Delegation { + cmd := fmt.Sprintf("../../../build/gaiacli query staking delegations-to %s %v", valAddr, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var delegations []staking.Delegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &delegations) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return delegations +} + +// QueryStakingPool is gaiacli query staking pool +func (f *Fixtures) QueryStakingPool(flags ...string) staking.Pool { + cmd := fmt.Sprintf("../../../build/gaiacli query staking pool %v", f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var pool staking.Pool + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &pool) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return pool +} + +// QueryStakingParameters is gaiacli query staking parameters +func (f *Fixtures) QueryStakingParameters(flags ...string) staking.Params { + cmd := fmt.Sprintf("../../../build/gaiacli query staking params %v", f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var params staking.Params + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), ¶ms) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return params +} + +//___________________________________________________________________________________ +// gaiacli query gov + +// QueryGovParamDeposit is gaiacli query gov param deposit +func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams { + cmd := fmt.Sprintf("../../../build/gaiacli query gov param deposit %s", f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var depositParam gov.DepositParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &depositParam) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return depositParam +} + +// QueryGovParamVoting is gaiacli query gov param voting +func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { + cmd := fmt.Sprintf("../../../build/gaiacli query gov param voting %s", f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var votingParam gov.VotingParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &votingParam) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return votingParam +} + +// QueryGovParamTallying is gaiacli query gov param tallying +func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { + cmd := fmt.Sprintf("../../../build/gaiacli query gov param tallying %s", f.Flags()) + out, _ := tests.ExecuteT(f.T, cmd, "") + var tallyingParam gov.TallyParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &tallyingParam) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return tallyingParam +} + +// QueryGovProposals is gaiacli query gov proposals +func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals { + cmd := fmt.Sprintf("../../../build/gaiacli query gov proposals %v", f.Flags()) + stdout, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + if strings.Contains(stderr, "No matching proposals found") { + return gov.Proposals{} + } + require.Empty(f.T, stderr) + var out gov.Proposals + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(stdout), &out) + require.NoError(f.T, err) + return out +} + +// QueryGovProposal is gaiacli query gov proposal +func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposal { + cmd := fmt.Sprintf("../../../build/gaiacli query gov proposal %d %v", proposalID, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var proposal gov.Proposal + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &proposal) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return proposal +} + +// QueryGovVote is gaiacli query gov vote +func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...string) gov.Vote { + cmd := fmt.Sprintf("../../../build/gaiacli query gov vote %d %s %v", proposalID, voter, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var vote gov.Vote + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &vote) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return vote +} + +// QueryGovVotes is gaiacli query gov votes +func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote { + cmd := fmt.Sprintf("../../../build/gaiacli query gov votes %d %v", proposalID, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var votes []gov.Vote + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &votes) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return votes +} + +// QueryGovDeposit is gaiacli query gov deposit +func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, flags ...string) gov.Deposit { + cmd := fmt.Sprintf("../../../build/gaiacli query gov deposit %d %s %v", proposalID, depositor, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var deposit gov.Deposit + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &deposit) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return deposit +} + +// QueryGovDeposits is gaiacli query gov deposits +func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposit { + cmd := fmt.Sprintf("../../../build/gaiacli query gov deposits %d %v", propsalID, f.Flags()) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var deposits []gov.Deposit + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &deposits) + require.NoError(f.T, err, "out %v\n, err %v", out, err) + return deposits +} + +//___________________________________________________________________________________ +// query slashing + +// QuerySigningInfo returns the signing info for a validator +func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { + cmd := fmt.Sprintf("../../../build/gaiacli query slashing signing-info %s %s", val, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var sinfo slashing.ValidatorSigningInfo + err := cdc.UnmarshalJSON([]byte(res), &sinfo) + require.NoError(f.T, err) + return sinfo +} + +// QuerySlashingParams is gaiacli query slashing params +func (f *Fixtures) QuerySlashingParams() slashing.Params { + cmd := fmt.Sprintf("../../../build/gaiacli query slashing params %s", f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var params slashing.Params + err := cdc.UnmarshalJSON([]byte(res), ¶ms) + require.NoError(f.T, err) + return params +} + +//___________________________________________________________________________________ +// executors + +func executeWriteCheckErr(t *testing.T, cmdStr string, writes ...string) { + require.True(t, executeWrite(t, cmdStr, writes...)) +} + +func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) { + exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...) + return +} + +func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (bool, string, string) { + proc := tests.GoExecuteT(t, cmdStr) + + // Enables use of interactive commands + for _, write := range writes { + _, err := proc.StdinPipe.Write([]byte(write + "\n")) + require.NoError(t, err) + } + + // Read both stdout and stderr from the process + stdout, stderr, err := proc.ReadAll() + if err != nil { + fmt.Println("Err on proc.ReadAll()", err, cmdStr) + } + + // Log output. + if len(stdout) > 0 { + t.Log("Stdout:", cmn.Green(string(stdout))) + } + if len(stderr) > 0 { + t.Log("Stderr:", cmn.Red(string(stderr))) + } + + // Wait for process to exit + proc.Wait() + + // Return succes, stdout, stderr + return proc.ExitState.Success(), string(stdout), string(stderr) +} + +//___________________________________________________________________________________ +// utils + +func addFlags(cmd string, flags []string) string { + for _, f := range flags { + cmd += " " + f + } + return strings.TrimSpace(cmd) +} + +func queryTags(tags []string) (out string) { + for _, tag := range tags { + out += tag + "&" + } + return strings.TrimSuffix(out, "&") +} + +// Write the given string to a new temporary file +func WriteToNewTempFile(t *testing.T, s string) *os.File { + fp, err := ioutil.TempFile(os.TempDir(), "cosmos_cli_test_") + require.Nil(t, err) + _, err = fp.WriteString(s) + require.Nil(t, err) + return fp +} + +func marshalStdTx(t *testing.T, stdTx auth.StdTx) []byte { + cdc := app.MakeCodec() + bz, err := cdc.MarshalBinaryBare(stdTx) + require.NoError(t, err) + return bz +} + +func unmarshalStdTx(t *testing.T, s string) (stdTx auth.StdTx) { + cdc := app.MakeCodec() + require.Nil(t, cdc.UnmarshalJSON([]byte(s), &stdTx)) + return +} From 1de350c4e9989676bbe2f4934c9b8fa2dc6c4765 Mon Sep 17 00:00:00 2001 From: rhuairahrighairigh Date: Tue, 25 Jun 2019 13:50:25 +0100 Subject: [PATCH 2/5] update import and build paths --- cli_test/cli_test.go | 3 +- cli_test/doc.go | 3 +- cli_test/test_helpers.go | 132 +++++++++++++++++++++------------------ 3 files changed, 75 insertions(+), 63 deletions(-) diff --git a/cli_test/cli_test.go b/cli_test/cli_test.go index 8a36a390..bbe42846 100644 --- a/cli_test/cli_test.go +++ b/cli_test/cli_test.go @@ -19,11 +19,12 @@ import ( "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/gov" + + "github.com/kava-labs/kava/app" ) func TestGaiaCLIKeysAddMultisig(t *testing.T) { diff --git a/cli_test/doc.go b/cli_test/doc.go index bcf9c5e4..51ef2ad5 100644 --- a/cli_test/doc.go +++ b/cli_test/doc.go @@ -1,3 +1,2 @@ +// Package clitest runs integration tests which make use of CLI commands. package clitest - -// package clitest runs integration tests which make use of CLI commands. diff --git a/cli_test/test_helpers.go b/cli_test/test_helpers.go index a05e967e..41d83016 100644 --- a/cli_test/test_helpers.go +++ b/cli_test/test_helpers.go @@ -3,6 +3,7 @@ package clitest import ( "encoding/json" "fmt" + "go/build" "io/ioutil" "os" "path/filepath" @@ -15,8 +16,6 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - appInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" @@ -26,6 +25,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" + + "github.com/kava-labs/kava/app" + appInit "github.com/kava-labs/kava/init" ) const ( @@ -58,20 +60,28 @@ var ( // Fixtures is used to setup the testing environment type Fixtures struct { - ChainID string - RPCAddr string - Port string - GDHome string - GCLIHome string - P2PAddr string - T *testing.T + ChainID string + RPCAddr string + Port string + AppDaemonBinary string + AppCliBinary string + GDHome string + GCLIHome string + P2PAddr string + T *testing.T } // NewFixtures creates a new instance of Fixtures with many vars set func NewFixtures(t *testing.T) *Fixtures { - tmpDir, err := ioutil.TempDir("", "gaia_integration_"+t.Name()+"_") + tmpDir, err := ioutil.TempDir("", "kava_integration_"+t.Name()+"_") require.NoError(t, err) + // set location of the app binaries + buildDir := os.Getenv("BUILDDIR") + if buildDir == "" { + buildDir = filepath.Join(build.Default.GOPATH, "bin") + } + servAddr, port, err := server.FreeTCPAddr() require.NoError(t, err) @@ -79,12 +89,14 @@ func NewFixtures(t *testing.T) *Fixtures { require.NoError(t, err) return &Fixtures{ - T: t, - GDHome: filepath.Join(tmpDir, ".gaiad"), - GCLIHome: filepath.Join(tmpDir, ".gaiacli"), - RPCAddr: servAddr, - P2PAddr: p2pAddr, - Port: port, + T: t, + AppDaemonBinary: filepath.Join(buildDir, "kvd"), + AppCliBinary: filepath.Join(buildDir, "kvcli"), + GDHome: filepath.Join(tmpDir, ".kvd"), + GCLIHome: filepath.Join(tmpDir, ".kvcli"), + RPCAddr: servAddr, + P2PAddr: p2pAddr, + Port: port, } } @@ -167,7 +179,7 @@ func (f *Fixtures) Flags() string { // UnsafeResetAll is gaiad unsafe-reset-all func (f *Fixtures) UnsafeResetAll(flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad --home=%s unsafe-reset-all", f.GDHome) + cmd := fmt.Sprintf("%s --home=%s unsafe-reset-all", f.AppDaemonBinary, f.GDHome) executeWrite(f.T, addFlags(cmd, flags)) err := os.RemoveAll(filepath.Join(f.GDHome, "config", "gentx")) require.NoError(f.T, err) @@ -176,7 +188,7 @@ func (f *Fixtures) UnsafeResetAll(flags ...string) { // GDInit is gaiad init // NOTE: GDInit sets the ChainID for the Fixtures instance func (f *Fixtures) GDInit(moniker string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad init -o --home=%s %s", f.GDHome, moniker) + cmd := fmt.Sprintf("%s init -o --home=%s %s", f.AppDaemonBinary, f.GDHome, moniker) _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), app.DefaultKeyPass) var chainID string @@ -193,25 +205,25 @@ func (f *Fixtures) GDInit(moniker string, flags ...string) { // AddGenesisAccount is gaiad add-genesis-account func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad add-genesis-account %s %s --home=%s", address, coins, f.GDHome) + cmd := fmt.Sprintf("%s add-genesis-account %s %s --home=%s", f.AppDaemonBinary, address, coins, f.GDHome) executeWriteCheckErr(f.T, addFlags(cmd, flags)) } // GenTx is gaiad gentx func (f *Fixtures) GenTx(name string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad gentx --name=%s --home=%s --home-client=%s", name, f.GDHome, f.GCLIHome) + cmd := fmt.Sprintf("%s gentx --name=%s --home=%s --home-client=%s", f.AppDaemonBinary, name, f.GDHome, f.GCLIHome) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // CollectGenTxs is gaiad collect-gentxs func (f *Fixtures) CollectGenTxs(flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiad collect-gentxs --home=%s", f.GDHome) + cmd := fmt.Sprintf("%s collect-gentxs --home=%s", f.AppDaemonBinary, f.GDHome) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // GDStart runs gaiad start with the appropriate flags and returns a process func (f *Fixtures) GDStart(flags ...string) *tests.Process { - cmd := fmt.Sprintf("../../../build/gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GDHome, f.RPCAddr, f.P2PAddr) + cmd := fmt.Sprintf("%s start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.AppDaemonBinary, f.GDHome, f.RPCAddr, f.P2PAddr) proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags)) tests.WaitForTMStart(f.Port) tests.WaitForNextNBlocksTM(1, f.Port) @@ -220,7 +232,7 @@ func (f *Fixtures) GDStart(flags ...string) *tests.Process { // GDTendermint returns the results of gaiad tendermint [query] func (f *Fixtures) GDTendermint(query string) string { - cmd := fmt.Sprintf("../../../build/gaiad tendermint %s --home=%s", query, f.GDHome) + cmd := fmt.Sprintf("%s tendermint %s --home=%s", f.AppDaemonBinary, query, f.GDHome) success, stdout, stderr := executeWriteRetStdStreams(f.T, cmd) require.Empty(f.T, stderr) require.True(f.T, success) @@ -229,7 +241,7 @@ func (f *Fixtures) GDTendermint(query string) string { // ValidateGenesis runs gaiad validate-genesis func (f *Fixtures) ValidateGenesis() { - cmd := fmt.Sprintf("../../../build/gaiad validate-genesis --home=%s", f.GDHome) + cmd := fmt.Sprintf("%s validate-genesis --home=%s", f.AppDaemonBinary, f.GDHome) executeWriteCheckErr(f.T, cmd) } @@ -238,31 +250,31 @@ func (f *Fixtures) ValidateGenesis() { // KeysDelete is gaiacli keys delete func (f *Fixtures) KeysDelete(name string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiacli keys delete --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("%s keys delete --home=%s %s", f.AppCliBinary, f.GCLIHome, name) executeWrite(f.T, addFlags(cmd, append(append(flags, "-y"), "-f"))) } // KeysAdd is gaiacli keys add func (f *Fixtures) KeysAdd(name string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("%s keys add --home=%s %s", f.AppCliBinary, f.GCLIHome, name) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // KeysAddRecover prepares gaiacli keys add --recover func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) (exitSuccess bool, stdout, stderr string) { - cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s", f.GCLIHome, name) + cmd := fmt.Sprintf("%s keys add --home=%s --recover %s", f.AppCliBinary, f.GCLIHome, name) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) } // KeysAddRecoverHDPath prepares gaiacli keys add --recover --account --index func (f *Fixtures) KeysAddRecoverHDPath(name, mnemonic string, account uint32, index uint32, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s --account %d --index %d", f.GCLIHome, name, account, index) + cmd := fmt.Sprintf("%s keys add --home=%s --recover %s --account %d --index %d", f.AppCliBinary, f.GCLIHome, name, account, index) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) } // KeysShow is gaiacli keys show func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { - cmd := fmt.Sprintf("../../../build/gaiacli keys show --home=%s %s", f.GCLIHome, name) + cmd := fmt.Sprintf("%s keys show --home=%s %s", f.AppCliBinary, f.GCLIHome, name) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var ko keys.KeyOutput err := clientkeys.UnmarshalJSON([]byte(out), &ko) @@ -283,7 +295,7 @@ func (f *Fixtures) KeyAddress(name string) sdk.AccAddress { // CLIConfig is gaiacli config func (f *Fixtures) CLIConfig(key, value string, flags ...string) { - cmd := fmt.Sprintf("../../../build/gaiacli config --home=%s %s %s", f.GCLIHome, key, value) + cmd := fmt.Sprintf("%s config --home=%s %s %s", f.AppCliBinary, f.GCLIHome, key, value) executeWriteCheckErr(f.T, addFlags(cmd, flags)) } @@ -292,7 +304,7 @@ func (f *Fixtures) CLIConfig(key, value string, flags ...string) { // TxSend is gaiacli tx send func (f *Fixtures) TxSend(from string, to sdk.AccAddress, amount sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) + cmd := fmt.Sprintf("%s tx send %s %s %v --from=%s", f.AppCliBinary, to, amount, f.Flags(), from) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -300,25 +312,25 @@ func (f *Fixtures) txSendWithConfirm( from string, to sdk.AccAddress, amount sdk.Coin, confirm string, flags ...string, ) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from) + cmd := fmt.Sprintf("%s tx send %s %s %v --from=%s", f.AppCliBinary, to, amount, f.Flags(), from) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), confirm, app.DefaultKeyPass) } // TxSign is gaiacli tx sign func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx sign %v --from=%s %v", f.Flags(), signer, fileName) + cmd := fmt.Sprintf("%s tx sign %v --from=%s %v", f.AppCliBinary, f.Flags(), signer, fileName) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxBroadcast is gaiacli tx broadcast func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx broadcast %v %v", f.Flags(), fileName) + cmd := fmt.Sprintf("%s tx broadcast %v %v", f.AppCliBinary, f.Flags(), fileName) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxEncode is gaiacli tx encode func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx encode %v %v", f.Flags(), fileName) + cmd := fmt.Sprintf("%s tx encode %v %v", f.AppCliBinary, f.Flags(), fileName) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -326,7 +338,7 @@ func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, str func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx multisign %v %s %s %s", f.Flags(), + cmd := fmt.Sprintf("%s tx multisign %v %s %s %s", f.AppCliBinary, f.Flags(), fileName, name, strings.Join(signaturesFiles, " "), ) return executeWriteRetStdStreams(f.T, cmd) @@ -337,7 +349,7 @@ func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string, // TxStakingCreateValidator is gaiacli tx staking create-validator func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx staking create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey) + cmd := fmt.Sprintf("%s tx staking create-validator %v --from=%s --pubkey=%s", f.AppCliBinary, f.Flags(), from, consPubKey) cmd += fmt.Sprintf(" --amount=%v --moniker=%v --commission-rate=%v", amount, from, "0.05") cmd += fmt.Sprintf(" --commission-max-rate=%v --commission-max-change-rate=%v", "0.20", "0.10") cmd += fmt.Sprintf(" --min-self-delegation=%v", "1") @@ -346,7 +358,7 @@ func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk. // TxStakingUnbond is gaiacli tx staking unbond func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool { - cmd := fmt.Sprintf("../../../build/gaiacli tx staking unbond %s %v --from=%s %v", validator, shares, from, f.Flags()) + cmd := fmt.Sprintf("%s tx staking unbond %s %v --from=%s %v", f.AppCliBinary, validator, shares, from, f.Flags()) return executeWrite(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -355,20 +367,20 @@ func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress // TxGovSubmitProposal is gaiacli tx gov submit-proposal func (f *Fixtures) TxGovSubmitProposal(from, typ, title, description string, deposit sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx gov submit-proposal %v --from=%s --type=%s", f.Flags(), from, typ) + cmd := fmt.Sprintf("%s tx gov submit-proposal %v --from=%s --type=%s", f.AppCliBinary, f.Flags(), from, typ) cmd += fmt.Sprintf(" --title=%s --description=%s --deposit=%s", title, description, deposit) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxGovDeposit is gaiacli tx gov deposit func (f *Fixtures) TxGovDeposit(proposalID int, from string, amount sdk.Coin, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx gov deposit %d %s --from=%s %v", proposalID, amount, from, f.Flags()) + cmd := fmt.Sprintf("%s tx gov deposit %d %s --from=%s %v", f.AppCliBinary, proposalID, amount, from, f.Flags()) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } // TxGovVote is gaiacli tx gov vote func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, flags ...string) (bool, string, string) { - cmd := fmt.Sprintf("../../../build/gaiacli tx gov vote %d %s --from=%s %v", proposalID, option, from, f.Flags()) + cmd := fmt.Sprintf("%s tx gov vote %d %s --from=%s %v", f.AppCliBinary, proposalID, option, from, f.Flags()) return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } @@ -377,7 +389,7 @@ func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, // QueryAccount is gaiacli query account func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.BaseAccount { - cmd := fmt.Sprintf("../../../build/gaiacli query account %s %v", address, f.Flags()) + cmd := fmt.Sprintf("%s query account %s %v", f.AppCliBinary, address, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var initRes map[string]json.RawMessage err := json.Unmarshal([]byte(out), &initRes) @@ -396,7 +408,7 @@ func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.Ba // QueryTxs is gaiacli query txs func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse { - cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + cmd := fmt.Sprintf("%s query txs --page=%d --limit=%d --tags='%s' %v", f.AppCliBinary, page, limit, queryTags(tags), f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var txs []sdk.TxResponse cdc := app.MakeCodec() @@ -407,7 +419,7 @@ func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse { // QueryTxsInvalid query txs with wrong parameters and compare expected error func (f *Fixtures) QueryTxsInvalid(expectedErr error, page, limit int, tags ...string) { - cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) + cmd := fmt.Sprintf("%s query txs --page=%d --limit=%d --tags='%s' %v", f.AppCliBinary, page, limit, queryTags(tags), f.Flags()) _, err := tests.ExecuteT(f.T, cmd, "") require.EqualError(f.T, expectedErr, err) } @@ -417,7 +429,7 @@ func (f *Fixtures) QueryTxsInvalid(expectedErr error, page, limit int, tags ...s // QueryStakingValidator is gaiacli query staking validator func (f *Fixtures) QueryStakingValidator(valAddr sdk.ValAddress, flags ...string) staking.Validator { - cmd := fmt.Sprintf("../../../build/gaiacli query staking validator %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("%s query staking validator %s %v", f.AppCliBinary, valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var validator staking.Validator cdc := app.MakeCodec() @@ -428,7 +440,7 @@ func (f *Fixtures) QueryStakingValidator(valAddr sdk.ValAddress, flags ...string // QueryStakingUnbondingDelegationsFrom is gaiacli query staking unbonding-delegations-from func (f *Fixtures) QueryStakingUnbondingDelegationsFrom(valAddr sdk.ValAddress, flags ...string) []staking.UnbondingDelegation { - cmd := fmt.Sprintf("../../../build/gaiacli query staking unbonding-delegations-from %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("%s query staking unbonding-delegations-from %s %v", f.AppCliBinary, valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var ubds []staking.UnbondingDelegation cdc := app.MakeCodec() @@ -439,7 +451,7 @@ func (f *Fixtures) QueryStakingUnbondingDelegationsFrom(valAddr sdk.ValAddress, // QueryStakingDelegationsTo is gaiacli query staking delegations-to func (f *Fixtures) QueryStakingDelegationsTo(valAddr sdk.ValAddress, flags ...string) []staking.Delegation { - cmd := fmt.Sprintf("../../../build/gaiacli query staking delegations-to %s %v", valAddr, f.Flags()) + cmd := fmt.Sprintf("%s query staking delegations-to %s %v", f.AppCliBinary, valAddr, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var delegations []staking.Delegation cdc := app.MakeCodec() @@ -450,7 +462,7 @@ func (f *Fixtures) QueryStakingDelegationsTo(valAddr sdk.ValAddress, flags ...st // QueryStakingPool is gaiacli query staking pool func (f *Fixtures) QueryStakingPool(flags ...string) staking.Pool { - cmd := fmt.Sprintf("../../../build/gaiacli query staking pool %v", f.Flags()) + cmd := fmt.Sprintf("%s query staking pool %v", f.AppCliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var pool staking.Pool cdc := app.MakeCodec() @@ -461,7 +473,7 @@ func (f *Fixtures) QueryStakingPool(flags ...string) staking.Pool { // QueryStakingParameters is gaiacli query staking parameters func (f *Fixtures) QueryStakingParameters(flags ...string) staking.Params { - cmd := fmt.Sprintf("../../../build/gaiacli query staking params %v", f.Flags()) + cmd := fmt.Sprintf("%s query staking params %v", f.AppCliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var params staking.Params cdc := app.MakeCodec() @@ -475,7 +487,7 @@ func (f *Fixtures) QueryStakingParameters(flags ...string) staking.Params { // QueryGovParamDeposit is gaiacli query gov param deposit func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams { - cmd := fmt.Sprintf("../../../build/gaiacli query gov param deposit %s", f.Flags()) + cmd := fmt.Sprintf("%s query gov param deposit %s", f.AppCliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var depositParam gov.DepositParams cdc := app.MakeCodec() @@ -486,7 +498,7 @@ func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams { // QueryGovParamVoting is gaiacli query gov param voting func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { - cmd := fmt.Sprintf("../../../build/gaiacli query gov param voting %s", f.Flags()) + cmd := fmt.Sprintf("%s query gov param voting %s", f.AppCliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var votingParam gov.VotingParams cdc := app.MakeCodec() @@ -497,7 +509,7 @@ func (f *Fixtures) QueryGovParamVoting() gov.VotingParams { // QueryGovParamTallying is gaiacli query gov param tallying func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { - cmd := fmt.Sprintf("../../../build/gaiacli query gov param tallying %s", f.Flags()) + cmd := fmt.Sprintf("%s query gov param tallying %s", f.AppCliBinary, f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") var tallyingParam gov.TallyParams cdc := app.MakeCodec() @@ -508,7 +520,7 @@ func (f *Fixtures) QueryGovParamTallying() gov.TallyParams { // QueryGovProposals is gaiacli query gov proposals func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals { - cmd := fmt.Sprintf("../../../build/gaiacli query gov proposals %v", f.Flags()) + cmd := fmt.Sprintf("%s query gov proposals %v", f.AppCliBinary, f.Flags()) stdout, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), "") if strings.Contains(stderr, "No matching proposals found") { return gov.Proposals{} @@ -523,7 +535,7 @@ func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals { // QueryGovProposal is gaiacli query gov proposal func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposal { - cmd := fmt.Sprintf("../../../build/gaiacli query gov proposal %d %v", proposalID, f.Flags()) + cmd := fmt.Sprintf("%s query gov proposal %d %v", f.AppCliBinary, proposalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var proposal gov.Proposal cdc := app.MakeCodec() @@ -534,7 +546,7 @@ func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposa // QueryGovVote is gaiacli query gov vote func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...string) gov.Vote { - cmd := fmt.Sprintf("../../../build/gaiacli query gov vote %d %s %v", proposalID, voter, f.Flags()) + cmd := fmt.Sprintf("%s query gov vote %d %s %v", f.AppCliBinary, proposalID, voter, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var vote gov.Vote cdc := app.MakeCodec() @@ -545,7 +557,7 @@ func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...s // QueryGovVotes is gaiacli query gov votes func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote { - cmd := fmt.Sprintf("../../../build/gaiacli query gov votes %d %v", proposalID, f.Flags()) + cmd := fmt.Sprintf("%s query gov votes %d %v", f.AppCliBinary, proposalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var votes []gov.Vote cdc := app.MakeCodec() @@ -556,7 +568,7 @@ func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote { // QueryGovDeposit is gaiacli query gov deposit func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, flags ...string) gov.Deposit { - cmd := fmt.Sprintf("../../../build/gaiacli query gov deposit %d %s %v", proposalID, depositor, f.Flags()) + cmd := fmt.Sprintf("%s query gov deposit %d %s %v", f.AppCliBinary, proposalID, depositor, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var deposit gov.Deposit cdc := app.MakeCodec() @@ -567,7 +579,7 @@ func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, fla // QueryGovDeposits is gaiacli query gov deposits func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposit { - cmd := fmt.Sprintf("../../../build/gaiacli query gov deposits %d %v", propsalID, f.Flags()) + cmd := fmt.Sprintf("%s query gov deposits %d %v", f.AppCliBinary, propsalID, f.Flags()) out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") var deposits []gov.Deposit cdc := app.MakeCodec() @@ -581,7 +593,7 @@ func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposi // QuerySigningInfo returns the signing info for a validator func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { - cmd := fmt.Sprintf("../../../build/gaiacli query slashing signing-info %s %s", val, f.Flags()) + cmd := fmt.Sprintf("%s query slashing signing-info %s %s", f.AppCliBinary, val, f.Flags()) res, errStr := tests.ExecuteT(f.T, cmd, "") require.Empty(f.T, errStr) cdc := app.MakeCodec() @@ -593,7 +605,7 @@ func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { // QuerySlashingParams is gaiacli query slashing params func (f *Fixtures) QuerySlashingParams() slashing.Params { - cmd := fmt.Sprintf("../../../build/gaiacli query slashing params %s", f.Flags()) + cmd := fmt.Sprintf("%s query slashing params %s", f.AppCliBinary, f.Flags()) res, errStr := tests.ExecuteT(f.T, cmd, "") require.Empty(f.T, errStr) cdc := app.MakeCodec() From 6a33517a5d83e7a58d7c9741ed385b8fce458fb3 Mon Sep 17 00:00:00 2001 From: rhuairahrighairigh Date: Tue, 25 Jun 2019 14:29:56 +0100 Subject: [PATCH 3/5] refactor address prefixes --- app/app.go | 6 ++++++ cmd/kvcli/main.go | 4 +--- cmd/kvd/main.go | 4 +--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/app.go b/app/app.go index 71a82b7c..46b0ccab 100644 --- a/app/app.go +++ b/app/app.go @@ -220,6 +220,12 @@ func MakeCodec() *codec.Codec { return cdc } +func SetBech32AddressPrefixes(config *sdk.Config) { + config.SetBech32PrefixForAccount(Bech32MainPrefix, Bech32MainPrefix+sdk.PrefixPublic) + config.SetBech32PrefixForValidator(Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixOperator, Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixOperator+sdk.PrefixPublic) + config.SetBech32PrefixForConsensusNode(Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixConsensus, Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixConsensus+sdk.PrefixPublic) +} + // application updates every end block func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { // mint new tokens for the previous block diff --git a/cmd/kvcli/main.go b/cmd/kvcli/main.go index 975c9240..755cb715 100644 --- a/cmd/kvcli/main.go +++ b/cmd/kvcli/main.go @@ -54,9 +54,7 @@ func main() { // Read in the configuration file for the sdk config := sdk.GetConfig() - config.SetBech32PrefixForAccount(app.Bech32MainPrefix, app.Bech32MainPrefix+sdk.PrefixPublic) - config.SetBech32PrefixForValidator(app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixOperator, app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixOperator+sdk.PrefixPublic) - config.SetBech32PrefixForConsensusNode(app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixConsensus, app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixConsensus+sdk.PrefixPublic) + app.SetBech32AddressPrefixes(config) config.Seal() // TODO: setup keybase, viper object, etc. to be passed into diff --git a/cmd/kvd/main.go b/cmd/kvd/main.go index 520badc4..e982a8ee 100644 --- a/cmd/kvd/main.go +++ b/cmd/kvd/main.go @@ -31,9 +31,7 @@ func main() { cdc := app.MakeCodec() config := sdk.GetConfig() - config.SetBech32PrefixForAccount(app.Bech32MainPrefix, app.Bech32MainPrefix+sdk.PrefixPublic) - config.SetBech32PrefixForValidator(app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixOperator, app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixOperator+sdk.PrefixPublic) - config.SetBech32PrefixForConsensusNode(app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixConsensus, app.Bech32MainPrefix+sdk.PrefixValidator+sdk.PrefixConsensus+sdk.PrefixPublic) + app.SetBech32AddressPrefixes(config) config.Seal() ctx := server.NewDefaultContext() From e5a63946799fb3b726c3abdcc909edf1c5ba5900 Mon Sep 17 00:00:00 2001 From: rhuairahrighairigh Date: Tue, 25 Jun 2019 14:30:06 +0100 Subject: [PATCH 4/5] fix tests --- cli_test/cli_test.go | 10 +++++----- cli_test/test_helpers.go | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cli_test/cli_test.go b/cli_test/cli_test.go index bbe42846..0b9ab572 100644 --- a/cli_test/cli_test.go +++ b/cli_test/cli_test.go @@ -56,7 +56,7 @@ func TestGaiaCLIKeysAddRecover(t *testing.T) { exitSuccess, _, _ = f.KeysAddRecover("test-recover", "dentist task convince chimney quality leave banana trade firm crawl eternal easily") require.True(t, exitSuccess) - require.Equal(t, "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4", f.KeyAddress("test-recover").String()) + require.Equal(t, "k1qcfdf69js922qrdr4yaww3ax7gjml6pd3fmkx7", f.KeyAddress("test-recover").String()) } func TestGaiaCLIKeysAddRecoverHDPath(t *testing.T) { @@ -64,16 +64,16 @@ func TestGaiaCLIKeysAddRecoverHDPath(t *testing.T) { f := InitFixtures(t) f.KeysAddRecoverHDPath("test-recoverHD1", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 0, 0) - require.Equal(t, "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4", f.KeyAddress("test-recoverHD1").String()) + require.Equal(t, "k1qcfdf69js922qrdr4yaww3ax7gjml6pd3fmkx7", f.KeyAddress("test-recoverHD1").String()) f.KeysAddRecoverHDPath("test-recoverH2", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 5) - require.Equal(t, "cosmos1pdfav2cjhry9k79nu6r8kgknnjtq6a7rykmafy", f.KeyAddress("test-recoverH2").String()) + require.Equal(t, "k1pdfav2cjhry9k79nu6r8kgknnjtq6a7rc043x0", f.KeyAddress("test-recoverH2").String()) f.KeysAddRecoverHDPath("test-recoverH3", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 17) - require.Equal(t, "cosmos1909k354n6wl8ujzu6kmh49w4d02ax7qvlkv4sn", f.KeyAddress("test-recoverH3").String()) + require.Equal(t, "k1909k354n6wl8ujzu6kmh49w4d02ax7qvr0zelc", f.KeyAddress("test-recoverH3").String()) f.KeysAddRecoverHDPath("test-recoverH4", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 2, 17) - require.Equal(t, "cosmos1v9plmhvyhgxk3th9ydacm7j4z357s3nhtwsjat", f.KeyAddress("test-recoverH4").String()) + require.Equal(t, "k1v9plmhvyhgxk3th9ydacm7j4z357s3nhhh77jq", f.KeyAddress("test-recoverH4").String()) } func TestGaiaCLIMinimumFees(t *testing.T) { diff --git a/cli_test/test_helpers.go b/cli_test/test_helpers.go index 41d83016..6441f444 100644 --- a/cli_test/test_helpers.go +++ b/cli_test/test_helpers.go @@ -55,6 +55,13 @@ var ( } ) +func init() { + // set the address prefixes + config := sdk.GetConfig() + app.SetBech32AddressPrefixes(config) + config.Seal() +} + //___________________________________________________________________________________ // Fixtures From cde0567a7e007f874ff8bdda3849216e3030fd0a Mon Sep 17 00:00:00 2001 From: rhuairahrighairigh Date: Tue, 25 Jun 2019 14:42:20 +0100 Subject: [PATCH 5/5] simplify README --- cli_test/README.md | 52 +++++++--------------------------------------- 1 file changed, 7 insertions(+), 45 deletions(-) diff --git a/cli_test/README.md b/cli_test/README.md index 37fd41ce..8dacad3a 100644 --- a/cli_test/README.md +++ b/cli_test/README.md @@ -1,51 +1,13 @@ -# Gaia CLI Integration tests +# CLI Integration tests -The gaia cli integration tests live in this folder. You can run the full suite by running: +Run the full suite: ```bash -$ go test -v -p 4 ./cmd/gaia/cli_test/... -# OR! -$ make test_cli +go test -v -p 4 ./cli_test -tags cli_test ``` + +`-v` for verbose, `-p 4` to use 4 cores, `-tags cli_test` a build tag (specified in `cli_test.go`) to tell go not to ignore the package + > NOTE: While the full suite runs in parallel, some of the tests can take up to a minute to complete -### Test Structure - -This integration suite [uses a thin wrapper](https://godoc.org/github.com/cosmos/cosmos-sdk/tests) over the [`os/exec`](https://golang.org/pkg/os/exec/) package. This allows the integration test to run against built binaries (both `gaiad` and `gaiacli` are used) while being written in golang. This allows tests to take advantage of the various golang code we have for operations like marshal/unmarshal, crypto, etc... - -> NOTE: The tests will use whatever `gaiad` or `gaiacli` binaries are available in your `$PATH`. You can check which binary will be run by the suite by running `which gaiad` or `which gaiacli`. If you have your `$GOPATH` properly setup they should be in `$GOPATH/bin/gaia*`. This will ensure that your test uses the latest binary you have built - -Tests generally follow this structure: - -```go -func TestMyNewCommand(t *testing.T) { - t.Parallel() - f := InitFixtures(t) - - // start gaiad server - proc := f.GDStart() - defer proc.Stop(false) - - // Your test code goes here... - - f.Cleanup() -} -``` - -This boilerplate above: -- Ensures the tests run in parallel. Because the tests are calling out to `os/exec` for many operations these tests can take a long time to run. -- Creates `.gaiad` and `.gaiacli` folders in a new temp folder. -- Uses `gaiacli` to create 2 accounts for use in testing: `foo` and `bar` -- Creates a genesis file with coins (`1000footoken,1000feetoken,150stake`) controlled by the `foo` key -- Generates an initial bonding transaction (`gentx`) to make the `foo` key a validator at genesis -- Starts `gaiad` and stops it once the test exits -- Cleans up test state on a successful run - -### Notes when adding/running tests - -- Because the tests run against a built binary, you should make sure you build every time the code changes and you want to test again, otherwise you will be testing against an older version. If you are adding new tests this can easily lead to confusing test results. -- The [`test_helpers.go`](./test_helpers.go) file is organized according to the format of `gaiacli` and `gaiad` commands. There are comments with section headers describing the different areas. Helper functions to call CLI functionality are generally named after the command (e.g. `gaiacli query staking validator` would be `QueryStakingValidator`). Try to keep functions grouped by their position in the command tree. -- Test state that is needed by `tx` and `query` commands (`home`, `chain_id`, etc...) is stored on the `Fixtures` object. This makes constructing your new tests almost trivial. -- Sometimes if you exit a test early there can be still running `gaiad` and `gaiacli` processes that will interrupt subsequent runs. Still running `gaiacli` processes will block access to the keybase while still running `gaiad` processes will block ports and prevent new tests from spinning up. You can ensure new tests spin up clean by running `pkill -9 gaiad && pkill -9 gaiacli` before each test run. -- Most `query` and `tx` commands take a variadic `flags` argument. This pattern allows for the creation of a general function which is easily modified by adding flags. See the `TxSend` function and its use for a good example. -- `Tx*` functions follow a general pattern and return `(success bool, stdout string, stderr string)`. This allows for easy testing of multiple different flag configurations. See `TestGaiaCLICreateValidator` or `TestGaiaCLISubmitProposal` for a good example of the pattern. +> NOTE: The tests will use the `kvd` or `kvcli` binaries in your `$GOPATH/bin`. Or in `$BUILDDIR` if that env var is set. \ No newline at end of file