github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/vesting/msg_server_test.go (about) 1 package vesting_test 2 3 import ( 4 "testing" 5 "time" 6 7 cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 8 cmttime "github.com/cometbft/cometbft/types/time" 9 "github.com/golang/mock/gomock" 10 "github.com/stretchr/testify/suite" 11 12 "cosmossdk.io/math" 13 storetypes "cosmossdk.io/store/types" 14 15 "github.com/cosmos/cosmos-sdk/runtime" 16 "github.com/cosmos/cosmos-sdk/testutil" 17 sdk "github.com/cosmos/cosmos-sdk/types" 18 moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" 19 authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" 20 authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" 21 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 22 "github.com/cosmos/cosmos-sdk/x/auth/vesting" 23 vestingtestutil "github.com/cosmos/cosmos-sdk/x/auth/vesting/testutil" 24 vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" 25 ) 26 27 var ( 28 fromAddr = sdk.AccAddress([]byte("from1________________")) 29 to1Addr = sdk.AccAddress([]byte("to1__________________")) 30 to2Addr = sdk.AccAddress([]byte("to2__________________")) 31 to3Addr = sdk.AccAddress([]byte("to3__________________")) 32 fooCoin = sdk.NewInt64Coin("foo", 100) 33 periodCoin = sdk.NewInt64Coin("foo", 20) 34 ) 35 36 type VestingTestSuite struct { 37 suite.Suite 38 39 ctx sdk.Context 40 accountKeeper authkeeper.AccountKeeper 41 bankKeeper *vestingtestutil.MockBankKeeper 42 msgServer vestingtypes.MsgServer 43 } 44 45 func (s *VestingTestSuite) SetupTest() { 46 key := storetypes.NewKVStoreKey(authtypes.StoreKey) 47 storeService := runtime.NewKVStoreService(key) 48 testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test")) 49 s.ctx = testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()}) 50 encCfg := moduletestutil.MakeTestEncodingConfig() 51 52 maccPerms := map[string][]string{} 53 54 ctrl := gomock.NewController(s.T()) 55 s.bankKeeper = vestingtestutil.NewMockBankKeeper(ctrl) 56 s.accountKeeper = authkeeper.NewAccountKeeper( 57 encCfg.Codec, 58 storeService, 59 authtypes.ProtoBaseAccount, 60 maccPerms, 61 authcodec.NewBech32Codec("cosmos"), 62 "cosmos", 63 authtypes.NewModuleAddress("gov").String(), 64 ) 65 66 vestingtypes.RegisterInterfaces(encCfg.InterfaceRegistry) 67 authtypes.RegisterInterfaces(encCfg.InterfaceRegistry) 68 s.msgServer = vesting.NewMsgServerImpl(s.accountKeeper, s.bankKeeper) 69 } 70 71 func (s *VestingTestSuite) TestCreateVestingAccount() { 72 testCases := map[string]struct { 73 preRun func() 74 input *vestingtypes.MsgCreateVestingAccount 75 expErr bool 76 expErrMsg string 77 }{ 78 "empty from address": { 79 input: vestingtypes.NewMsgCreateVestingAccount( 80 []byte{}, 81 to1Addr, 82 sdk.Coins{fooCoin}, 83 time.Now().Unix(), 84 true, 85 ), 86 expErr: true, 87 expErrMsg: "invalid 'from' address", 88 }, 89 "empty to address": { 90 input: vestingtypes.NewMsgCreateVestingAccount( 91 fromAddr, 92 []byte{}, 93 sdk.Coins{fooCoin}, 94 time.Now().Unix(), 95 true, 96 ), 97 expErr: true, 98 expErrMsg: "invalid 'to' address", 99 }, 100 "invalid coins": { 101 input: vestingtypes.NewMsgCreateVestingAccount( 102 fromAddr, 103 to1Addr, 104 sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}}, 105 time.Now().Unix(), 106 true, 107 ), 108 expErr: true, 109 expErrMsg: "-1stake: invalid coins", 110 }, 111 "invalid end time": { 112 input: vestingtypes.NewMsgCreateVestingAccount( 113 fromAddr, 114 to1Addr, 115 sdk.Coins{fooCoin}, 116 -10, 117 true, 118 ), 119 expErr: true, 120 expErrMsg: "invalid end time", 121 }, 122 "create for existing account": { 123 preRun: func() { 124 toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr) 125 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil) 126 s.accountKeeper.SetAccount(s.ctx, toAcc) 127 s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false) 128 }, 129 input: vestingtypes.NewMsgCreateVestingAccount( 130 fromAddr, 131 to1Addr, 132 sdk.Coins{fooCoin}, 133 time.Now().Unix(), 134 true, 135 ), 136 expErr: true, 137 expErrMsg: "already exists", 138 }, 139 "create for blocked account": { 140 preRun: func() { 141 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil) 142 s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(true) 143 }, 144 input: vestingtypes.NewMsgCreateVestingAccount( 145 fromAddr, 146 to1Addr, 147 sdk.Coins{fooCoin}, 148 time.Now().Unix(), 149 true, 150 ), 151 expErr: true, 152 expErrMsg: "not allowed to receive funds", 153 }, 154 "create a valid delayed vesting account": { 155 preRun: func() { 156 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil) 157 s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false) 158 s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, sdk.Coins{fooCoin}).Return(nil) 159 }, 160 input: vestingtypes.NewMsgCreateVestingAccount( 161 fromAddr, 162 to2Addr, 163 sdk.Coins{fooCoin}, 164 time.Now().Unix(), 165 true, 166 ), 167 expErr: false, 168 expErrMsg: "", 169 }, 170 "create a valid continuous vesting account": { 171 preRun: func() { 172 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil) 173 s.bankKeeper.EXPECT().BlockedAddr(to3Addr).Return(false) 174 s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to3Addr, sdk.Coins{fooCoin}).Return(nil) 175 }, 176 input: vestingtypes.NewMsgCreateVestingAccount( 177 fromAddr, 178 to3Addr, 179 sdk.Coins{fooCoin}, 180 time.Now().Unix(), 181 false, 182 ), 183 expErr: false, 184 expErrMsg: "", 185 }, 186 } 187 188 for name, tc := range testCases { 189 s.Run(name, func() { 190 if tc.preRun != nil { 191 tc.preRun() 192 } 193 _, err := s.msgServer.CreateVestingAccount(s.ctx, tc.input) 194 if tc.expErr { 195 s.Require().Error(err) 196 s.Require().Contains(err.Error(), tc.expErrMsg) 197 } else { 198 s.Require().NoError(err) 199 } 200 }) 201 } 202 } 203 204 func (s *VestingTestSuite) TestCreatePermanentLockedAccount() { 205 testCases := map[string]struct { 206 preRun func() 207 input *vestingtypes.MsgCreatePermanentLockedAccount 208 expErr bool 209 expErrMsg string 210 }{ 211 "empty from address": { 212 input: vestingtypes.NewMsgCreatePermanentLockedAccount( 213 []byte{}, 214 to1Addr, 215 sdk.Coins{fooCoin}, 216 ), 217 expErr: true, 218 expErrMsg: "invalid 'from' address", 219 }, 220 "empty to address": { 221 input: vestingtypes.NewMsgCreatePermanentLockedAccount( 222 fromAddr, 223 []byte{}, 224 sdk.Coins{fooCoin}, 225 ), 226 expErr: true, 227 expErrMsg: "invalid 'to' address", 228 }, 229 "invalid coins": { 230 input: vestingtypes.NewMsgCreatePermanentLockedAccount( 231 fromAddr, 232 to1Addr, 233 sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}}, 234 ), 235 expErr: true, 236 expErrMsg: "-1stake: invalid coins", 237 }, 238 "create for existing account": { 239 preRun: func() { 240 toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr) 241 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil) 242 s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false) 243 s.accountKeeper.SetAccount(s.ctx, toAcc) 244 }, 245 input: vestingtypes.NewMsgCreatePermanentLockedAccount( 246 fromAddr, 247 to1Addr, 248 sdk.Coins{fooCoin}, 249 ), 250 expErr: true, 251 expErrMsg: "already exists", 252 }, 253 "create for blocked account": { 254 preRun: func() { 255 toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr) 256 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil) 257 s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(true) 258 s.accountKeeper.SetAccount(s.ctx, toAcc) 259 }, 260 input: vestingtypes.NewMsgCreatePermanentLockedAccount( 261 fromAddr, 262 to1Addr, 263 sdk.Coins{fooCoin}, 264 ), 265 expErr: true, 266 expErrMsg: "not allowed to receive funds", 267 }, 268 269 "create a valid permanent locked account": { 270 preRun: func() { 271 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil) 272 s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false) 273 s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, sdk.Coins{fooCoin}).Return(nil) 274 }, 275 input: vestingtypes.NewMsgCreatePermanentLockedAccount( 276 fromAddr, 277 to2Addr, 278 sdk.Coins{fooCoin}, 279 ), 280 expErr: false, 281 expErrMsg: "", 282 }, 283 } 284 285 for name, tc := range testCases { 286 s.Run(name, func() { 287 if tc.preRun != nil { 288 tc.preRun() 289 } 290 291 _, err := s.msgServer.CreatePermanentLockedAccount(s.ctx, tc.input) 292 if tc.expErr { 293 s.Require().Error(err) 294 s.Require().Contains(err.Error(), tc.expErrMsg) 295 } else { 296 s.Require().NoError(err) 297 } 298 }) 299 } 300 } 301 302 func (s *VestingTestSuite) TestCreatePeriodicVestingAccount() { 303 testCases := []struct { 304 name string 305 preRun func() 306 input *vestingtypes.MsgCreatePeriodicVestingAccount 307 expErr bool 308 expErrMsg string 309 }{ 310 { 311 name: "empty from address", 312 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 313 []byte{}, 314 to1Addr, 315 time.Now().Unix(), 316 []vestingtypes.Period{ 317 { 318 Length: 10, 319 Amount: sdk.NewCoins(periodCoin), 320 }, 321 }, 322 ), 323 expErr: true, 324 expErrMsg: "invalid 'from' address", 325 }, 326 { 327 name: "empty to address", 328 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 329 fromAddr, 330 []byte{}, 331 time.Now().Unix(), 332 []vestingtypes.Period{ 333 { 334 Length: 10, 335 Amount: sdk.NewCoins(periodCoin), 336 }, 337 }, 338 ), 339 expErr: true, 340 expErrMsg: "invalid 'to' address", 341 }, 342 { 343 name: "invalid start time", 344 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 345 fromAddr, 346 to1Addr, 347 0, 348 []vestingtypes.Period{ 349 { 350 Length: 10, 351 Amount: sdk.NewCoins(periodCoin), 352 }, 353 }, 354 ), 355 expErr: true, 356 expErrMsg: "invalid start time", 357 }, 358 { 359 name: "invalid period", 360 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 361 fromAddr, 362 to1Addr, 363 time.Now().Unix(), 364 []vestingtypes.Period{ 365 { 366 Length: 0, 367 Amount: sdk.NewCoins(periodCoin), 368 }, 369 }, 370 ), 371 expErr: true, 372 expErrMsg: "invalid period", 373 }, 374 { 375 name: "invalid coins", 376 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 377 fromAddr, 378 to1Addr, 379 time.Now().Unix(), 380 []vestingtypes.Period{ 381 { 382 Length: 1, 383 Amount: sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}}, 384 }, 385 }, 386 ), 387 expErr: true, 388 expErrMsg: "-1stake: invalid coins", 389 }, 390 { 391 name: "create for existing account", 392 preRun: func() { 393 s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false) 394 toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr) 395 s.accountKeeper.SetAccount(s.ctx, toAcc) 396 }, 397 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 398 fromAddr, 399 to1Addr, 400 time.Now().Unix(), 401 []vestingtypes.Period{ 402 { 403 Length: 10, 404 Amount: sdk.NewCoins(periodCoin), 405 }, 406 }, 407 ), 408 expErr: true, 409 expErrMsg: "already exists", 410 }, 411 { 412 name: "create for blocked address", 413 preRun: func() { 414 s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(true) 415 }, 416 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 417 fromAddr, 418 to2Addr, 419 time.Now().Unix(), 420 []vestingtypes.Period{ 421 { 422 Length: 10, 423 Amount: sdk.NewCoins(periodCoin), 424 }, 425 { 426 Length: 20, 427 Amount: sdk.NewCoins(fooCoin), 428 }, 429 }, 430 ), 431 expErr: true, 432 expErrMsg: "not allowed to receive funds", 433 }, 434 { 435 name: "create a valid periodic vesting account", 436 preRun: func() { 437 s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), periodCoin.Add(fooCoin)).Return(nil) 438 s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false) 439 s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, gomock.Any()).Return(nil) 440 }, 441 input: vestingtypes.NewMsgCreatePeriodicVestingAccount( 442 fromAddr, 443 to2Addr, 444 time.Now().Unix(), 445 []vestingtypes.Period{ 446 { 447 Length: 10, 448 Amount: sdk.NewCoins(periodCoin), 449 }, 450 { 451 Length: 20, 452 Amount: sdk.NewCoins(fooCoin), 453 }, 454 }, 455 ), 456 expErr: false, 457 expErrMsg: "", 458 }, 459 } 460 461 for _, tc := range testCases { 462 s.Run(tc.name, func() { 463 if tc.preRun != nil { 464 tc.preRun() 465 } 466 _, err := s.msgServer.CreatePeriodicVestingAccount(s.ctx, tc.input) 467 if tc.expErr { 468 s.Require().Error(err) 469 s.Require().Contains(err.Error(), tc.expErrMsg) 470 } else { 471 s.Require().NoError(err) 472 } 473 }) 474 } 475 } 476 477 func TestVestingTestSuite(t *testing.T) { 478 suite.Run(t, new(VestingTestSuite)) 479 }