package keeper_test import ( "testing" "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/feegrant/types" ) type KeeperTestSuite struct { suite.Suite app *simapp.SimApp ctx sdk.Context addrs []sdk.AccAddress } func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } func (suite *KeeperTestSuite) SetupTest() { app := simapp.Setup(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) suite.app = app suite.ctx = ctx suite.addrs = simapp.AddTestAddrsIncremental(app, ctx, 4, sdk.NewInt(30000000)) } func (suite *KeeperTestSuite) TestKeeperCrud() { ctx := suite.ctx k := suite.app.FeeGrantKeeper // some helpers atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) basic := &types.BasicFeeAllowance{ SpendLimit: atom, Expiration: types.ExpiresAtHeight(334455), } basic2 := &types.BasicFeeAllowance{ SpendLimit: eth, Expiration: types.ExpiresAtHeight(172436), } // let's set up some initial state here err := k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[1], basic) suite.Require().NoError(err) err = k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[2], basic2) suite.Require().NoError(err) err = k.GrantFeeAllowance(ctx, suite.addrs[1], suite.addrs[2], basic) suite.Require().NoError(err) err = k.GrantFeeAllowance(ctx, suite.addrs[1], suite.addrs[3], basic) suite.Require().NoError(err) err = k.GrantFeeAllowance(ctx, suite.addrs[3], suite.addrs[0], basic2) suite.Require().NoError(err) // remove some, overwrite other k.RevokeFeeAllowance(ctx, suite.addrs[0], suite.addrs[1]) k.RevokeFeeAllowance(ctx, suite.addrs[0], suite.addrs[2]) err = k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[2], basic) suite.Require().NoError(err) err = k.GrantFeeAllowance(ctx, suite.addrs[1], suite.addrs[2], basic2) suite.Require().NoError(err) // end state: // addr -> addr3 (basic) // addr2 -> addr3 (basic2), addr4(basic) // addr4 -> addr (basic2) // then lots of queries cases := map[string]struct { grantee sdk.AccAddress granter sdk.AccAddress allowance types.FeeAllowanceI }{ "addr revoked": { granter: suite.addrs[0], grantee: suite.addrs[1], }, "addr revoked and added": { granter: suite.addrs[0], grantee: suite.addrs[2], allowance: basic, }, "addr never there": { granter: suite.addrs[0], grantee: suite.addrs[3], }, "addr modified": { granter: suite.addrs[1], grantee: suite.addrs[2], allowance: basic2, }, } for name, tc := range cases { tc := tc suite.Run(name, func() { allow := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) if tc.allowance == nil { suite.Nil(allow) return } suite.NotNil(allow) suite.Equal(tc.allowance, allow) }) } grant1, err := types.NewFeeAllowanceGrant(suite.addrs[3], suite.addrs[0], basic2) suite.NoError(err) grant2, err := types.NewFeeAllowanceGrant(suite.addrs[1], suite.addrs[2], basic2) suite.NoError(err) grant3, err := types.NewFeeAllowanceGrant(suite.addrs[0], suite.addrs[2], basic) suite.NoError(err) allCases := map[string]struct { grantee sdk.AccAddress grants []types.FeeAllowanceGrant }{ "addr2 has none": { grantee: suite.addrs[1], }, "addr has one": { grantee: suite.addrs[0], grants: []types.FeeAllowanceGrant{ grant1, }, }, "addr3 has two": { grantee: suite.addrs[2], grants: []types.FeeAllowanceGrant{ grant3, grant2, }, }, } for name, tc := range allCases { tc := tc suite.Run(name, func() { var grants []types.FeeAllowanceGrant err := k.IterateAllGranteeFeeAllowances(ctx, tc.grantee, func(grant types.FeeAllowanceGrant) bool { grants = append(grants, grant) return false }) suite.NoError(err) suite.Equal(tc.grants, grants) }) } } func (suite *KeeperTestSuite) TestUseGrantedFee() { ctx := suite.ctx k := suite.app.FeeGrantKeeper // some helpers atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) future := &types.BasicFeeAllowance{ SpendLimit: atom, Expiration: types.ExpiresAtHeight(5678), } expired := &types.BasicFeeAllowance{ SpendLimit: eth, Expiration: types.ExpiresAtHeight(55), } // for testing limits of the contract hugeAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 9999)) _ = hugeAtom smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) _ = smallAtom futureAfterSmall := &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 554)), Expiration: types.ExpiresAtHeight(5678), } // then lots of queries cases := map[string]struct { grantee sdk.AccAddress granter sdk.AccAddress fee sdk.Coins allowed bool final types.FeeAllowanceI }{ "use entire pot": { granter: suite.addrs[0], grantee: suite.addrs[1], fee: atom, allowed: true, final: nil, }, "expired and removed": { granter: suite.addrs[0], grantee: suite.addrs[2], fee: eth, allowed: false, final: nil, }, "too high": { granter: suite.addrs[0], grantee: suite.addrs[1], fee: hugeAtom, allowed: false, final: future, }, "use a little": { granter: suite.addrs[0], grantee: suite.addrs[1], fee: smallAtom, allowed: true, final: futureAfterSmall, }, } for name, tc := range cases { tc := tc suite.Run(name, func() { // let's set up some initial state here // addr -> addr2 (future) // addr -> addr3 (expired) err := k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[1], future) suite.Require().NoError(err) err = k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[3], expired) suite.Require().NoError(err) err = k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) if tc.allowed { suite.NoError(err) } else { suite.Error(err) } loaded := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) suite.Equal(tc.final, loaded) }) } }