Skip to content

Commit 4caac04

Browse files
authored
test(systemtests): fix gRPC tests for v1 & v2 (#22774)
1 parent 3a2a0ac commit 4caac04

File tree

8 files changed

+233
-170
lines changed

8 files changed

+233
-170
lines changed

server/v2/appmanager/appmanager.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func (a appManager[T]) Query(ctx context.Context, version uint64, request transa
230230
_, queryState, err = a.db.StateLatest()
231231
}
232232
if err != nil {
233-
return nil, err
233+
return nil, fmt.Errorf("invalid height: %w", err)
234234
}
235235
return a.stf.Query(ctx, queryState, a.config.QueryGasLimit, request)
236236
}

systemtests/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Ref: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.j
3636

3737
## [Unreleased]
3838

39+
## [v1.0.0-rc.3] - 2024-12-05
40+
41+
* [#22774](https://github.com/cosmos/cosmos-sdk/pull/22774) Add greater than or equal support in Rest test suite
42+
3943
## [v1.0.0-rc.2] - 2024-11-26
4044

4145
* [#22577](https://github.com/cosmos/cosmos-sdk/pull/22577) Support invalid RPC response for CometBFT v1

systemtests/rest_support.go

+79-20
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ import (
77
"testing"
88

99
"github.com/stretchr/testify/require"
10-
11-
"github.com/cosmos/cosmos-sdk/testutil"
1210
)
1311

1412
type RestTestCase struct {
15-
Name string
16-
Url string
17-
ExpCode int
18-
ExpOut string
13+
Name string
14+
Url string
15+
ExpCode int
16+
ExpCodeGTE int
17+
ExpOut string
1918
}
2019

2120
// RunRestQueries runs given Rest testcases by making requests and
@@ -25,33 +24,71 @@ func RunRestQueries(t *testing.T, testCases ...RestTestCase) {
2524

2625
for _, tc := range testCases {
2726
t.Run(tc.Name, func(t *testing.T) {
28-
resp := GetRequestWithHeaders(t, tc.Url, nil, tc.ExpCode)
27+
if tc.ExpCodeGTE > 0 && tc.ExpCode > 0 {
28+
require.Fail(t, "only one of ExpCode or ExpCodeGTE should be set")
29+
}
30+
31+
var resp []byte
32+
if tc.ExpCodeGTE > 0 {
33+
resp = GetRequestWithHeadersGreaterThanOrEqual(t, tc.Url, nil, tc.ExpCodeGTE)
34+
} else {
35+
resp = GetRequestWithHeaders(t, tc.Url, nil, tc.ExpCode)
36+
}
2937
require.JSONEq(t, tc.ExpOut, string(resp))
3038
})
3139
}
3240
}
3341

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

48+
// regex for standalone quoted numbers (e.g., "-3" or "0.02")
49+
standaloneQuotedNumberRegex := regexp.MustCompile(`"(-?\d+(\.\d+)?)"`)
50+
// regex for numbers in escaped strings (e.g., \"-3\")
51+
escapedNumberRegex := regexp.MustCompile(`\\\"(-?\d+(\.\d+)?)\\\"`)
52+
// regex for unquoted numbers (e.g., 2, -1, 0.02,)
53+
unquotedNumberRegex := regexp.MustCompile(`\b-?\d+(\.\d+)?\b,`)
54+
55+
replaceNumber := func(input string) string {
56+
// handle numbers in escaped strings
57+
result := escapedNumberRegex.ReplaceAllStringFunc(input, func(match string) string {
58+
// replace with escaped "NUMBER"
59+
return `\"NUMBER\"`
60+
})
61+
62+
// handle standalone quoted numbers
63+
result = standaloneQuotedNumberRegex.ReplaceAllStringFunc(result, func(match string) string {
64+
// replace with "NUMBER" (quotes preserved)
65+
return `"NUMBER"`
66+
})
67+
68+
// handle unquoted numbers
69+
result = unquotedNumberRegex.ReplaceAllStringFunc(result, func(match string) string {
70+
// replace with "NUMBER" (add quotes to ensure json validity)
71+
return `"NUMBER",`
72+
})
73+
74+
return result
75+
}
76+
4077
for _, tc := range testCases {
4178
t.Run(tc.Name, func(t *testing.T) {
42-
resp, err := testutil.GetRequest(tc.Url)
43-
require.NoError(t, err)
44-
45-
// regular expression pattern to match any numeric value in the JSON
46-
numberRegexPattern := `"\d+(\.\d+)?"`
79+
if tc.ExpCodeGTE > 0 && tc.ExpCode > 0 {
80+
require.Fail(t, "only one of ExpCode or ExpCodeGTE should be set")
81+
}
4782

48-
// compile the regex
49-
r, err := regexp.Compile(numberRegexPattern)
50-
require.NoError(t, err)
83+
var resp []byte
84+
if tc.ExpCodeGTE > 0 {
85+
resp = GetRequestWithHeadersGreaterThanOrEqual(t, tc.Url, nil, tc.ExpCodeGTE)
86+
} else {
87+
resp = GetRequestWithHeaders(t, tc.Url, nil, tc.ExpCode)
88+
}
5189

52-
// replace all numeric values in the above JSONs with `NUMBER` text
53-
expectedJSON := r.ReplaceAllString(tc.ExpOut, `"NUMBER"`)
54-
actualJSON := r.ReplaceAllString(string(resp), `"NUMBER"`)
90+
expectedJSON := replaceNumber(tc.ExpOut)
91+
actualJSON := replaceNumber(string(resp))
5592

5693
// compare two jsons
5794
require.JSONEq(t, expectedJSON, actualJSON)
@@ -85,3 +122,25 @@ func GetRequestWithHeaders(t *testing.T, url string, headers map[string]string,
85122

86123
return body
87124
}
125+
126+
func GetRequestWithHeadersGreaterThanOrEqual(t *testing.T, url string, headers map[string]string, expCode int) []byte {
127+
t.Helper()
128+
req, err := http.NewRequest("GET", url, nil)
129+
require.NoError(t, err)
130+
131+
for key, value := range headers {
132+
req.Header.Set(key, value)
133+
}
134+
135+
httpClient := &http.Client{}
136+
res, err := httpClient.Do(req)
137+
require.NoError(t, err)
138+
defer func() {
139+
_ = res.Body.Close()
140+
}()
141+
body, err := io.ReadAll(res.Body)
142+
require.NoError(t, err)
143+
require.GreaterOrEqual(t, res.StatusCode, expCode, "status code should be greater or equal to %d, got: %d, %s", expCode, res.StatusCode, body)
144+
145+
return body
146+
}

tests/systemtests/authz_test.go

+71-71
Original file line numberDiff line numberDiff line change
@@ -682,78 +682,78 @@ func TestAuthzGRPCQueries(t *testing.T) {
682682

683683
grantTestCases := []systest.RestTestCase{
684684
{
685-
"invalid granter address",
686-
fmt.Sprintf(grantURL, "invalid_granter", grantee1Addr, msgSendTypeURL),
687-
http.StatusInternalServerError,
688-
bech32FailOutput,
685+
Name: "invalid granter address",
686+
Url: fmt.Sprintf(grantURL, "invalid_granter", grantee1Addr, msgSendTypeURL),
687+
ExpCodeGTE: http.StatusBadRequest,
688+
ExpOut: bech32FailOutput,
689689
},
690690
{
691-
"invalid grantee address",
692-
fmt.Sprintf(grantURL, granterAddr, "invalid_grantee", msgSendTypeURL),
693-
http.StatusInternalServerError,
694-
bech32FailOutput,
691+
Name: "invalid grantee address",
692+
Url: fmt.Sprintf(grantURL, granterAddr, "invalid_grantee", msgSendTypeURL),
693+
ExpCodeGTE: http.StatusBadRequest,
694+
ExpOut: bech32FailOutput,
695695
},
696696
{
697-
"with empty granter",
698-
fmt.Sprintf(grantURL, "", grantee1Addr, msgSendTypeURL),
699-
http.StatusInternalServerError,
700-
emptyStrOutput,
697+
Name: "with empty granter",
698+
Url: fmt.Sprintf(grantURL, "", grantee1Addr, msgSendTypeURL),
699+
ExpCodeGTE: http.StatusBadRequest,
700+
ExpOut: emptyStrOutput,
701701
},
702702
{
703-
"with empty grantee",
704-
fmt.Sprintf(grantURL, granterAddr, "", msgSendTypeURL),
705-
http.StatusInternalServerError,
706-
emptyStrOutput,
703+
Name: "with empty grantee",
704+
Url: fmt.Sprintf(grantURL, granterAddr, "", msgSendTypeURL),
705+
ExpCodeGTE: http.StatusBadRequest,
706+
ExpOut: emptyStrOutput,
707707
},
708708
{
709-
"invalid msg-type",
710-
fmt.Sprintf(grantURL, granterAddr, grantee1Addr, "invalidMsg"),
711-
http.StatusInternalServerError,
712-
invalidMsgTypeOutput,
709+
Name: "invalid msg-type",
710+
Url: fmt.Sprintf(grantURL, granterAddr, grantee1Addr, "invalidMsg"),
711+
ExpCode: http.StatusInternalServerError,
712+
ExpOut: invalidMsgTypeOutput,
713713
},
714714
{
715-
"valid grant query",
716-
fmt.Sprintf(grantURL, granterAddr, grantee1Addr, msgSendTypeURL),
717-
http.StatusOK,
718-
expGrantOutput,
715+
Name: "valid grant query",
716+
Url: fmt.Sprintf(grantURL, granterAddr, grantee1Addr, msgSendTypeURL),
717+
ExpCode: http.StatusOK,
718+
ExpOut: expGrantOutput,
719719
},
720720
}
721721

722-
systest.RunRestQueries(t, grantTestCases...)
722+
systest.RunRestQueriesIgnoreNumbers(t, grantTestCases...)
723723

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

727727
grantsTestCases := []systest.RestTestCase{
728728
{
729-
"expect single grant",
730-
fmt.Sprintf(grantsURL, granterAddr, grantee1Addr),
731-
http.StatusOK,
732-
fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":null,"total":"1"}}`, grant1),
729+
Name: "expect single grant",
730+
Url: fmt.Sprintf(grantsURL, granterAddr, grantee1Addr),
731+
ExpCode: http.StatusOK,
732+
ExpOut: fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":null,"total":"1"}}`, grant1),
733733
},
734734
{
735-
"expect two grants",
736-
fmt.Sprintf(grantsURL, granterAddr, grantee2Addr),
737-
http.StatusOK,
738-
fmt.Sprintf(`{"grants":[{%s},{%s}],"pagination":{"next_key":null,"total":"2"}}`, grant2, grant3),
735+
Name: "expect two grants",
736+
Url: fmt.Sprintf(grantsURL, granterAddr, grantee2Addr),
737+
ExpCode: http.StatusOK,
738+
ExpOut: fmt.Sprintf(`{"grants":[{%s},{%s}],"pagination":{"next_key":null,"total":"2"}}`, grant2, grant3),
739739
},
740740
{
741-
"expect single grant with pagination",
742-
fmt.Sprintf(grantsURL+"&pagination.limit=1", granterAddr, grantee2Addr),
743-
http.StatusOK,
744-
fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":"L2Nvc21vcy5nb3YudjEuTXNnVm90ZQ==","total":"0"}}`, grant2),
741+
Name: "expect single grant with pagination",
742+
Url: fmt.Sprintf(grantsURL+"&pagination.limit=1", granterAddr, grantee2Addr),
743+
ExpCode: http.StatusOK,
744+
ExpOut: fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":"L2Nvc21vcy5nb3YudjEuTXNnVm90ZQ==","total":"0"}}`, grant2),
745745
},
746746
{
747-
"expect single grant with pagination limit and offset",
748-
fmt.Sprintf(grantsURL+"&pagination.limit=1&pagination.offset=1", granterAddr, grantee2Addr),
749-
http.StatusOK,
750-
fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":null,"total":"0"}}`, grant3),
747+
Name: "expect single grant with pagination limit and offset",
748+
Url: fmt.Sprintf(grantsURL+"&pagination.limit=1&pagination.offset=1", granterAddr, grantee2Addr),
749+
ExpCode: http.StatusOK,
750+
ExpOut: fmt.Sprintf(`{"grants":[{%s}],"pagination":{"next_key":null,"total":"0"}}`, grant3),
751751
},
752752
{
753-
"expect two grants with pagination",
754-
fmt.Sprintf(grantsURL+"&pagination.limit=2", granterAddr, grantee2Addr),
755-
http.StatusOK,
756-
fmt.Sprintf(`{"grants":[{%s},{%s}],"pagination":{"next_key":null,"total":"0"}}`, grant2, grant3),
753+
Name: "expect two grants with pagination",
754+
Url: fmt.Sprintf(grantsURL+"&pagination.limit=2", granterAddr, grantee2Addr),
755+
ExpCode: http.StatusOK,
756+
ExpOut: fmt.Sprintf(`{"grants":[{%s},{%s}],"pagination":{"next_key":null,"total":"0"}}`, grant2, grant3),
757757
},
758758
}
759759

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

769769
granterTestCases := []systest.RestTestCase{
770770
{
771-
"invalid granter account address",
772-
fmt.Sprintf(grantsByGranterURL, "invalid address"),
773-
http.StatusInternalServerError,
774-
decodingFailedOutput,
771+
Name: "invalid granter account address",
772+
Url: fmt.Sprintf(grantsByGranterURL, "invalid address"),
773+
ExpCodeGTE: http.StatusBadRequest,
774+
ExpOut: decodingFailedOutput,
775775
},
776776
{
777-
"no authorizations found from granter",
778-
fmt.Sprintf(grantsByGranterURL, grantee2Addr),
779-
http.StatusOK,
780-
noAuthorizationsOutput,
777+
Name: "no authorizations found from granter",
778+
Url: fmt.Sprintf(grantsByGranterURL, grantee2Addr),
779+
ExpCode: http.StatusOK,
780+
ExpOut: noAuthorizationsOutput,
781781
},
782782
{
783-
"valid granter query",
784-
fmt.Sprintf(grantsByGranterURL, grantee1Addr),
785-
http.StatusOK,
786-
granterQueryOutput,
783+
Name: "valid granter query",
784+
Url: fmt.Sprintf(grantsByGranterURL, grantee1Addr),
785+
ExpCode: http.StatusOK,
786+
ExpOut: granterQueryOutput,
787787
},
788788
}
789789

790-
systest.RunRestQueries(t, granterTestCases...)
790+
systest.RunRestQueriesIgnoreNumbers(t, granterTestCases...)
791791

792792
// test query grants by grantee grpc endpoint
793793
grantsByGranteeURL := baseurl + "/cosmos/authz/v1beta1/grants/grantee/%s"
794794
grantee1GrantsOutput := fmt.Sprintf(`{"grants":[{"granter":"%s","grantee":"%s",%s}],"pagination":{"next_key":null,"total":"1"}}`, granterAddr, grantee1Addr, grant1)
795795

796796
granteeTestCases := []systest.RestTestCase{
797797
{
798-
"invalid grantee account address",
799-
fmt.Sprintf(grantsByGranteeURL, "invalid address"),
800-
http.StatusInternalServerError,
801-
decodingFailedOutput,
798+
Name: "invalid grantee account address",
799+
Url: fmt.Sprintf(grantsByGranteeURL, "invalid address"),
800+
ExpCodeGTE: http.StatusBadRequest,
801+
ExpOut: decodingFailedOutput,
802802
},
803803
{
804-
"no authorizations found from grantee",
805-
fmt.Sprintf(grantsByGranteeURL, granterAddr),
806-
http.StatusOK,
807-
noAuthorizationsOutput,
804+
Name: "no authorizations found from grantee",
805+
Url: fmt.Sprintf(grantsByGranteeURL, granterAddr),
806+
ExpCode: http.StatusOK,
807+
ExpOut: noAuthorizationsOutput,
808808
},
809809
{
810-
"valid grantee query",
811-
fmt.Sprintf(grantsByGranteeURL, grantee1Addr),
812-
http.StatusOK,
813-
grantee1GrantsOutput,
810+
Name: "valid grantee query",
811+
Url: fmt.Sprintf(grantsByGranteeURL, grantee1Addr),
812+
ExpCode: http.StatusOK,
813+
ExpOut: grantee1GrantsOutput,
814814
},
815815
}
816816

817-
systest.RunRestQueries(t, granteeTestCases...)
817+
systest.RunRestQueriesIgnoreNumbers(t, granteeTestCases...)
818818
}
819819

820820
func setupChain(t *testing.T) (*systest.CLIWrapper, string, string) {

0 commit comments

Comments
 (0)