From 75cb27a4b5faf6b64e33abd4c2f760d287027bff Mon Sep 17 00:00:00 2001 From: Rafael Tenfen Date: Tue, 12 Apr 2022 13:35:09 -0300 Subject: [PATCH] test: increase x/oracle unit test coverage (#798) ## Description - Add unit test to - `x/oracle/keeper/msg_server_test.go` - `x/oracle/keeper/params_test.go` - `x/oracle/keeper/reward_test.go` - `x/oracle/types/ballot_test.go` - `x/oracle/types/codec_test.go` - `x/oracle/types/genesis_test.go` - `x/oracle/types/msgs_test.go` helps: #661 ---- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added appropriate labels to the PR - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/umee-network/umee/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) (cherry picked from commit 060fbfedc89934511b99a3607fbb6f3fc9dfb887) --- CHANGELOG.md | 1 + x/oracle/keeper/msg_server_test.go | 1 + x/oracle/keeper/params_test.go | 21 +++++ x/oracle/keeper/reward_test.go | 8 +- x/oracle/types/ballot_test.go | 84 +++++++++++++++++ x/oracle/types/codec_test.go | 14 +++ x/oracle/types/genesis_test.go | 22 ++++- x/oracle/types/msgs_test.go | 139 ++++++++++++++++++++++------- x/oracle/types/test_utils.go | 33 +++++++ 9 files changed, 287 insertions(+), 36 deletions(-) create mode 100644 x/oracle/keeper/params_test.go create mode 100644 x/oracle/types/codec_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a5ded9aeaa..56ed430199 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [781](https://github.com/umee-network/umee/pull/781) Oracle module unit test cleanup. - [782](https://github.com/umee-network/umee/pull/782) Add unit test to `x/oracle/types/denom.go` and `x/oracle/types/keys.go`. - [786](https://github.com/umee-network/umee/pull/786) Add unit test to `x/oracle/...`. +- [798](https://github.com/umee-network/umee/pull/798) Increase `x/oracle` unit test coverage. ## [v2.0.0](https://github.com/umee-network/umee/releases/tag/v2.0.0) - 2022-04-06 diff --git a/x/oracle/keeper/msg_server_test.go b/x/oracle/keeper/msg_server_test.go index f175dbd0e1..f43029cae3 100644 --- a/x/oracle/keeper/msg_server_test.go +++ b/x/oracle/keeper/msg_server_test.go @@ -120,6 +120,7 @@ func (s *IntegrationTestSuite) TestMsgServer_AggregateExchangeRateVote() { _, err = s.msgServer.AggregateExchangeRateVote(sdk.WrapSDKContext(ctx), voteMsg) s.Require().NoError(err) vote, err := s.app.OracleKeeper.GetAggregateExchangeRateVote(ctx, valAddr) + s.Require().Nil(err) for _, v := range vote.ExchangeRateTuples { s.Require().Contains(acceptListFlat, strings.ToLower(v.Denom)) } diff --git a/x/oracle/keeper/params_test.go b/x/oracle/keeper/params_test.go new file mode 100644 index 0000000000..d3fefb3947 --- /dev/null +++ b/x/oracle/keeper/params_test.go @@ -0,0 +1,21 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/umee-network/umee/v2/x/oracle/types" +) + +func (s *IntegrationTestSuite) TestVoteThreshold() { + app, ctx := s.app, s.ctx + + voteDec := app.OracleKeeper.VoteThreshold(ctx) + s.Require().Equal(sdk.MustNewDecFromStr("0.5"), voteDec) + + newVoteTreshold := sdk.MustNewDecFromStr("0.6") + defaultParams := types.DefaultParams() + defaultParams.VoteThreshold = newVoteTreshold + app.OracleKeeper.SetParams(ctx, defaultParams) + + voteThresholdDec := app.OracleKeeper.VoteThreshold(ctx) + s.Require().Equal(newVoteTreshold, voteThresholdDec) +} diff --git a/x/oracle/keeper/reward_test.go b/x/oracle/keeper/reward_test.go index 9fbd5bae1b..a9df75e3a2 100644 --- a/x/oracle/keeper/reward_test.go +++ b/x/oracle/keeper/reward_test.go @@ -22,11 +22,9 @@ func (s *IntegrationTestSuite) TestRewardBallotWinners() { s.Require().NoError(err) var voteTargets []string - var voteTargetDenoms []string params := s.app.OracleKeeper.GetParams(s.ctx) for _, v := range params.AcceptList { voteTargets = append(voteTargets, v.SymbolDenom) - voteTargetDenoms = append(voteTargetDenoms, v.BaseDenom) } votePeriodsPerWindow := sdk.NewDec((int64)(s.app.OracleKeeper.RewardDistributionWindow(s.ctx))). @@ -38,3 +36,9 @@ func (s *IntegrationTestSuite) TestRewardBallotWinners() { s.Require().Equal(sdk.NewDecFromInt(givingAmt.AmountOf(types.UmeeDenom)).QuoInt64(votePeriodsPerWindow).QuoInt64(3).TruncateInt(), outstandingRewards.AmountOf(types.UmeeDenom)) } + +func (s *IntegrationTestSuite) TestRewardBallotWinnersZeroPower() { + s.app.OracleKeeper.RewardBallotWinners(s.ctx, 0, 0, []string{}, map[string]types.Claim{valAddr.String(): {}}) + outstandingRewardsDec := s.app.DistrKeeper.GetValidatorOutstandingRewardsCoins(s.ctx, valAddr) + s.Require().Equal("", outstandingRewardsDec.String()) +} diff --git a/x/oracle/types/ballot_test.go b/x/oracle/types/ballot_test.go index 83844d9a53..87b0eb8255 100644 --- a/x/oracle/types/ballot_test.go +++ b/x/oracle/types/ballot_test.go @@ -292,3 +292,87 @@ func TestPBStandardDeviation_Overflow(t *testing.T) { expectedDevation := sdk.MustNewDecFromStr("871.862661203013097586") require.Equal(t, expectedDevation, deviation) } + +func TestBallotMapToSlice(t *testing.T) { + valAddress := GenerateRandomValAddr(1) + + pb := ExchangeRateBallot{ + NewVoteForTally( + sdk.NewDec(1234), + UmeeSymbol, + valAddress[0], + 2, + ), + NewVoteForTally( + sdk.NewDec(12345), + UmeeSymbol, + valAddress[0], + 1, + ), + } + + ballotSlice := BallotMapToSlice(map[string]ExchangeRateBallot{ + UmeeDenom: pb, + IbcDenomAtom: pb, + }) + require.Equal(t, []BallotDenom{{Ballot: pb, Denom: IbcDenomAtom}, {Ballot: pb, Denom: UmeeDenom}}, ballotSlice) +} + +func TestExchangeRateBallotSwap(t *testing.T) { + valAddress := GenerateRandomValAddr(2) + + voteTallies := []VoteForTally{ + NewVoteForTally( + sdk.NewDec(1234), + UmeeSymbol, + valAddress[0], + 2, + ), + NewVoteForTally( + sdk.NewDec(12345), + UmeeSymbol, + valAddress[1], + 1, + ), + } + + pb := ExchangeRateBallot{voteTallies[0], voteTallies[1]} + + require.Equal(t, pb[0], voteTallies[0]) + require.Equal(t, pb[1], voteTallies[1]) + pb.Swap(1, 0) + require.Equal(t, pb[1], voteTallies[0]) + require.Equal(t, pb[0], voteTallies[1]) +} + +func TestStandardDeviationUnsorted(t *testing.T) { + valAddress := GenerateRandomValAddr(1) + pb := ExchangeRateBallot{ + NewVoteForTally( + sdk.NewDec(1234), + UmeeSymbol, + valAddress[0], + 2, + ), + NewVoteForTally( + sdk.NewDec(12), + UmeeSymbol, + valAddress[0], + 1, + ), + } + + deviation, err := pb.StandardDeviation() + require.ErrorIs(t, err, ErrBallotNotSorted) + require.Equal(t, "0.000000000000000000", deviation.String()) +} + +func TestClaimMapToSlice(t *testing.T) { + valAddress := GenerateRandomValAddr(1) + claim := NewClaim(10, 1, 4, valAddress[0]) + claimSlice := ClaimMapToSlice(map[string]Claim{ + "testClaim": claim, + "anotherClaim": claim, + }) + require.Equal(t, []Claim{claim, claim}, claimSlice) +} diff --git a/x/oracle/types/codec_test.go b/x/oracle/types/codec_test.go new file mode 100644 index 0000000000..391df4ad82 --- /dev/null +++ b/x/oracle/types/codec_test.go @@ -0,0 +1,14 @@ +package types + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/stretchr/testify/require" +) + +func TestRegisterInterfaces(t *testing.T) { + registry := types.NewInterfaceRegistry() + RegisterInterfaces(registry) + require.Equal(t, registry.ListAllInterfaces(), []string([]string{})) +} diff --git a/x/oracle/types/genesis_test.go b/x/oracle/types/genesis_test.go index 59510c6e8a..579618fe54 100644 --- a/x/oracle/types/genesis_test.go +++ b/x/oracle/types/genesis_test.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -9,7 +10,7 @@ import ( func TestGenesisValidation(t *testing.T) { // Valid state - genState := DefaultGenesisState() + genState := NewGenesisState(DefaultParams(), []ExchangeRateTuple{}, []FeederDelegation{}, []MissCounter{}, []AggregateExchangeRatePrevote{}, []AggregateExchangeRateVote{}) require.NoError(t, ValidateGenesis(genState)) // Invalid Vote Period @@ -57,3 +58,22 @@ func TestGenesisValidation(t *testing.T) { genState.Params.AcceptList = DenomList{Denom{}} require.Error(t, ValidateGenesis(genState)) } + +func TestGetGenesisStateFromAppState(t *testing.T) { + emptyGenesis := GenesisState{ + Params: Params{}, + ExchangeRates: []ExchangeRateTuple{}, + FeederDelegations: []FeederDelegation{}, + MissCounters: []MissCounter{}, + AggregateExchangeRatePrevotes: []AggregateExchangeRatePrevote{}, + AggregateExchangeRateVotes: []AggregateExchangeRateVote{}, + } + + bz, err := json.Marshal(emptyGenesis) + require.Nil(t, err) + + require.NotNil(t, GetGenesisStateFromAppState(ModuleCdc, map[string]json.RawMessage{ + ModuleName: bz, + })) + require.NotNil(t, GetGenesisStateFromAppState(ModuleCdc, map[string]json.RawMessage{})) +} diff --git a/x/oracle/types/msgs_test.go b/x/oracle/types/msgs_test.go index 69bb7a4fca..0e1558c32f 100644 --- a/x/oracle/types/msgs_test.go +++ b/x/oracle/types/msgs_test.go @@ -13,15 +13,19 @@ func TestMsgFeederDelegation(t *testing.T) { sdk.AccAddress([]byte("addr2_______________")), } + msgInvalidOperatorAddr := "invalid operator address (empty address string is not allowed): invalid address" + msgInvalidDelegatorAddr := "invalid delegate address (empty address string is not allowed): invalid address" + tests := []struct { - delegator sdk.ValAddress - delegate sdk.AccAddress - expectPass bool + delegator sdk.ValAddress + delegate sdk.AccAddress + expectPass bool + expectedErrorMsg string }{ - {sdk.ValAddress(addrs[0]), addrs[1], true}, - {sdk.ValAddress{}, addrs[1], false}, - {sdk.ValAddress(addrs[0]), sdk.AccAddress{}, false}, - {nil, nil, false}, + {sdk.ValAddress(addrs[0]), addrs[1], true, "test should pass"}, + {sdk.ValAddress{}, addrs[1], false, msgInvalidOperatorAddr}, + {sdk.ValAddress(addrs[0]), sdk.AccAddress{}, false, msgInvalidDelegatorAddr}, + {nil, nil, false, msgInvalidOperatorAddr}, } for i, tc := range tests { @@ -29,7 +33,7 @@ func TestMsgFeederDelegation(t *testing.T) { if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", i) } else { - require.NotNil(t, msg.ValidateBasic(), "test: %v", i) + require.ErrorContainsf(t, msg.ValidateBasic(), tc.expectedErrorMsg, "test: %v", i) } } } @@ -41,25 +45,32 @@ func TestMsgAggregateExchangeRatePrevote(t *testing.T) { exchangeRates := sdk.DecCoins{sdk.NewDecCoinFromDec(UmeeDenom, sdk.OneDec()), sdk.NewDecCoinFromDec(UmeeDenom, sdk.NewDecWithPrec(32121, 1))} bz := GetAggregateVoteHash("1", exchangeRates.String(), sdk.ValAddress(addrs[0])) + msgInvalidHashLength := "invalid hash length; should equal 20" + msgInvalidFeederAddr := "invalid feeder address (empty address string is not allowed): invalid address" + msgInvalidOperatorAddr := "invalid operator address (empty address string is not allowed): invalid address" tests := []struct { - hash AggregateVoteHash - exchangeRates sdk.DecCoins - voter sdk.AccAddress - expectPass bool + hash AggregateVoteHash + exchangeRates sdk.DecCoins + feeder sdk.AccAddress + validator sdk.AccAddress + expectPass bool + expectedErrorMsg string }{ - {bz, exchangeRates, addrs[0], true}, - {bz[1:], exchangeRates, addrs[0], false}, - {bz, exchangeRates, sdk.AccAddress{}, false}, - {AggregateVoteHash{}, exchangeRates, addrs[0], false}, + {bz, exchangeRates, addrs[0], addrs[0], true, "test should pass"}, + {bz[1:], exchangeRates, addrs[0], addrs[0], false, msgInvalidHashLength}, + {[]byte("0\x01"), exchangeRates, addrs[0], addrs[0], false, msgInvalidHashLength}, + {AggregateVoteHash{}, exchangeRates, addrs[0], addrs[0], false, msgInvalidHashLength}, + {bz, exchangeRates, sdk.AccAddress{}, addrs[0], false, msgInvalidFeederAddr}, + {bz, exchangeRates, addrs[0], sdk.AccAddress{}, false, msgInvalidOperatorAddr}, } for i, tc := range tests { - msg := NewMsgAggregateExchangeRatePrevote(tc.hash, tc.voter, sdk.ValAddress(tc.voter)) + msg := NewMsgAggregateExchangeRatePrevote(tc.hash, tc.feeder, sdk.ValAddress(tc.validator)) if tc.expectPass { require.NoError(t, msg.ValidateBasic(), "test: %v", i) } else { - require.Error(t, msg.ValidateBasic(), "test: %v", i) + require.ErrorContainsf(t, msg.ValidateBasic(), tc.expectedErrorMsg, "test: %v", i) } } } @@ -73,31 +84,93 @@ func TestMsgAggregateExchangeRateVote(t *testing.T) { exchangeRates := "foo:1.0,bar:1232.123" zeroExchangeRates := "foo:0.0,bar:1232.132" negativeExchangeRates := "foo:-1234.5,bar:1232.132" - overFlowExchangeRates := "foo:1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0,bar:1232.132" + overFlowMsgExchangeRates := StringWithCharset(4097, "56432") + overFlowExchangeRates := "foo:100000000000000000000000000000000000000000000000000000000000000000000000000000.01,bar:1232.132" validSalt := "0cf33fb528b388660c3a42c3f3250e983395290b75fef255050fb5bc48a6025f" saltWithColon := "0cf33fb528b388660c3a42c3f3250e983395290b75fef255050fb5bc48a6025:" + msgInvalidSalt := "invalid salt length; must be 64" + msgInvalidOverflowValue := "overflow: invalid exchange rate" + msgInvalidHexString := "salt must be a valid hex string: invalid salt format" + msgInvalidUnknownRequest := "must provide at least one oracle exchange rate: unknown request" + msgInvalidFeederAddr := "invalid feeder address (empty address string is not allowed): invalid address" + msgInvalidOperatorAddr := "invalid operator address (empty address string is not allowed): invalid address" + msgInvalidOraclePrice := "failed to parse exchange rates string cause: invalid oracle price: invalid coins" + msgInvalidOverflowExceedCharacter := "exchange rates string can not exceed 4096 characters: invalid request" + msgInvalidExchangeRates := "failed to parse exchange rates string cause: invalid exchange rate a: invalid coins" + tests := []struct { - voter sdk.AccAddress - salt string - exchangeRates string - expectPass bool + feeder sdk.AccAddress + validator sdk.AccAddress + salt string + exchangeRates string + expectPass bool + expectedErrorMsg string }{ - {addrs[0], validSalt, exchangeRates, true}, - {addrs[0], validSalt, invalidExchangeRates, false}, - {addrs[0], validSalt, zeroExchangeRates, false}, - {addrs[0], validSalt, negativeExchangeRates, false}, - {addrs[0], validSalt, overFlowExchangeRates, false}, - {sdk.AccAddress{}, validSalt, exchangeRates, false}, - {addrs[0], "", exchangeRates, false}, - {addrs[0], saltWithColon, exchangeRates, false}, + {addrs[0], addrs[0], validSalt, exchangeRates, true, "test should pass"}, + {addrs[0], addrs[0], validSalt, invalidExchangeRates, false, msgInvalidExchangeRates}, + {addrs[0], addrs[0], validSalt, zeroExchangeRates, false, msgInvalidOraclePrice}, + {addrs[0], addrs[0], validSalt, negativeExchangeRates, false, msgInvalidOraclePrice}, + {addrs[0], addrs[0], validSalt, overFlowMsgExchangeRates, false, msgInvalidOverflowExceedCharacter}, + {addrs[0], addrs[0], validSalt, overFlowExchangeRates, false, msgInvalidOverflowValue}, + {sdk.AccAddress{}, sdk.AccAddress{}, validSalt, exchangeRates, false, msgInvalidFeederAddr}, + {addrs[0], sdk.AccAddress{}, validSalt, exchangeRates, false, msgInvalidOperatorAddr}, + {addrs[0], addrs[0], "", exchangeRates, false, msgInvalidSalt}, + {addrs[0], addrs[0], validSalt, "", false, msgInvalidUnknownRequest}, + {addrs[0], addrs[0], saltWithColon, exchangeRates, false, msgInvalidHexString}, } for i, tc := range tests { - msg := NewMsgAggregateExchangeRateVote(tc.salt, tc.exchangeRates, tc.voter, sdk.ValAddress(tc.voter)) + msg := NewMsgAggregateExchangeRateVote(tc.salt, tc.exchangeRates, tc.feeder, sdk.ValAddress(tc.validator)) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", i) } else { - require.NotNil(t, msg.ValidateBasic(), "test: %v", i) + require.ErrorContainsf(t, msg.ValidateBasic(), tc.expectedErrorMsg, "test: %v", i) } } } + +func TestNewMsgAggregateExchangeRatePrevote(t *testing.T) { + vals := GenerateRandomValAddr(2) + feederAddr := sdk.AccAddress(vals[1]) + + exchangeRates := sdk.DecCoins{sdk.NewDecCoinFromDec(UmeeDenom, sdk.OneDec()), sdk.NewDecCoinFromDec(UmeeDenom, sdk.NewDecWithPrec(32121, 1))} + bz := GetAggregateVoteHash("1", exchangeRates.String(), sdk.ValAddress(vals[0])) + + aggregateExchangeRatePreVote := NewMsgAggregateExchangeRatePrevote( + bz, + feederAddr, + vals[0], + ) + + require.Equal(t, aggregateExchangeRatePreVote.Route(), RouterKey) + require.Equal(t, aggregateExchangeRatePreVote.Type(), TypeMsgAggregateExchangeRatePrevote) + require.NotNil(t, aggregateExchangeRatePreVote.GetSignBytes()) + require.Equal(t, aggregateExchangeRatePreVote.GetSigners(), []sdk.AccAddress{feederAddr}) +} + +func TestNewMsgAggregateExchangeRateVote(t *testing.T) { + vals := GenerateRandomValAddr(2) + feederAddr := sdk.AccAddress(vals[1]) + + aggregateExchangeRateVote := NewMsgAggregateExchangeRateVote( + "salt", + "0.1", + feederAddr, + vals[0], + ) + + require.Equal(t, aggregateExchangeRateVote.Route(), RouterKey) + require.Equal(t, aggregateExchangeRateVote.Type(), TypeMsgAggregateExchangeRateVote) + require.NotNil(t, aggregateExchangeRateVote.GetSignBytes()) + require.Equal(t, aggregateExchangeRateVote.GetSigners(), []sdk.AccAddress{feederAddr}) +} + +func TestMsgDelegateFeedConsent(t *testing.T) { + vals := GenerateRandomValAddr(2) + msgFeedConsent := NewMsgDelegateFeedConsent(vals[0], sdk.AccAddress(vals[1])) + + require.Equal(t, msgFeedConsent.Route(), RouterKey) + require.Equal(t, msgFeedConsent.Type(), TypeMsgDelegateFeedConsent) + require.NotNil(t, msgFeedConsent.GetSignBytes()) + require.Equal(t, msgFeedConsent.GetSigners(), []sdk.AccAddress{sdk.AccAddress(vals[0])}) +} diff --git a/x/oracle/types/test_utils.go b/x/oracle/types/test_utils.go index 1c6477de53..bb5f499531 100644 --- a/x/oracle/types/test_utils.go +++ b/x/oracle/types/test_utils.go @@ -37,6 +37,39 @@ var ( } ) +// StringWithCharset generates a new string with the size of "length" param +// repeating every character of charset, if charset is empty uses "abcd" +func StringWithCharset(length int, charset string) string { + b := make([]byte, length) + + if len(charset) == 0 { + charset = "abcd" + } + + for i := 0; i < length; i++ { + for j := 0; j < len(charset); j++ { + b[i] = charset[j] + i++ + if len(b) == length { + return string(b) + } + } + } + + return string(b) +} + +// GenerateRandomValAddr returns N random validator addresses. +func GenerateRandomValAddr(quantity int) (validatorAddrs []sdk.ValAddress) { + for i := 0; i < quantity; i++ { + pubKey := secp256k1.GenPrivKey().PubKey() + valAddr := sdk.ValAddress(pubKey.Address()) + validatorAddrs = append(validatorAddrs, valAddr) + } + + return validatorAddrs +} + // GenerateRandomTestCase func GenerateRandomTestCase() (valValAddrs []sdk.ValAddress, stakingKeeper MockStakingKeeper) { valValAddrs = []sdk.ValAddress{}