From 8f4141d90483e2fe398445b158cb885fc877e195 Mon Sep 17 00:00:00 2001 From: Sairam kola Date: Fri, 28 Jul 2023 17:36:32 +0530 Subject: [PATCH] add keeper and proposal tests --- x/govshuttle/keeper/keeper_test.go | 111 +++++++- x/govshuttle/keeper/proposals_test.go | 367 ++++++++++++++++++++++++++ 2 files changed, 471 insertions(+), 7 deletions(-) diff --git a/x/govshuttle/keeper/keeper_test.go b/x/govshuttle/keeper/keeper_test.go index fa1f4a89..488edc40 100644 --- a/x/govshuttle/keeper/keeper_test.go +++ b/x/govshuttle/keeper/keeper_test.go @@ -4,34 +4,42 @@ import ( "encoding/json" "math/big" "testing" + "time" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/version" "github.com/Canto-Network/Canto/v6/app" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/evmos/ethermint/crypto/ethsecp256k1" "github.com/evmos/ethermint/server/config" evm "github.com/evmos/ethermint/x/evm/types" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmversion "github.com/tendermint/tendermint/proto/tendermint/version" //used for deploying contracts "github.com/Canto-Network/Canto/v6/contracts" - "github.com/Canto-Network/Canto/v6/x/erc20/types" + "github.com/Canto-Network/Canto/v6/x/govshuttle/types" ethtypes "github.com/ethereum/go-ethereum/core/types" + evmtypes "github.com/evmos/ethermint/x/evm/types" ) type KeeperTestSuite struct { suite.Suite //top level testing suite - ctx sdk.Context - app *app.Canto - address common.Address - - queryClient types.QueryClient + ctx sdk.Context + app *app.Canto + address common.Address + consAddress sdk.ConsAddress queryClientEvm evm.QueryClient signer keyring.Signer } @@ -43,8 +51,13 @@ func TestKeeperTestSuite(t *testing.T) { suite.Run(t, s) } -//Test Helpers +// Test Helpers func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) { + + // consensus key + priv, err := ethsecp256k1.GenerateKey() + require.NoError(t, err) + suite.consAddress = sdk.ConsAddress(priv.PubKey().Address()) checkTx := false feemarketGenesis := feemarkettypes.DefaultGenesisState() @@ -53,6 +66,30 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) { //init app suite.app = app.Setup(checkTx, feemarketGenesis) + suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{ + Height: 1, + ChainID: "canto_9001-1", + Time: time.Now().UTC(), + ProposerAddress: suite.consAddress.Bytes(), + + Version: tmversion.Consensus{ + Block: version.BlockProtocol, + }, + LastBlockId: tmproto.BlockID{ + Hash: tmhash.Sum([]byte("block_id")), + PartSetHeader: tmproto.PartSetHeader{ + Total: 11, + Hash: tmhash.Sum([]byte("partset_header")), + }, + }, + AppHash: tmhash.Sum([]byte("app")), + DataHash: tmhash.Sum([]byte("data")), + EvidenceHash: tmhash.Sum([]byte("evidence")), + ValidatorsHash: tmhash.Sum([]byte("validators")), + NextValidatorsHash: tmhash.Sum([]byte("next_validators")), + ConsensusHash: tmhash.Sum([]byte("consensus")), + LastResultsHash: tmhash.Sum([]byte("last_result")), + }) } func (suite *KeeperTestSuite) SetupTest() { @@ -119,3 +156,63 @@ func (suite *KeeperTestSuite) DeployCaller() (common.Address, error) { func (suite *KeeperTestSuite) DeployCallee() { } + +var _ types.GovKeeper = &MockGovKeeper{} + +type MockGovKeeper struct { + mock.Mock +} + +func (m *MockGovKeeper) GetProposalID(ctx sdk.Context) (uint64, error) { + args := m.Called(mock.Anything) + return args.Get(0).(uint64), args.Error(1) +} + +var _ types.AccountKeeper = &MockAccountKeeper{} + +type MockAccountKeeper struct { + mock.Mock +} + +func (m *MockAccountKeeper) GetSequence(ctx sdk.Context, addr sdk.AccAddress) (uint64, error) { + args := m.Called(mock.Anything, mock.Anything) + return args.Get(0).(uint64), args.Error(1) +} + +func (m *MockAccountKeeper) GetModuleAddress(moduleName string) sdk.AccAddress { + args := m.Called(mock.Anything) + return args.Get(0).(sdk.AccAddress) +} + +var _ types.ERC20Keeper = &MockERC20Keeper{} + +type MockERC20Keeper struct { + mock.Mock +} + +func (m *MockERC20Keeper) CallEVM(ctx sdk.Context, abi abi.ABI, from common.Address, contract common.Address, commit bool, method string, args ...interface{}) (*evmtypes.MsgEthereumTxResponse, error) { + resArgs := m.Called(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) + if resArgs.Get(0) == nil { + return nil, resArgs.Error(1) + } + return resArgs.Get(0).(*evmtypes.MsgEthereumTxResponse), resArgs.Error(1) +} + +func (m *MockERC20Keeper) CallEVMWithData(ctx sdk.Context, from common.Address, contract *common.Address, data []byte, commit bool) (*evmtypes.MsgEthereumTxResponse, error) { + args := m.Called(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).(*evmtypes.MsgEthereumTxResponse), args.Error(1) +} + +func (suite *KeeperTestSuite) TestKeeper() { + address, found := suite.app.GovshuttleKeeper.GetPort(suite.ctx) + suite.Require().Equal(false, found) + suite.Require().Equal(common.Address{}, address) + testAddress := common.HexToAddress("0x648a5Aa0C4FbF2C1CF5a3B432c2766EeaF8E402d") + suite.app.GovshuttleKeeper.SetPort(suite.ctx, testAddress) + address, found = suite.app.GovshuttleKeeper.GetPort(suite.ctx) + suite.Require().Equal(true, found) + suite.Require().Equal(testAddress, address) +} diff --git a/x/govshuttle/keeper/proposals_test.go b/x/govshuttle/keeper/proposals_test.go index 94292649..4ab027e0 100644 --- a/x/govshuttle/keeper/proposals_test.go +++ b/x/govshuttle/keeper/proposals_test.go @@ -1 +1,368 @@ package keeper_test + +import ( + "fmt" + "math/big" + "reflect" + "testing" + + "github.com/Canto-Network/Canto/v6/x/govshuttle/keeper" + "github.com/Canto-Network/Canto/v6/x/govshuttle/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/stretchr/testify/mock" +) + +func (suite KeeperTestSuite) TestAppendLendingMarketProposal() { + inputLmProposal := &types.LendingMarketProposal{ + Title: "title", + Description: "description", + Metadata: &types.LendingMarketMetadata{ + Account: []string{ + "0x5E23dC409Fc2F832f83CEc191E245A191a4bCc5C", + }, + PropId: 0, + Values: []uint64{0}, + Calldatas: []string{""}, + Signatures: []string{"_acceptAdmin"}, + }, + } + expLmProposal := &types.LendingMarketProposal{ + Title: "title", + Description: "description", + Metadata: &types.LendingMarketMetadata{ + Account: []string{ + "0x5E23dC409Fc2F832f83CEc191E245A191a4bCc5C", + }, + PropId: 1, + Values: []uint64{0}, + Calldatas: []string{""}, + Signatures: []string{"_acceptAdmin"}, + }, + } + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + name: "Prop Id 0 ", + malleate: func() { + inputLmProposal.Metadata.PropId = 0 + expLmProposal.Metadata.PropId = 1 + mockGovKeeper := &MockGovKeeper{} + mockAccountKeeper := &MockAccountKeeper{} + mockERC20Keeper := &MockERC20Keeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, mockAccountKeeper, mockERC20Keeper, mockGovKeeper) + mockGovKeeper.On("GetProposalID", mock.Anything).Return(uint64(1), nil) + mockAccountKeeper.On("GetSequence", mock.Anything, mock.Anything).Return(uint64(0), nil) + mockERC20Keeper.On("CallEVMWithData", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + mockERC20Keeper.On("CallEVM", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + }, + expPass: true, + }, + { + name: "Force fail gov", + malleate: func() { + inputLmProposal.Metadata.PropId = 0 + expLmProposal.Metadata.PropId = 1 + mockGovKeeper := &MockGovKeeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, suite.app.AccountKeeper, suite.app.Erc20Keeper, mockGovKeeper) + mockGovKeeper.On("GetProposalID", mock.Anything).Return(uint64(0), fmt.Errorf("forced GetProposalID error")) + }, + expPass: false, + }, + { + name: "Prop Id 1", + malleate: func() { + inputLmProposal.Metadata.PropId = 1 + expLmProposal.Metadata.PropId = 1 + mockAccountKeeper := &MockAccountKeeper{} + mockERC20Keeper := &MockERC20Keeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, mockAccountKeeper, mockERC20Keeper, suite.app.GovKeeper) + mockAccountKeeper.On("GetSequence", mock.Anything, mock.Anything).Return(uint64(0), nil) + mockERC20Keeper.On("CallEVMWithData", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + mockERC20Keeper.On("CallEVM", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + }, + expPass: true, + }, + { + name: "force fail erc20", + malleate: func() { + inputLmProposal.Metadata.PropId = 1 + expLmProposal.Metadata.PropId = 1 + mockAccountKeeper := &MockAccountKeeper{} + mockERC20Keeper := &MockERC20Keeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, mockAccountKeeper, mockERC20Keeper, suite.app.GovKeeper) + mockAccountKeeper.On("GetSequence", mock.Anything, mock.Anything).Return(uint64(0), nil) + mockERC20Keeper.On("CallEVMWithData", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + mockERC20Keeper.On("CallEVM", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced CallEVM error")) + }, + expPass: false, + }, + { + name: "Map contract already deployed", + malleate: func() { + inputLmProposal.Metadata.PropId = 1 + expLmProposal.Metadata.PropId = 1 + suite.app.GovshuttleKeeper.SetPort(suite.ctx, common.HexToAddress("0x648a5Aa0C4FbF2C1CF5a3B432c2766EeaF8E402d")) + mockERC20Keeper := &MockERC20Keeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, suite.app.AccountKeeper, mockERC20Keeper, suite.app.GovKeeper) + mockERC20Keeper.On("CallEVM", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + }, + expPass: true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() + + tc.malleate() + + outputLmProposal, err := suite.app.GovshuttleKeeper.AppendLendingMarketProposal(suite.ctx, inputLmProposal) + + if tc.expPass { + suite.Require().NoError(err, tc.name) + suite.Require().Equal(expLmProposal, outputLmProposal) + } else { + suite.Require().Error(err, tc.name) + } + }) + } +} + +func (suite KeeperTestSuite) TestDeployMapContract() { + inputLmProposal := &types.LendingMarketProposal{ + Title: "title", + Description: "description", + Metadata: &types.LendingMarketMetadata{ + Account: []string{ + "0x5E23dC409Fc2F832f83CEc191E245A191a4bCc5C", + }, + PropId: 1, + Values: []uint64{0}, + Calldatas: []string{""}, + Signatures: []string{"_acceptAdmin"}, + }, + } + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + name: "ok", + malleate: func() { + mockAccountKeeper := &MockAccountKeeper{} + mockERC20Keeper := &MockERC20Keeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, mockAccountKeeper, mockERC20Keeper, suite.app.GovKeeper) + mockAccountKeeper.On("GetSequence", mock.Anything, mock.Anything).Return(uint64(0), nil) + mockERC20Keeper.On("CallEVMWithData", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + }, + expPass: true, + }, + { + name: "Force fail account", + malleate: func() { + mockAccountKeeper := &MockAccountKeeper{} + mockERC20Keeper := &MockERC20Keeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, mockAccountKeeper, mockERC20Keeper, suite.app.GovKeeper) + mockAccountKeeper.On("GetSequence", mock.Anything, mock.Anything).Return(uint64(0), fmt.Errorf("forced GetSequence error")) + mockERC20Keeper.On("CallEVMWithData", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{}}, nil) + }, + expPass: false, + }, + { + name: "Force fail erc20", + malleate: func() { + mockAccountKeeper := &MockAccountKeeper{} + mockERC20Keeper := &MockERC20Keeper{} + subspace, ok := suite.app.ParamsKeeper.GetSubspace(types.ModuleName) + suite.Require().True(ok) + suite.app.GovshuttleKeeper = keeper.NewKeeper(suite.app.GetKey("shuttle"), suite.app.AppCodec(), subspace, mockAccountKeeper, mockERC20Keeper, suite.app.GovKeeper) + mockAccountKeeper.On("GetSequence", mock.Anything, mock.Anything).Return(uint64(0), nil) + mockERC20Keeper.On("CallEVMWithData", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced CallEVMWithData error")) + }, + expPass: false, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() + + tc.malleate() + + address, err := suite.app.GovshuttleKeeper.DeployMapContract(suite.ctx, inputLmProposal) + expAddress := crypto.CreateAddress(types.ModuleAddress, 0) + if tc.expPass { + suite.Require().NoError(err, tc.name) + suite.Require().Equal(expAddress, address) + } else { + suite.Require().Error(err, tc.name) + } + }) + } +} + +func TestToAddress(t *testing.T) { + type args struct { + addrs []string + } + tests := []struct { + name string + args args + want []common.Address + }{ + { + name: "empty input", + args: args{}, + want: []common.Address{}, + }, + { + name: "valid input", + args: args{ + addrs: []string{ + "0x1234567890abcdef1234567890abcdef1234561A", + "0x1234567890AbcdEF1234567890aBcdef1234561B", + }, + }, + want: []common.Address{ + common.HexToAddress("0x1234567890ABCdeF1234567890abcDEF1234561a"), + common.HexToAddress("0x1234567890ABCdef1234567890ABCdEf1234561b"), + }, + }, + { + name: "invalid input", + args: args{ + addrs: []string{ + "0x1234567890abcdef1234567890abcdef1234561A", + "invalid_input", + "0x1234567890AbcdEF1234567890aBcdef1234561B", + }, + }, + want: []common.Address{ + common.HexToAddress("0x1234567890ABCdeF1234567890abcDEF1234561a"), + {}, + common.HexToAddress("0x1234567890ABCdef1234567890ABCdEf1234561b"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := keeper.ToAddress(tt.args.addrs); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ToAddress() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestToBytes(t *testing.T) { + type args struct { + strs []string + } + tests := []struct { + name string + args args + want [][]byte + }{ + { + name: "empty input", + args: args{}, + want: [][]byte{}, + }, + { + name: "valid input", + args: args{ + strs: []string{ + "1234567890abcdef1234567890abcdef1234561A", + "1234567890AbcdEF1234567890aBcdef1234561B", + }, + }, + want: [][]byte{ + common.Hex2Bytes("1234567890abcdef1234567890abcdef1234561A"), + common.Hex2Bytes("1234567890AbcdEF1234567890aBcdef1234561B"), + }, + }, + { + name: "mixed input", + args: args{ + strs: []string{ + "1234567890abcdef1234567890abcdef1234561A", + "", + "1234567890AbcdEF1234567890aBcdef1234561B", + }, + }, + want: [][]byte{ + common.Hex2Bytes("1234567890abcdef1234567890abcdef1234561A"), + {}, + common.Hex2Bytes("1234567890AbcdEF1234567890aBcdef1234561B"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := keeper.ToBytes(tt.args.strs); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ToBytes() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestToBigInt(t *testing.T) { + type args struct { + ints []uint64 + } + tests := []struct { + name string + args args + want []*big.Int + }{ + { + name: "empty input", + args: args{}, + want: []*big.Int{}, + }, + { + name: "single input", + args: args{ + ints: []uint64{10}, + }, + want: []*big.Int{ + big.NewInt(10), + }, + }, + { + name: "multiple inputs", + args: args{ + ints: []uint64{10, 20, 30}, + }, + want: []*big.Int{ + big.NewInt(10), + big.NewInt(20), + big.NewInt(30), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := keeper.ToBigInt(tt.args.ints); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ToBigInt() = %v, want %v", got, tt.want) + } + }) + } +}