diff --git a/tests/systemtests/authz_test.go b/tests/systemtests/authz_test.go index c411ad20d36e..da66aef5d267 100644 --- a/tests/systemtests/authz_test.go +++ b/tests/systemtests/authz_test.go @@ -4,6 +4,7 @@ package systemtests import ( "fmt" + "net/http" "os" "testing" "time" @@ -670,73 +671,84 @@ func TestAuthzGRPCQueries(t *testing.T) { invalidMsgTypeOutput := `{"code":2, "message":"codespace authz code 2: authorization not found: authorization not found for invalidMsg type", "details":[]}` expGrantOutput := fmt.Sprintf(`{"grants":[{%s}],"pagination":null}`, grant1) - grantTestCases := []GRPCTestCase{ + grantTestCases := []RestTestCase{ { "invalid granter address", fmt.Sprintf(grantURL, "invalid_granter", grantee1Addr, msgSendTypeURL), + http.StatusInternalServerError, bech32FailOutput, }, { "invalid grantee address", fmt.Sprintf(grantURL, granterAddr, "invalid_grantee", msgSendTypeURL), + http.StatusInternalServerError, bech32FailOutput, }, { "with empty granter", fmt.Sprintf(grantURL, "", grantee1Addr, msgSendTypeURL), + http.StatusInternalServerError, emptyStrOutput, }, { "with empty grantee", fmt.Sprintf(grantURL, granterAddr, "", msgSendTypeURL), + http.StatusInternalServerError, emptyStrOutput, }, { "invalid msg-type", fmt.Sprintf(grantURL, granterAddr, grantee1Addr, "invalidMsg"), + http.StatusInternalServerError, invalidMsgTypeOutput, }, { "valid grant query", fmt.Sprintf(grantURL, granterAddr, grantee1Addr, msgSendTypeURL), + http.StatusOK, expGrantOutput, }, } - RunGRPCQueries(t, grantTestCases) + RunRestQueries(t, grantTestCases) // test query grants grpc endpoint grantsURL := baseurl + "/cosmos/authz/v1beta1/grants?granter=%s&grantee=%s" - grantsTestCases := []GRPCTestCase{ + grantsTestCases := []RestTestCase{ { "expect single grant", fmt.Sprintf(grantsURL, granterAddr, grantee1Addr), + http.StatusOK, fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":null,"total":"1"}}`, grant1), }, { "expect two grants", fmt.Sprintf(grantsURL, granterAddr, grantee2Addr), + http.StatusOK, fmt.Sprintf(`{"grants":[{%s},{%s}],"pagination":{"next_key":null,"total":"2"}}`, grant2, grant3), }, { "expect single grant with pagination", fmt.Sprintf(grantsURL+"&pagination.limit=1", granterAddr, grantee2Addr), + http.StatusOK, fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":"L2Nvc21vcy5nb3YudjEuTXNnVm90ZQ==","total":"0"}}`, grant2), }, { "expect single grant with pagination limit and offset", fmt.Sprintf(grantsURL+"&pagination.limit=1&pagination.offset=1", granterAddr, grantee2Addr), + http.StatusOK, fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":null,"total":"0"}}`, grant3), }, { "expect two grants with pagination", fmt.Sprintf(grantsURL+"&pagination.limit=2", granterAddr, grantee2Addr), + http.StatusOK, fmt.Sprintf(`{"grants":[{%s},{%s}],"pagination":{"next_key":null,"total":"0"}}`, grant2, grant3), }, } - RunGRPCQueries(t, grantsTestCases) + RunRestQueries(t, grantsTestCases) // test query grants by granter grpc endpoint grantsByGranterURL := baseurl + "/cosmos/authz/v1beta1/grants/granter/%s" @@ -745,49 +757,55 @@ func TestAuthzGRPCQueries(t *testing.T) { granterQueryOutput := fmt.Sprintf(`{"grants":[{"granter":"%s","grantee":"%s",%s}],"pagination":{"next_key":null,"total":"1"}}`, grantee1Addr, grantee2Addr, grant4) - granterTestCases := []GRPCTestCase{ + granterTestCases := []RestTestCase{ { "invalid granter account address", fmt.Sprintf(grantsByGranterURL, "invalid address"), + http.StatusInternalServerError, decodingFailedOutput, }, { "no authorizations found from granter", fmt.Sprintf(grantsByGranterURL, grantee2Addr), + http.StatusOK, noAuthorizationsOutput, }, { "valid granter query", fmt.Sprintf(grantsByGranterURL, grantee1Addr), + http.StatusOK, granterQueryOutput, }, } - RunGRPCQueries(t, granterTestCases) + RunRestQueries(t, granterTestCases) // test query grants by grantee grpc endpoint grantsByGranteeURL := baseurl + "/cosmos/authz/v1beta1/grants/grantee/%s" grantee1GrantsOutput := fmt.Sprintf(`{"grants":[{"granter":"%s","grantee":"%s",%s}],"pagination":{"next_key":null,"total":"1"}}`, granterAddr, grantee1Addr, grant1) - granteeTestCases := []GRPCTestCase{ + granteeTestCases := []RestTestCase{ { "invalid grantee account address", fmt.Sprintf(grantsByGranteeURL, "invalid address"), + http.StatusInternalServerError, decodingFailedOutput, }, { "no authorizations found from grantee", fmt.Sprintf(grantsByGranteeURL, granterAddr), + http.StatusOK, noAuthorizationsOutput, }, { "valid grantee query", fmt.Sprintf(grantsByGranteeURL, grantee1Addr), + http.StatusOK, grantee1GrantsOutput, }, } - RunGRPCQueries(t, granteeTestCases) + RunRestQueries(t, granteeTestCases) } func setupChain(t *testing.T) (*CLIWrapper, string, string) { diff --git a/tests/systemtests/bank_test.go b/tests/systemtests/bank_test.go index 255e4bae9662..d776bc06a874 100644 --- a/tests/systemtests/bank_test.go +++ b/tests/systemtests/bank_test.go @@ -4,15 +4,13 @@ package systemtests import ( "fmt" + "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" "github.com/tidwall/sjson" - - "github.com/cosmos/cosmos-sdk/testutil" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) func TestBankSendTxCmd(t *testing.T) { @@ -53,7 +51,7 @@ func TestBankSendTxCmd(t *testing.T) { insufficientCmdArgs = append(insufficientCmdArgs, fmt.Sprintf("%d%s", valBalance, denom), "--fees=10stake") rsp = cli.Run(insufficientCmdArgs...) RequireTxFailure(t, rsp) - require.Contains(t, rsp, sdkerrors.ErrInsufficientFunds.Error()) + require.Contains(t, rsp, "insufficient funds") // test tx bank send with unauthorized signature assertUnauthorizedErr := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool { @@ -234,10 +232,11 @@ func TestBankGRPCQueries(t *testing.T) { blockHeight := sut.CurrentHeight() supplyTestCases := []struct { - name string - url string - headers map[string]string - expOut string + name string + url string + headers map[string]string + expHttpCode int + expOut string }{ { "test GRPC total supply", @@ -245,12 +244,14 @@ func TestBankGRPCQueries(t *testing.T) { map[string]string{ blockHeightHeader: fmt.Sprintf("%d", blockHeight), }, + http.StatusOK, expTotalSupplyOutput, }, { "test GRPC total supply of a specific denom", supplyUrl + "/by_denom?denom=" + newDenom, map[string]string{}, + http.StatusOK, specificDenomOutput, }, { @@ -259,67 +260,75 @@ func TestBankGRPCQueries(t *testing.T) { map[string]string{ blockHeightHeader: fmt.Sprintf("%d", blockHeight+5), }, + http.StatusInternalServerError, "invalid height", }, { "test GRPC total supply of a bogus denom", supplyUrl + "/by_denom?denom=foobar", map[string]string{}, + http.StatusOK, + // http.StatusNotFound, bogusDenomOutput, }, } for _, tc := range supplyTestCases { t.Run(tc.name, func(t *testing.T) { - resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) - require.NoError(t, err) + resp := GetRequestWithHeaders(t, tc.url, tc.headers, tc.expHttpCode) require.Contains(t, string(resp), tc.expOut) }) } // test denom metadata endpoint denomMetadataUrl := baseurl + "/cosmos/bank/v1beta1/denoms_metadata" - dmTestCases := []GRPCTestCase{ + dmTestCases := []RestTestCase{ { "test GRPC client metadata", denomMetadataUrl, + http.StatusOK, fmt.Sprintf(`{"metadatas":%s,"pagination":{"next_key":null,"total":"2"}}`, bankDenomMetadata), }, { "test GRPC client metadata of a specific denom", denomMetadataUrl + "/uatom", + http.StatusOK, fmt.Sprintf(`{"metadata":%s}`, atomDenomMetadata), }, { "test GRPC client metadata of a bogus denom", denomMetadataUrl + "/foobar", + http.StatusNotFound, `{"code":5, "message":"client metadata for denom foobar", "details":[]}`, }, } - RunGRPCQueries(t, dmTestCases) + RunRestQueries(t, dmTestCases) // test bank balances endpoint balanceUrl := baseurl + "/cosmos/bank/v1beta1/balances/" allBalancesOutput := `{"balances":[` + specificDenomOutput + `,{"denom":"stake","amount":"10000000"}],"pagination":{"next_key":null,"total":"2"}}` - balanceTestCases := []GRPCTestCase{ + balanceTestCases := []RestTestCase{ { "test GRPC total account balance", balanceUrl + account1Addr, + http.StatusOK, allBalancesOutput, }, { "test GRPC account balance of a specific denom", fmt.Sprintf("%s%s/by_denom?denom=%s", balanceUrl, account1Addr, newDenom), + http.StatusOK, fmt.Sprintf(`{"balance":%s}`, specificDenomOutput), }, { "test GRPC account balance of a bogus denom", fmt.Sprintf("%s%s/by_denom?denom=foobar", balanceUrl, account1Addr), + http.StatusOK, fmt.Sprintf(`{"balance":%s}`, bogusDenomOutput), }, } - RunGRPCQueries(t, balanceTestCases) + RunRestQueries(t, balanceTestCases) } diff --git a/tests/systemtests/fraud_test.go b/tests/systemtests/fraud_test.go index 043db59a545e..0479f526cc2d 100644 --- a/tests/systemtests/fraud_test.go +++ b/tests/systemtests/fraud_test.go @@ -35,8 +35,7 @@ func TestValidatorDoubleSign(t *testing.T) { newNode := sut.AddFullnode(t, func(nodeNumber int, nodePath string) { valKeyFile := filepath.Join(WorkDir, nodePath, "config", "priv_validator_key.json") _ = os.Remove(valKeyFile) - _, err := copyFile(filepath.Join(WorkDir, sut.nodePath(0), "config", "priv_validator_key.json"), valKeyFile) - require.NoError(t, err) + _ = MustCopyFile(filepath.Join(WorkDir, sut.nodePath(0), "config", "priv_validator_key.json"), valKeyFile) }) sut.AwaitNodeUp(t, fmt.Sprintf("http://%s:%d", newNode.IP, newNode.RPCPort)) diff --git a/tests/systemtests/gov_test.go b/tests/systemtests/gov_test.go index 73bc7fd96efc..092773dc2053 100644 --- a/tests/systemtests/gov_test.go +++ b/tests/systemtests/gov_test.go @@ -8,13 +8,14 @@ import ( "testing" "time" - "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/testutil" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestSubmitProposal(t *testing.T) { @@ -39,7 +40,8 @@ func TestSubmitProposal(t *testing.T) { "type": "Text", "deposit": "-324foocoin" }` - invalidPropFile := testutil.WriteToNewTempFile(t, invalidProp) + + invalidPropFile := StoreTempFile(t, []byte(invalidProp)) defer invalidPropFile.Close() // Create a valid new proposal JSON. @@ -62,7 +64,7 @@ func TestSubmitProposal(t *testing.T) { "metadata": "%s", "deposit": "%s" }`, govAddress, base64.StdEncoding.EncodeToString(propMetadata), sdk.NewCoin("stake", math.NewInt(100000))) - validPropFile := testutil.WriteToNewTempFile(t, validProp) + validPropFile := StoreTempFile(t, []byte(validProp)) defer validPropFile.Close() testCases := []struct { @@ -136,7 +138,7 @@ func TestSubmitLegacyProposal(t *testing.T) { "type": "Text", "deposit": "-324foocoin" }` - invalidPropFile := testutil.WriteToNewTempFile(t, invalidProp) + invalidPropFile := StoreTempFile(t, []byte(invalidProp)) defer invalidPropFile.Close() validProp := fmt.Sprintf(`{ @@ -145,7 +147,7 @@ func TestSubmitLegacyProposal(t *testing.T) { "type": "Text", "deposit": "%s" }`, sdk.NewCoin("stake", math.NewInt(154310))) - validPropFile := testutil.WriteToNewTempFile(t, validProp) + validPropFile := StoreTempFile(t, []byte(validProp)) defer validPropFile.Close() testCases := []struct { @@ -253,7 +255,8 @@ func TestNewCmdWeightedVote(t *testing.T) { fmt.Sprintf("--%s=%s", flags.FlagFrom, valAddr), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(10))).String())} + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(10))).String()), + } rsp := cli.Run(proposalArgs...) txResult, found := cli.AwaitTxCommitted(rsp) require.True(t, found) @@ -389,7 +392,8 @@ func TestQueryDeposit(t *testing.T) { fmt.Sprintf("--%s=%s", flags.FlagFrom, valAddr), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(10))).String())} + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(10))).String()), + } rsp := cli.Run(proposalArgs...) txResult, found := cli.AwaitTxCommitted(rsp) require.True(t, found) diff --git a/tests/systemtests/io_utils.go b/tests/systemtests/io_utils.go new file mode 100644 index 000000000000..a9cd1094fba6 --- /dev/null +++ b/tests/systemtests/io_utils.go @@ -0,0 +1,65 @@ +package systemtests + +import ( + "bytes" + "fmt" + "io" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +// MustCopyFile copies the file from the source path `src` to the destination path `dest` and returns an open file handle to `dest`. +func MustCopyFile(src, dest string) *os.File { + in, err := os.Open(src) + if err != nil { + panic(fmt.Sprintf("failed to open %q: %v", src, err)) + } + defer in.Close() + + out, err := os.Create(dest) + if err != nil { + panic(fmt.Sprintf("failed to create %q: %v", dest, err)) + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + panic(fmt.Sprintf("failed to copy from %q to %q: %v", src, dest, err)) + } + return out +} + +// MustCopyFilesInDir copies all files (excluding directories) from the source directory `src` to the destination directory `dest`. +func MustCopyFilesInDir(src, dest string) { + err := os.MkdirAll(dest, 0o750) + if err != nil { + panic(fmt.Sprintf("failed to create %q: %v", dest, err)) + } + + fs, err := os.ReadDir(src) + if err != nil { + panic(fmt.Sprintf("failed to read dir %q: %v", src, err)) + } + + for _, f := range fs { + if f.IsDir() { + continue + } + _ = MustCopyFile(filepath.Join(src, f.Name()), filepath.Join(dest, f.Name())) + } +} + +// StoreTempFile creates a temporary file in the test's temporary directory with the provided content. +// It returns a pointer to the created file. Errors during the process are handled with test assertions. +func StoreTempFile(t *testing.T, content []byte) *os.File { + t.Helper() + out, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + _, err = io.Copy(out, bytes.NewReader(content)) + require.NoError(t, err) + require.NoError(t, out.Close()) + return out +} diff --git a/tests/systemtests/rest_cli.go b/tests/systemtests/rest_cli.go index c53533c0786f..2ae879e1a242 100644 --- a/tests/systemtests/rest_cli.go +++ b/tests/systemtests/rest_cli.go @@ -1,29 +1,56 @@ package systemtests import ( + "io" + "net/http" "testing" "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/testutil" ) -type GRPCTestCase struct { - name string - url string - expOut string +type RestTestCase struct { + name string + url string + expCode int + expOut string } -// RunGRPCQueries runs given grpc testcases by making requests and +// RunRestQueries runs given Rest testcases by making requests and // checking response with expected output -func RunGRPCQueries(t *testing.T, testCases []GRPCTestCase) { +func RunRestQueries(t *testing.T, testCases []RestTestCase) { t.Helper() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - resp, err := testutil.GetRequest(tc.url) - require.NoError(t, err) + resp := GetRequestWithHeaders(t, tc.url, nil, tc.expCode) require.JSONEq(t, tc.expOut, string(resp)) }) } } + +func GetRequest(t *testing.T, url string) []byte { + t.Helper() + return GetRequestWithHeaders(t, url, nil, http.StatusOK) +} + +func GetRequestWithHeaders(t *testing.T, url string, headers map[string]string, expCode int) []byte { + t.Helper() + req, err := http.NewRequest("GET", url, nil) + require.NoError(t, err) + + for key, value := range headers { + req.Header.Set(key, value) + } + + httpClient := &http.Client{} + res, err := httpClient.Do(req) + require.NoError(t, err) + defer func() { + _ = res.Body.Close() + }() + require.Equal(t, expCode, res.StatusCode, "status code should be %d, got: %d", expCode, res.StatusCode) + + body, err := io.ReadAll(res.Body) + require.NoError(t, err) + return body +} diff --git a/tests/systemtests/snapshots_test.go b/tests/systemtests/snapshots_test.go index 42569a031799..d80eb530f413 100644 --- a/tests/systemtests/snapshots_test.go +++ b/tests/systemtests/snapshots_test.go @@ -4,13 +4,13 @@ package systemtests import ( "fmt" - "github.com/stretchr/testify/require" "os" "testing" + + "github.com/stretchr/testify/require" ) func TestSnapshots(t *testing.T) { - sut.ResetChain(t) cli := NewCLIWrapper(t, sut, verbose) sut.StartChain(t) diff --git a/tests/systemtests/system.go b/tests/systemtests/system.go index fd6a53781e9f..04755c782b3f 100644 --- a/tests/systemtests/system.go +++ b/tests/systemtests/system.go @@ -147,15 +147,11 @@ func (s *SystemUnderTest) SetupChain() { // backup genesis dest := filepath.Join(WorkDir, s.nodePath(0), "config", "genesis.json.orig") - if _, err := copyFile(src, dest); err != nil { - panic(fmt.Sprintf("copy failed :%#+v", err)) - } + MustCopyFile(src, dest) // backup keyring src = filepath.Join(WorkDir, s.nodePath(0), "keyring-test") dest = filepath.Join(WorkDir, s.outputDir, "keyring-test") - if err := copyFilesInDir(src, dest); err != nil { - panic(fmt.Sprintf("copy files from dir :%#+v", err)) - } + MustCopyFilesInDir(src, dest) } func (s *SystemUnderTest) StartChain(t *testing.T, xargs ...string) { @@ -485,7 +481,7 @@ func (s *SystemUnderTest) modifyGenesisJSON(t *testing.T, mutators ...GenesisMut for _, m := range mutators { current = m(current) } - out := storeTempFile(t, current) + out := StoreTempFile(t, current) defer os.Remove(out.Name()) s.setGenesis(t, out.Name()) s.MarkDirty() @@ -712,8 +708,7 @@ func (s *SystemUnderTest) AddFullnode(t *testing.T, beforeStart ...func(nodeNumb for _, tomlFile := range []string{"config.toml", "app.toml"} { configFile := filepath.Join(configPath, tomlFile) _ = os.Remove(configFile) - _, err := copyFile(filepath.Join(WorkDir, s.nodePath(0), "config", tomlFile), configFile) - require.NoError(t, err) + _ = MustCopyFile(filepath.Join(WorkDir, s.nodePath(0), "config", tomlFile), configFile) } // start node allNodes := s.AllNodes(t) @@ -949,54 +944,6 @@ func restoreOriginalKeyring(t *testing.T, s *SystemUnderTest) { require.NoError(t, os.RemoveAll(dest)) for i := 0; i < s.initialNodesCount; i++ { src := filepath.Join(WorkDir, s.nodePath(i), "keyring-test") - require.NoError(t, copyFilesInDir(src, dest)) - } -} - -// copyFile copy source file to dest file path -func copyFile(src, dest string) (*os.File, error) { - in, err := os.Open(src) - if err != nil { - return nil, err + MustCopyFilesInDir(src, dest) } - defer in.Close() - out, err := os.Create(dest) - if err != nil { - return nil, err - } - defer out.Close() - - _, err = io.Copy(out, in) - return out, err -} - -// copyFilesInDir copy files in src dir to dest path -func copyFilesInDir(src, dest string) error { - err := os.MkdirAll(dest, 0o750) - if err != nil { - return fmt.Errorf("mkdirs: %w", err) - } - fs, err := os.ReadDir(src) - if err != nil { - return fmt.Errorf("read dir: %w", err) - } - for _, f := range fs { - if f.IsDir() { - continue - } - if _, err := copyFile(filepath.Join(src, f.Name()), filepath.Join(dest, f.Name())); err != nil { - return fmt.Errorf("copy file: %q: %w", f.Name(), err) - } - } - return nil -} - -func storeTempFile(t *testing.T, content []byte) *os.File { - t.Helper() - out, err := os.CreateTemp(t.TempDir(), "genesis") - require.NoError(t, err) - _, err = io.Copy(out, bytes.NewReader(content)) - require.NoError(t, err) - require.NoError(t, out.Close()) - return out }