github.com/Finschia/finschia-sdk@v0.48.1/x/foundation/keeper/internal/keeper_test.go (about) 1 package internal_test 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 "github.com/stretchr/testify/suite" 9 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 10 11 "github.com/Finschia/finschia-sdk/crypto/keys/secp256k1" 12 "github.com/Finschia/finschia-sdk/simapp" 13 "github.com/Finschia/finschia-sdk/testutil/testdata" 14 sdk "github.com/Finschia/finschia-sdk/types" 15 authtypes "github.com/Finschia/finschia-sdk/x/auth/types" 16 "github.com/Finschia/finschia-sdk/x/foundation" 17 keeper "github.com/Finschia/finschia-sdk/x/foundation/keeper" 18 "github.com/Finschia/finschia-sdk/x/foundation/keeper/internal" 19 govtypes "github.com/Finschia/finschia-sdk/x/gov/types" 20 minttypes "github.com/Finschia/finschia-sdk/x/mint/types" 21 ) 22 23 type KeeperTestSuite struct { 24 suite.Suite 25 26 deterministic bool 27 28 ctx sdk.Context 29 30 bankKeeper foundation.BankKeeper 31 keeper keeper.Keeper 32 impl internal.Keeper 33 34 queryServer foundation.QueryServer 35 msgServer foundation.MsgServer 36 proposalHandler govtypes.Handler 37 38 authority sdk.AccAddress 39 members []sdk.AccAddress 40 stranger sdk.AccAddress 41 42 activeProposal uint64 43 votedProposal uint64 44 withdrawnProposal uint64 45 invalidProposal uint64 46 noHandlerProposal uint64 47 nextProposal uint64 48 49 balance sdk.Int 50 } 51 52 func newMsgCreateDog(name string) sdk.Msg { 53 return &testdata.MsgCreateDog{ 54 Dog: &testdata.Dog{ 55 Name: name, 56 }, 57 } 58 } 59 60 func (s *KeeperTestSuite) createAddresses(accNum int) []sdk.AccAddress { 61 if s.deterministic { 62 addresses := make([]sdk.AccAddress, accNum) 63 for i := range addresses { 64 addresses[i] = sdk.AccAddress(fmt.Sprintf("address%d", i)) 65 } 66 return addresses 67 } else { 68 seenAddresses := make(map[string]bool, accNum) 69 addresses := make([]sdk.AccAddress, accNum) 70 for i := range addresses { 71 var address sdk.AccAddress 72 for { 73 pk := secp256k1.GenPrivKey().PubKey() 74 address = sdk.AccAddress(pk.Address()) 75 if !seenAddresses[address.String()] { 76 seenAddresses[address.String()] = true 77 break 78 } 79 } 80 addresses[i] = address 81 } 82 return addresses 83 } 84 } 85 86 func (s *KeeperTestSuite) SetupTest() { 87 checkTx := false 88 app := simapp.Setup(checkTx) 89 testdata.RegisterInterfaces(app.InterfaceRegistry()) 90 testdata.RegisterMsgServer(app.MsgServiceRouter(), testdata.MsgServerImpl{}) 91 92 s.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{}) 93 s.bankKeeper = app.BankKeeper 94 s.keeper = app.FoundationKeeper 95 s.impl = internal.NewKeeper( 96 app.AppCodec(), 97 app.GetKey(foundation.ModuleName), 98 app.MsgServiceRouter(), 99 app.AccountKeeper, 100 app.BankKeeper, 101 authtypes.FeeCollectorName, 102 foundation.DefaultConfig(), 103 foundation.DefaultAuthority().String(), 104 app.GetSubspace(foundation.ModuleName), 105 ) 106 107 s.queryServer = keeper.NewQueryServer(s.keeper) 108 s.msgServer = keeper.NewMsgServer(s.keeper) 109 110 s.proposalHandler = keeper.NewFoundationProposalsHandler(s.keeper) 111 112 s.impl.SetParams(s.ctx, foundation.Params{ 113 FoundationTax: sdk.OneDec(), 114 }) 115 116 s.impl.SetCensorship(s.ctx, foundation.Censorship{ 117 MsgTypeUrl: sdk.MsgTypeURL((*foundation.MsgWithdrawFromTreasury)(nil)), 118 Authority: foundation.CensorshipAuthorityFoundation, 119 }) 120 121 s.authority = sdk.MustAccAddressFromBech32(s.impl.GetAuthority()) 122 123 numMembers := 10 124 addresses := s.createAddresses(numMembers + 1) 125 s.members = addresses[:numMembers] 126 for i := range s.members { 127 member := foundation.Member{ 128 Address: s.members[i].String(), 129 } 130 s.impl.SetMember(s.ctx, member) 131 } 132 s.stranger = addresses[len(addresses)-1] 133 134 info := foundation.DefaultFoundation() 135 info.TotalWeight = sdk.NewDec(int64(len(s.members))) 136 err := info.SetDecisionPolicy(workingPolicy()) 137 s.Require().NoError(err) 138 s.impl.SetFoundationInfo(s.ctx, info) 139 140 s.balance = sdk.NewInt(987654321) 141 s.impl.SetPool(s.ctx, foundation.Pool{ 142 Treasury: sdk.NewDecCoinsFromCoins(sdk.NewCoin(sdk.DefaultBondDenom, s.balance)), 143 }) 144 holders := []sdk.AccAddress{ 145 s.stranger, 146 app.AccountKeeper.GetModuleAccount(s.ctx, foundation.TreasuryName).GetAddress(), 147 app.AccountKeeper.GetModuleAccount(s.ctx, authtypes.FeeCollectorName).GetAddress(), 148 } 149 for _, holder := range holders { 150 amount := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, s.balance)) 151 152 // using minttypes here introduces dependency on x/mint 153 // the work around would be registering a new module account on this suite 154 // because x/bank already has dependency on x/mint, and we must have dependency 155 // on x/bank, it's OK to use x/mint here. 156 minterName := minttypes.ModuleName 157 err := app.BankKeeper.MintCoins(s.ctx, minterName, amount) 158 s.Require().NoError(err) 159 160 minter := app.AccountKeeper.GetModuleAccount(s.ctx, minterName).GetAddress() 161 err = app.BankKeeper.SendCoins(s.ctx, minter, holder, amount) 162 s.Require().NoError(err) 163 } 164 165 // create an active proposal, voted yes by all members except the first member 166 activeProposal, err := s.impl.SubmitProposal(s.ctx, []string{s.members[0].String()}, "", []sdk.Msg{ 167 &foundation.MsgWithdrawFromTreasury{ 168 Authority: s.authority.String(), 169 To: s.stranger.String(), 170 Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, s.balance)), 171 }, 172 }) 173 s.Require().NoError(err) 174 s.activeProposal = *activeProposal 175 176 for _, member := range s.members[1:] { 177 err := s.impl.Vote(s.ctx, foundation.Vote{ 178 ProposalId: s.activeProposal, 179 Voter: member.String(), 180 Option: foundation.VOTE_OPTION_YES, 181 }) 182 s.Require().NoError(err) 183 } 184 185 // create a proposal voted no by all members 186 votedProposal, err := s.impl.SubmitProposal(s.ctx, []string{s.members[0].String()}, "", []sdk.Msg{newMsgCreateDog("shiba1")}) 187 s.Require().NoError(err) 188 s.votedProposal = *votedProposal 189 190 for _, member := range s.members { 191 err := s.impl.Vote(s.ctx, foundation.Vote{ 192 ProposalId: s.votedProposal, 193 Voter: member.String(), 194 Option: foundation.VOTE_OPTION_NO, 195 }) 196 s.Require().NoError(err) 197 } 198 199 // create an withdrawn proposal 200 withdrawnProposal, err := s.impl.SubmitProposal(s.ctx, []string{s.members[0].String()}, "", []sdk.Msg{newMsgCreateDog("shiba2")}) 201 s.Require().NoError(err) 202 s.withdrawnProposal = *withdrawnProposal 203 204 err = s.impl.WithdrawProposal(s.ctx, s.withdrawnProposal) 205 s.Require().NoError(err) 206 207 // create an invalid proposal which contains invalid message 208 invalidProposal, err := s.impl.SubmitProposal(s.ctx, []string{s.members[0].String()}, "", []sdk.Msg{ 209 &foundation.MsgWithdrawFromTreasury{ 210 Authority: s.authority.String(), 211 To: s.stranger.String(), 212 Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, s.balance.Add(sdk.OneInt()))), 213 }, 214 }) 215 s.Require().NoError(err) 216 s.invalidProposal = *invalidProposal 217 218 for _, member := range s.members { 219 err := s.impl.Vote(s.ctx, foundation.Vote{ 220 ProposalId: s.invalidProposal, 221 Voter: member.String(), 222 Option: foundation.VOTE_OPTION_YES, 223 }) 224 s.Require().NoError(err) 225 } 226 227 // create an invalid proposal which contains invalid message 228 noHandlerProposal, err := s.impl.SubmitProposal(s.ctx, []string{s.members[0].String()}, "", []sdk.Msg{testdata.NewTestMsg(s.authority)}) 229 s.Require().NoError(err) 230 s.noHandlerProposal = *noHandlerProposal 231 232 for _, member := range s.members { 233 err := s.impl.Vote(s.ctx, foundation.Vote{ 234 ProposalId: s.noHandlerProposal, 235 Voter: member.String(), 236 Option: foundation.VOTE_OPTION_YES, 237 }) 238 s.Require().NoError(err) 239 } 240 241 // next proposal is the proposal id for the upcoming proposal 242 s.nextProposal = s.noHandlerProposal + 1 243 244 // grant stranger to receive foundation treasury 245 err = s.impl.Grant(s.ctx, s.stranger, &foundation.ReceiveFromTreasuryAuthorization{}) 246 s.Require().NoError(err) 247 } 248 249 func TestKeeperTestSuite(t *testing.T) { 250 for _, deterministic := range []bool{ 251 false, 252 true, 253 } { 254 suite.Run(t, &KeeperTestSuite{deterministic: deterministic}) 255 } 256 } 257 258 func TestNewKeeper(t *testing.T) { 259 createAddress := func() sdk.AccAddress { 260 return sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) 261 } 262 authority := foundation.DefaultAuthority() 263 264 testCases := map[string]struct { 265 authority sdk.AccAddress 266 panics bool 267 }{ 268 "default authority": { 269 authority: authority, 270 }, 271 "invalid account": { 272 panics: true, 273 }, 274 "not the default authority": { 275 authority: createAddress(), 276 panics: true, 277 }, 278 } 279 280 for name, tc := range testCases { 281 tc := tc 282 t.Run(name, func(t *testing.T) { 283 newKeeper := func() keeper.Keeper { 284 app := simapp.Setup(false) 285 return keeper.NewKeeper(app.AppCodec(), sdk.NewKVStoreKey(foundation.StoreKey), app.MsgServiceRouter(), app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, foundation.DefaultConfig(), tc.authority.String(), app.GetSubspace(foundation.ModuleName)) 286 } 287 288 if tc.panics { 289 require.Panics(t, func() { newKeeper() }) 290 return 291 } 292 require.NotPanics(t, func() { newKeeper() }) 293 294 k := newKeeper() 295 require.Equal(t, authority.String(), k.GetAuthority()) 296 }) 297 } 298 }