test: migrate e2e/distribution to system tests (backport #21908) (#22133)

Co-authored-by: Akhil Kumar P <36399231+akhilkumarpilli@users.noreply.github.com>
This commit is contained in:
mergify[bot]
2024-10-04 17:46:34 +02:00
committed by GitHub
co-authored by Akhil Kumar P
parent 10c19a2592
commit aead233e24
7 changed files with 340 additions and 620 deletions
+1 -1
View File
@@ -607,7 +607,7 @@ func TestAuthzExecRedelegateAuthorization(t *testing.T) {
msgRedelegateTypeURL, granterAddr, val1Addr, val2Addr, testDenom, redelegationAmount)
execMsg := WriteToTempJSONFile(t, redelegateTx)
redelegateCmd := []string{"tx", "authz", "exec", execMsg.Name(), "--from=" + granteeAddr, "--gas=auto"}
redelegateCmd := []string{"tx", "authz", "exec", execMsg.Name(), "--from=" + granteeAddr, "--gas=500000", "--fees=10stake"}
rsp = cli.RunAndWait(redelegateCmd...)
RequireTxSuccess(t, rsp)
+301
View File
@@ -0,0 +1,301 @@
//go:build system_test
package systemtests
import (
"fmt"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
const (
distrTestDenom = "stake"
)
func TestWithdrawAllRewardsCmd(t *testing.T) {
// scenario: test distribution withdraw all rewards command
// given a running chain
sut.ResetChain(t)
cli := NewCLIWrapper(t, sut, verbose)
newAddr := cli.AddKey("newAddr")
require.NotEmpty(t, newAddr)
var initialAmount int64 = 10000000
initialBalance := fmt.Sprintf("%d%s", initialAmount, distrTestDenom)
sut.ModifyGenesisCLI(t,
[]string{"genesis", "add-genesis-account", newAddr, initialBalance},
)
sut.StartChain(t)
// query balance
newAddrBal := cli.QueryBalance(newAddr, distrTestDenom)
require.Equal(t, initialAmount, newAddrBal)
// query validator operator address
rsp := cli.CustomQuery("q", "staking", "validators")
validators := gjson.Get(rsp, "validators.#.operator_address").Array()
require.GreaterOrEqual(t, len(validators), 2)
val1Addr := validators[0].String()
val2Addr := validators[1].String()
var delegationAmount int64 = 100000
delegation := fmt.Sprintf("%d%s", delegationAmount, distrTestDenom)
// delegate tokens to validator1
rsp = cli.RunAndWait("tx", "staking", "delegate", val1Addr, delegation, "--from="+newAddr, "--fees=1"+distrTestDenom)
RequireTxSuccess(t, rsp)
// delegate tokens to validator2
rsp = cli.RunAndWait("tx", "staking", "delegate", val2Addr, delegation, "--from="+newAddr, "--fees=1"+distrTestDenom)
RequireTxSuccess(t, rsp)
// check updated balance: newAddrBal - delegatedBal - fees
expBal := newAddrBal - (delegationAmount * 2) - 2
newAddrBal = cli.QueryBalance(newAddr, distrTestDenom)
require.Equal(t, expBal, newAddrBal)
withdrawCmdArgs := []string{"tx", "distribution", "withdraw-all-rewards", "--from=" + newAddr, "--fees=1" + distrTestDenom}
// test with --max-msgs
testCases := []struct {
name string
maxMsgs int
expTxLen int
}{
{
"--max-msgs value is 1",
1,
2,
},
{
"--max-msgs value is 2",
2,
1,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assertGenOnlyOutput := func(_ assert.TestingT, gotErr error, gotOutputs ...interface{}) bool {
require.Len(t, gotOutputs, 1)
// gets output combining two objects without any space or new line
splitOutput := strings.Split(gotOutputs[0].(string), "}{")
require.Len(t, splitOutput, tc.expTxLen)
return false
}
cmd := append(withdrawCmdArgs, fmt.Sprintf("--max-msgs=%d", tc.maxMsgs), "--generate-only")
_ = cli.WithRunErrorMatcher(assertGenOnlyOutput).Run(cmd...)
})
}
// test withdraw-all-rewards transaction
rsp = cli.RunAndWait(withdrawCmdArgs...)
RequireTxSuccess(t, rsp)
}
func TestDistrValidatorGRPCQueries(t *testing.T) {
// scenario: test distribution validator grpc gateway queries
// given a running chain
sut.ResetChain(t)
cli := NewCLIWrapper(t, sut, verbose)
// get validator address
valAddr := cli.GetKeyAddr("node0")
require.NotEmpty(t, valAddr)
valOperAddr := cli.GetKeyAddrPrefix("node0", "val")
require.NotEmpty(t, valOperAddr)
sut.StartChain(t)
sut.AwaitNBlocks(t, 3)
baseurl := sut.APIAddress()
expectedAmountOutput := fmt.Sprintf(`{"denom":"%s","amount":"203.105000000000000000"}`, distrTestDenom)
// test params grpc endpoint
paramsURL := baseurl + "/cosmos/distribution/v1beta1/params"
paramsTestCases := []RestTestCase{
{
"gRPC request params",
paramsURL,
http.StatusOK,
`{"params":{"community_tax":"0.020000000000000000","base_proposer_reward":"0.000000000000000000","bonus_proposer_reward":"0.000000000000000000","withdraw_addr_enabled":true}}`,
},
}
RunRestQueries(t, paramsTestCases)
// test validator distribution info grpc endpoint
validatorsURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s`
validatorsOutput := fmt.Sprintf(`{"operator_address":"%s","self_bond_rewards":[],"commission":[%s]}`, valAddr, expectedAmountOutput)
validatorsTestCases := []RestTestCase{
{
"gRPC request validator with valid validator address",
fmt.Sprintf(validatorsURL, valOperAddr),
http.StatusOK,
validatorsOutput,
},
}
TestRestQueryIgnoreNumbers(t, validatorsTestCases)
// test outstanding rewards grpc endpoint
outstandingRewardsURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s/outstanding_rewards`
rewardsTestCases := []RestTestCase{
{
"gRPC request outstanding rewards with valid validator address",
fmt.Sprintf(outstandingRewardsURL, valOperAddr),
http.StatusOK,
fmt.Sprintf(`{"rewards":{"rewards":[%s]}}`, expectedAmountOutput),
},
}
TestRestQueryIgnoreNumbers(t, rewardsTestCases)
// test validator commission grpc endpoint
commissionURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s/commission`
commissionTestCases := []RestTestCase{
{
"gRPC request commission with valid validator address",
fmt.Sprintf(commissionURL, valOperAddr),
http.StatusOK,
fmt.Sprintf(`{"commission":{"commission":[%s]}}`, expectedAmountOutput),
},
}
TestRestQueryIgnoreNumbers(t, commissionTestCases)
// test validator slashes grpc endpoint
slashURL := baseurl + `/cosmos/distribution/v1beta1/validators/%s/slashes`
invalidHeightOutput := `{"code":3, "message":"strconv.ParseUint: parsing \"-3\": invalid syntax", "details":[]}`
slashTestCases := []RestTestCase{
{
"invalid start height",
fmt.Sprintf(slashURL+`?starting_height=%s&ending_height=%s`, valOperAddr, "-3", "3"),
http.StatusBadRequest,
invalidHeightOutput,
},
{
"invalid end height",
fmt.Sprintf(slashURL+`?starting_height=%s&ending_height=%s`, valOperAddr, "1", "-3"),
http.StatusBadRequest,
invalidHeightOutput,
},
{
"valid request get slashes",
fmt.Sprintf(slashURL+`?starting_height=%s&ending_height=%s`, valOperAddr, "1", "3"),
http.StatusOK,
`{"slashes":[],"pagination":{"next_key":null,"total":"0"}}`,
},
}
RunRestQueries(t, slashTestCases)
}
func TestDistrDelegatorGRPCQueries(t *testing.T) {
// scenario: test distribution validator gsrpc gateway queries
// given a running chain
sut.ResetChain(t)
cli := NewCLIWrapper(t, sut, verbose)
// get validator address
valAddr := cli.GetKeyAddr("node0")
require.NotEmpty(t, valAddr)
valOperAddr := cli.GetKeyAddrPrefix("node0", "val")
require.NotEmpty(t, valOperAddr)
// update commission rate of node0 validator
// generate new gentx and copy it to genesis.json before starting network
rsp := cli.RunCommandWithArgs("genesis", "gentx", "node0", "100000000"+distrTestDenom, "--chain-id="+cli.chainID, "--commission-rate=0.01", "--home", sut.nodePath(0), "--keyring-backend=test")
// extract gentx path from above command output
re := regexp.MustCompile(`"(.*?\.json)"`)
match := re.FindStringSubmatch(rsp)
require.GreaterOrEqual(t, len(match), 1)
updatedGentx := filepath.Join(WorkDir, match[1])
updatedGentxBz, err := os.ReadFile(updatedGentx) // #nosec G304
require.NoError(t, err)
sut.ModifyGenesisJSON(t, func(genesis []byte) []byte {
state, err := sjson.SetRawBytes(genesis, "app_state.genutil.gen_txs.0", updatedGentxBz)
require.NoError(t, err)
return state
})
// create new address which will be used as delegator address
delAddr := cli.AddKey("delAddr")
require.NotEmpty(t, delAddr)
var initialAmount int64 = 1000000000
initialBalance := fmt.Sprintf("%d%s", initialAmount, distrTestDenom)
sut.ModifyGenesisCLI(t,
[]string{"genesis", "add-genesis-account", delAddr, initialBalance},
)
sut.StartChain(t)
// delegate some tokens to valOperAddr
rsp = cli.RunAndWait("tx", "staking", "delegate", valOperAddr, "100000000"+distrTestDenom, "--from="+delAddr)
RequireTxSuccess(t, rsp)
sut.AwaitNBlocks(t, 5)
baseurl := sut.APIAddress()
// test delegator rewards grpc endpoint
delegatorRewardsURL := baseurl + `/cosmos/distribution/v1beta1/delegators/%s/rewards`
expectedAmountOutput := `{"denom":"stake","amount":"0.121275000000000000"}`
rewardsOutput := fmt.Sprintf(`{"rewards":[{"validator_address":"%s","reward":[%s]}],"total":[%s]}`, valOperAddr, expectedAmountOutput, expectedAmountOutput)
delegatorRewardsTestCases := []RestTestCase{
{
"valid rewards request with valid delegator address",
fmt.Sprintf(delegatorRewardsURL, delAddr),
http.StatusOK,
rewardsOutput,
},
{
"valid request(specific validator rewards)",
fmt.Sprintf(delegatorRewardsURL+`/%s`, delAddr, valOperAddr),
http.StatusOK,
fmt.Sprintf(`{"rewards":[%s]}`, expectedAmountOutput),
},
}
TestRestQueryIgnoreNumbers(t, delegatorRewardsTestCases)
// test delegator validators grpc endpoint
delegatorValsURL := baseurl + `/cosmos/distribution/v1beta1/delegators/%s/validators`
valsTestCases := []RestTestCase{
{
"gRPC request delegator validators with valid delegator address",
fmt.Sprintf(delegatorValsURL, delAddr),
http.StatusOK,
fmt.Sprintf(`{"validators":["%s"]}`, valOperAddr),
},
}
RunRestQueries(t, valsTestCases)
// test withdraw address grpc endpoint
withdrawAddrURL := baseurl + `/cosmos/distribution/v1beta1/delegators/%s/withdraw_address`
withdrawAddrTestCases := []RestTestCase{
{
"gRPC request withdraw address with valid delegator address",
fmt.Sprintf(withdrawAddrURL, delAddr),
http.StatusOK,
fmt.Sprintf(`{"withdraw_address":"%s"}`, delAddr),
},
}
RunRestQueries(t, withdrawAddrTestCases)
}
+31
View File
@@ -3,9 +3,12 @@ package systemtests
import (
"io"
"net/http"
"regexp"
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/testutil"
)
type RestTestCase struct {
@@ -28,6 +31,34 @@ func RunRestQueries(t *testing.T, testCases []RestTestCase) {
}
}
// TestRestQueryIgnoreNumbers 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) {
t.Helper()
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+)?"`
// compile the regex
r, err := regexp.Compile(numberRegexPattern)
require.NoError(t, err)
// replace all numeric values in the above JSONs with `NUMBER` text
expectedJSON := r.ReplaceAllString(tc.expOut, `"NUMBER"`)
actualJSON := r.ReplaceAllString(string(resp), `"NUMBER"`)
// compare two jsons
require.JSONEq(t, expectedJSON, actualJSON)
})
}
}
func GetRequest(t *testing.T, url string) []byte {
t.Helper()
return GetRequestWithHeaders(t, url, nil, http.StatusOK)
+7 -2
View File
@@ -132,12 +132,17 @@ func (s *SystemUnderTest) SetupChain() {
genesisBz, err = sjson.SetRawBytes(genesisBz, "consensus.params.block.max_gas", []byte(fmt.Sprintf(`"%d"`, 10_000_000)))
if err != nil {
panic(fmt.Sprintf("failed set block max gas: %s", err))
panic(fmt.Sprintf("failed to set block max gas: %s", err))
}
// Short period for gov
genesisBz, err = sjson.SetRawBytes(genesisBz, "app_state.gov.params.voting_period", []byte(fmt.Sprintf(`"%s"`, "8s")))
if err != nil {
panic(fmt.Sprintf("failed set voting period: %s", err))
panic(fmt.Sprintf("failed to set regular voting period: %s", err))
}
// update expedited voting period to avoid validation error
genesisBz, err = sjson.SetRawBytes(genesisBz, "app_state.gov.params.expedited_voting_period", []byte(fmt.Sprintf(`"%s"`, "7s")))
if err != nil {
panic(fmt.Sprintf("failed to set expedited voting period: %s", err))
}
s.withEachNodeHome(func(i int, home string) {
if err := saveGenesis(home, genesisBz); err != nil {