Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(systemtests): fix gRPC tests for v1 & v2 #22774

Merged
merged 6 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/v2/appmanager/appmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func (a appManager[T]) Query(ctx context.Context, version uint64, request transa
_, queryState, err = a.db.StateLatest()
}
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid height: %w", err)
}
return a.stf.Query(ctx, queryState, a.config.QueryGasLimit, request)
}
Expand Down
4 changes: 4 additions & 0 deletions systemtests/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Ref: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.j

## [Unreleased]

## [v1.0.0-rc.3] - 2024-12-05

* [#22774](https://github.com/cosmos/cosmos-sdk/pull/22774) Add greater than or equal support in Rest test suite

## [v1.0.0-rc.2] - 2024-11-26

* [#22577](https://github.com/cosmos/cosmos-sdk/pull/22577) Support invalid RPC response for CometBFT v1
Expand Down
99 changes: 79 additions & 20 deletions systemtests/rest_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
"testing"

"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/testutil"
)

type RestTestCase struct {
Name string
Url string
ExpCode int
ExpOut string
Name string
Url string
ExpCode int
ExpCodeGTE int
ExpOut string
}

// RunRestQueries runs given Rest testcases by making requests and
Expand All @@ -25,33 +24,71 @@

for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
resp := GetRequestWithHeaders(t, tc.Url, nil, tc.ExpCode)
if tc.ExpCodeGTE > 0 && tc.ExpCode > 0 {
require.Fail(t, "only one of ExpCode or ExpCodeGTE should be set")
}

var resp []byte
if tc.ExpCodeGTE > 0 {
resp = GetRequestWithHeadersGreaterThanOrEqual(t, tc.Url, nil, tc.ExpCodeGTE)
} else {
resp = GetRequestWithHeaders(t, tc.Url, nil, tc.ExpCode)
}
require.JSONEq(t, tc.ExpOut, string(resp))
})
}
}

// TestRestQueryIgnoreNumbers runs given rest testcases by making requests and
// RunRestQueriesIgnoreNumbers runs given rest testcases by making requests and
// checking response with expected output ignoring number values
// This method is used when number values in response are non-deterministic
func TestRestQueryIgnoreNumbers(t *testing.T, testCases ...RestTestCase) {
func RunRestQueriesIgnoreNumbers(t *testing.T, testCases ...RestTestCase) {
t.Helper()

// regex for standalone quoted numbers (e.g., "-3" or "0.02")
standaloneQuotedNumberRegex := regexp.MustCompile(`"(-?\d+(\.\d+)?)"`)
// regex for numbers in escaped strings (e.g., \"-3\")
escapedNumberRegex := regexp.MustCompile(`\\\"(-?\d+(\.\d+)?)\\\"`)
// regex for unquoted numbers (e.g., 2, -1, 0.02,)
unquotedNumberRegex := regexp.MustCompile(`\b-?\d+(\.\d+)?\b,`)

replaceNumber := func(input string) string {
// handle numbers in escaped strings
result := escapedNumberRegex.ReplaceAllStringFunc(input, func(match string) string {
// replace with escaped "NUMBER"
return `\"NUMBER\"`
})

// handle standalone quoted numbers
result = standaloneQuotedNumberRegex.ReplaceAllStringFunc(result, func(match string) string {
// replace with "NUMBER" (quotes preserved)
return `"NUMBER"`
})

// handle unquoted numbers
result = unquotedNumberRegex.ReplaceAllStringFunc(result, func(match string) string {
// replace with "NUMBER" (add quotes to ensure json validity)
return `"NUMBER",`
})

return result
}

for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
resp, err := testutil.GetRequest(tc.Url)
require.NoError(t, err)

// regular expression pattern to match any numeric value in the JSON
numberRegexPattern := `"\d+(\.\d+)?"`
if tc.ExpCodeGTE > 0 && tc.ExpCode > 0 {
require.Fail(t, "only one of ExpCode or ExpCodeGTE should be set")
}

// compile the regex
r, err := regexp.Compile(numberRegexPattern)
require.NoError(t, err)
var resp []byte
if tc.ExpCodeGTE > 0 {
resp = GetRequestWithHeadersGreaterThanOrEqual(t, tc.Url, nil, tc.ExpCodeGTE)
} else {
resp = GetRequestWithHeaders(t, tc.Url, nil, tc.ExpCode)
}

// replace all numeric values in the above JSONs with `NUMBER` text
expectedJSON := r.ReplaceAllString(tc.ExpOut, `"NUMBER"`)
actualJSON := r.ReplaceAllString(string(resp), `"NUMBER"`)
expectedJSON := replaceNumber(tc.ExpOut)
actualJSON := replaceNumber(string(resp))

// compare two jsons
require.JSONEq(t, expectedJSON, actualJSON)
Expand Down Expand Up @@ -85,3 +122,25 @@

return body
}

func GetRequestWithHeadersGreaterThanOrEqual(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)
}
Comment on lines +131 to +133

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism

httpClient := &http.Client{}
res, err := httpClient.Do(req)
require.NoError(t, err)
defer func() {
_ = res.Body.Close()
}()
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.GreaterOrEqual(t, res.StatusCode, expCode, "status code should be greater or equal to %d, got: %d, %s", expCode, res.StatusCode, body)

return body
}
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
142 changes: 71 additions & 71 deletions tests/systemtests/authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,78 +682,78 @@ func TestAuthzGRPCQueries(t *testing.T) {

grantTestCases := []systest.RestTestCase{
{
"invalid granter address",
fmt.Sprintf(grantURL, "invalid_granter", grantee1Addr, msgSendTypeURL),
http.StatusInternalServerError,
bech32FailOutput,
Name: "invalid granter address",
Url: fmt.Sprintf(grantURL, "invalid_granter", grantee1Addr, msgSendTypeURL),
ExpCodeGTE: http.StatusBadRequest,
ExpOut: bech32FailOutput,
},
{
"invalid grantee address",
fmt.Sprintf(grantURL, granterAddr, "invalid_grantee", msgSendTypeURL),
http.StatusInternalServerError,
bech32FailOutput,
Name: "invalid grantee address",
Url: fmt.Sprintf(grantURL, granterAddr, "invalid_grantee", msgSendTypeURL),
ExpCodeGTE: http.StatusBadRequest,
ExpOut: bech32FailOutput,
},
{
"with empty granter",
fmt.Sprintf(grantURL, "", grantee1Addr, msgSendTypeURL),
http.StatusInternalServerError,
emptyStrOutput,
Name: "with empty granter",
Url: fmt.Sprintf(grantURL, "", grantee1Addr, msgSendTypeURL),
ExpCodeGTE: http.StatusBadRequest,
ExpOut: emptyStrOutput,
},
{
"with empty grantee",
fmt.Sprintf(grantURL, granterAddr, "", msgSendTypeURL),
http.StatusInternalServerError,
emptyStrOutput,
Name: "with empty grantee",
Url: fmt.Sprintf(grantURL, granterAddr, "", msgSendTypeURL),
ExpCodeGTE: http.StatusBadRequest,
ExpOut: emptyStrOutput,
},
{
"invalid msg-type",
fmt.Sprintf(grantURL, granterAddr, grantee1Addr, "invalidMsg"),
http.StatusInternalServerError,
invalidMsgTypeOutput,
Name: "invalid msg-type",
Url: fmt.Sprintf(grantURL, granterAddr, grantee1Addr, "invalidMsg"),
ExpCode: http.StatusInternalServerError,
ExpOut: invalidMsgTypeOutput,
},
{
"valid grant query",
fmt.Sprintf(grantURL, granterAddr, grantee1Addr, msgSendTypeURL),
http.StatusOK,
expGrantOutput,
Name: "valid grant query",
Url: fmt.Sprintf(grantURL, granterAddr, grantee1Addr, msgSendTypeURL),
ExpCode: http.StatusOK,
ExpOut: expGrantOutput,
},
}

systest.RunRestQueries(t, grantTestCases...)
systest.RunRestQueriesIgnoreNumbers(t, grantTestCases...)

// test query grants grpc endpoint
grantsURL := baseurl + "/cosmos/authz/v1beta1/grants?granter=%s&grantee=%s"

grantsTestCases := []systest.RestTestCase{
{
"expect single grant",
fmt.Sprintf(grantsURL, granterAddr, grantee1Addr),
http.StatusOK,
fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":null,"total":"1"}}`, grant1),
Name: "expect single grant",
Url: fmt.Sprintf(grantsURL, granterAddr, grantee1Addr),
ExpCode: http.StatusOK,
ExpOut: 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),
Name: "expect two grants",
Url: fmt.Sprintf(grantsURL, granterAddr, grantee2Addr),
ExpCode: http.StatusOK,
ExpOut: 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),
Name: "expect single grant with pagination",
Url: fmt.Sprintf(grantsURL+"&pagination.limit=1", granterAddr, grantee2Addr),
ExpCode: http.StatusOK,
ExpOut: 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),
Name: "expect single grant with pagination limit and offset",
Url: fmt.Sprintf(grantsURL+"&pagination.limit=1&pagination.offset=1", granterAddr, grantee2Addr),
ExpCode: http.StatusOK,
ExpOut: 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),
Name: "expect two grants with pagination",
Url: fmt.Sprintf(grantsURL+"&pagination.limit=2", granterAddr, grantee2Addr),
ExpCode: http.StatusOK,
ExpOut: fmt.Sprintf(`{"grants":[{%s},{%s}],"pagination":{"next_key":null,"total":"0"}}`, grant2, grant3),
},
}

Expand All @@ -768,53 +768,53 @@ func TestAuthzGRPCQueries(t *testing.T) {

granterTestCases := []systest.RestTestCase{
{
"invalid granter account address",
fmt.Sprintf(grantsByGranterURL, "invalid address"),
http.StatusInternalServerError,
decodingFailedOutput,
Name: "invalid granter account address",
Url: fmt.Sprintf(grantsByGranterURL, "invalid address"),
ExpCodeGTE: http.StatusBadRequest,
ExpOut: decodingFailedOutput,
},
{
"no authorizations found from granter",
fmt.Sprintf(grantsByGranterURL, grantee2Addr),
http.StatusOK,
noAuthorizationsOutput,
Name: "no authorizations found from granter",
Url: fmt.Sprintf(grantsByGranterURL, grantee2Addr),
ExpCode: http.StatusOK,
ExpOut: noAuthorizationsOutput,
},
{
"valid granter query",
fmt.Sprintf(grantsByGranterURL, grantee1Addr),
http.StatusOK,
granterQueryOutput,
Name: "valid granter query",
Url: fmt.Sprintf(grantsByGranterURL, grantee1Addr),
ExpCode: http.StatusOK,
ExpOut: granterQueryOutput,
},
}

systest.RunRestQueries(t, granterTestCases...)
systest.RunRestQueriesIgnoreNumbers(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 := []systest.RestTestCase{
{
"invalid grantee account address",
fmt.Sprintf(grantsByGranteeURL, "invalid address"),
http.StatusInternalServerError,
decodingFailedOutput,
Name: "invalid grantee account address",
Url: fmt.Sprintf(grantsByGranteeURL, "invalid address"),
ExpCodeGTE: http.StatusBadRequest,
ExpOut: decodingFailedOutput,
},
{
"no authorizations found from grantee",
fmt.Sprintf(grantsByGranteeURL, granterAddr),
http.StatusOK,
noAuthorizationsOutput,
Name: "no authorizations found from grantee",
Url: fmt.Sprintf(grantsByGranteeURL, granterAddr),
ExpCode: http.StatusOK,
ExpOut: noAuthorizationsOutput,
},
{
"valid grantee query",
fmt.Sprintf(grantsByGranteeURL, grantee1Addr),
http.StatusOK,
grantee1GrantsOutput,
Name: "valid grantee query",
Url: fmt.Sprintf(grantsByGranteeURL, grantee1Addr),
ExpCode: http.StatusOK,
ExpOut: grantee1GrantsOutput,
},
}

systest.RunRestQueries(t, granteeTestCases...)
systest.RunRestQueriesIgnoreNumbers(t, granteeTestCases...)
}

func setupChain(t *testing.T) (*systest.CLIWrapper, string, string) {
Expand Down
Loading
Loading