github.com/cosmos/cosmos-sdk@v0.50.10/x/gov/client/cli/tx_test.go (about) 1 package cli_test 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "io" 7 "testing" 8 9 abci "github.com/cometbft/cometbft/abci/types" 10 rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" 11 "github.com/stretchr/testify/suite" 12 13 sdkmath "cosmossdk.io/math" 14 15 "github.com/cosmos/cosmos-sdk/client" 16 "github.com/cosmos/cosmos-sdk/client/flags" 17 "github.com/cosmos/cosmos-sdk/crypto/keyring" 18 "github.com/cosmos/cosmos-sdk/testutil" 19 clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" 20 sdk "github.com/cosmos/cosmos-sdk/types" 21 testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" 22 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 23 "github.com/cosmos/cosmos-sdk/x/gov" 24 "github.com/cosmos/cosmos-sdk/x/gov/client/cli" 25 govclitestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" 26 "github.com/cosmos/cosmos-sdk/x/gov/types" 27 v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" 28 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" 29 ) 30 31 type CLITestSuite struct { 32 suite.Suite 33 34 kr keyring.Keyring 35 encCfg testutilmod.TestEncodingConfig 36 baseCtx client.Context 37 clientCtx client.Context 38 } 39 40 func TestCLITestSuite(t *testing.T) { 41 suite.Run(t, new(CLITestSuite)) 42 } 43 44 func (s *CLITestSuite) SetupSuite() { 45 s.encCfg = testutilmod.MakeTestEncodingConfig(gov.AppModuleBasic{}) 46 s.kr = keyring.NewInMemory(s.encCfg.Codec) 47 s.baseCtx = client.Context{}. 48 WithKeyring(s.kr). 49 WithTxConfig(s.encCfg.TxConfig). 50 WithCodec(s.encCfg.Codec). 51 WithClient(clitestutil.MockCometRPC{Client: rpcclientmock.Client{}}). 52 WithAccountRetriever(client.MockAccountRetriever{}). 53 WithOutput(io.Discard). 54 WithChainID("test-chain") 55 56 ctxGen := func() client.Context { 57 bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) 58 c := clitestutil.NewMockCometRPC(abci.ResponseQuery{ 59 Value: bz, 60 }) 61 return s.baseCtx.WithClient(c) 62 } 63 s.clientCtx = ctxGen() 64 65 val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) 66 67 // create a proposal with deposit 68 _, err := govclitestutil.MsgSubmitLegacyProposal(s.clientCtx, val[0].Address.String(), 69 "Text Proposal 1", "Where is the title!?", v1beta1.ProposalTypeText, 70 fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin("stake", v1.DefaultMinDepositTokens).String())) 71 s.Require().NoError(err) 72 73 // vote for proposal 74 _, err = govclitestutil.MsgVote(s.clientCtx, val[0].Address.String(), "1", "yes") 75 s.Require().NoError(err) 76 77 // create a proposal without deposit 78 _, err = govclitestutil.MsgSubmitLegacyProposal(s.clientCtx, val[0].Address.String(), 79 "Text Proposal 2", "Where is the title!?", v1beta1.ProposalTypeText) 80 s.Require().NoError(err) 81 82 // create a proposal3 with deposit 83 _, err = govclitestutil.MsgSubmitLegacyProposal(s.clientCtx, val[0].Address.String(), 84 "Text Proposal 3", "Where is the title!?", v1beta1.ProposalTypeText, 85 fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin("stake", v1.DefaultMinDepositTokens).String())) 86 s.Require().NoError(err) 87 88 // vote for proposal3 as val 89 _, err = govclitestutil.MsgVote(s.clientCtx, val[0].Address.String(), "3", "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05") 90 s.Require().NoError(err) 91 } 92 93 func (s *CLITestSuite) TestNewCmdSubmitProposal() { 94 val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) 95 96 // Create a legacy proposal JSON, make sure it doesn't pass this new CLI 97 // command. 98 invalidProp := `{ 99 "title": "", 100 "description": "Where is the title!?", 101 "type": "Text", 102 "deposit": "-324foocoin" 103 }` 104 invalidPropFile := testutil.WriteToNewTempFile(s.T(), invalidProp) 105 defer invalidPropFile.Close() 106 107 // Create a valid new proposal JSON. 108 propMetadata := []byte{42} 109 validProp := fmt.Sprintf(` 110 { 111 "messages": [ 112 { 113 "@type": "/cosmos.gov.v1.MsgExecLegacyContent", 114 "authority": "%s", 115 "content": { 116 "@type": "/cosmos.gov.v1beta1.TextProposal", 117 "title": "My awesome title", 118 "description": "My awesome description" 119 } 120 } 121 ], 122 "title": "My awesome title", 123 "summary": "My awesome description", 124 "metadata": "%s", 125 "deposit": "%s" 126 }`, authtypes.NewModuleAddress(types.ModuleName), base64.StdEncoding.EncodeToString(propMetadata), sdk.NewCoin("stake", sdkmath.NewInt(5431))) 127 validPropFile := testutil.WriteToNewTempFile(s.T(), validProp) 128 defer validPropFile.Close() 129 130 testCases := []struct { 131 name string 132 args []string 133 expectErrMsg string 134 }{ 135 { 136 "invalid proposal", 137 []string{ 138 invalidPropFile.Name(), 139 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 140 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 141 }, 142 "invalid decimal coin expression", 143 }, 144 { 145 "valid proposal", 146 []string{ 147 validPropFile.Name(), 148 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 149 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 150 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 151 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 152 }, 153 "", 154 }, 155 } 156 157 for _, tc := range testCases { 158 tc := tc 159 160 s.Run(tc.name, func() { 161 cmd := cli.NewCmdSubmitProposal() 162 163 out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) 164 if tc.expectErrMsg != "" { 165 s.Require().Error(err) 166 s.Require().Contains(err.Error(), tc.expectErrMsg) 167 } else { 168 s.Require().NoError(err) 169 msg := &sdk.TxResponse{} 170 s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), msg), out.String()) 171 } 172 }) 173 } 174 } 175 176 func (s *CLITestSuite) TestNewCmdSubmitLegacyProposal() { 177 val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) 178 179 invalidProp := `{ 180 "title": "", 181 "description": "Where is the title!?", 182 "type": "Text", 183 "deposit": "-324foocoin" 184 }` 185 invalidPropFile := testutil.WriteToNewTempFile(s.T(), invalidProp) 186 defer invalidPropFile.Close() 187 validProp := fmt.Sprintf(`{ 188 "title": "Text Proposal", 189 "description": "Hello, World!", 190 "type": "Text", 191 "deposit": "%s" 192 }`, sdk.NewCoin("stake", sdkmath.NewInt(5431))) 193 validPropFile := testutil.WriteToNewTempFile(s.T(), validProp) 194 defer validPropFile.Close() 195 196 testCases := []struct { 197 name string 198 args []string 199 expectErrMsg string 200 }{ 201 { 202 "invalid proposal (file)", 203 []string{ 204 fmt.Sprintf("--%s=%s", cli.FlagProposal, invalidPropFile.Name()), 205 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 206 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 207 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 208 }, 209 "failed to parse proposal: proposal title is required", 210 }, 211 { 212 "invalid proposal", 213 []string{ 214 fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), 215 fmt.Sprintf("--%s=%s", cli.FlagProposalType, v1beta1.ProposalTypeText), 216 fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin("stake", sdkmath.NewInt(5431)).String()), 217 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 218 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 219 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 220 }, 221 "failed to parse proposal: proposal title is required", 222 }, 223 { 224 "valid transaction (file)", 225 226 []string{ 227 fmt.Sprintf("--%s=%s", cli.FlagProposal, validPropFile.Name()), 228 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 229 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 230 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 231 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 232 }, 233 "", 234 }, 235 { 236 "valid transaction", 237 []string{ 238 fmt.Sprintf("--%s='Text Proposal'", cli.FlagTitle), 239 fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), 240 fmt.Sprintf("--%s=%s", cli.FlagProposalType, v1beta1.ProposalTypeText), 241 fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin("stake", sdkmath.NewInt(5431)).String()), 242 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 243 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 244 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 245 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 246 }, 247 "", 248 }, 249 } 250 251 for _, tc := range testCases { 252 tc := tc 253 254 s.Run(tc.name, func() { 255 cmd := cli.NewCmdSubmitLegacyProposal() 256 257 out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) 258 if tc.expectErrMsg != "" { 259 s.Require().Error(err) 260 s.Require().Contains(err.Error(), tc.expectErrMsg) 261 } else { 262 s.Require().NoError(err) 263 msg := &sdk.TxResponse{} 264 s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), msg), out.String()) 265 } 266 }) 267 } 268 } 269 270 func (s *CLITestSuite) TestNewCmdDeposit() { 271 val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) 272 273 testCases := []struct { 274 name string 275 args []string 276 expectErrMsg string 277 }{ 278 { 279 "invalid proposal id", 280 []string{ 281 "abc", 282 sdk.NewCoin("stake", sdkmath.NewInt(10)).String(), // 10stake 283 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 284 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 285 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 286 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 287 }, 288 "proposal-id abc not a valid uint, please input a valid proposal-id", 289 }, 290 { 291 "without deposit amount", 292 []string{ 293 "1", 294 "invalidCoin", 295 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 296 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 297 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 298 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 299 }, 300 "invalid decimal coin expression: invalidCoin", 301 }, 302 { 303 "deposit on a proposal", 304 []string{ 305 "10", 306 sdk.NewCoin("stake", sdkmath.NewInt(10)).String(), // 10stake 307 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 308 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 309 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 310 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 311 }, 312 "", 313 }, 314 } 315 316 for _, tc := range testCases { 317 tc := tc 318 319 s.Run(tc.name, func() { 320 cmd := cli.NewCmdDeposit() 321 322 out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) 323 if tc.expectErrMsg != "" { 324 s.Require().Error(err) 325 s.Require().Contains(err.Error(), tc.expectErrMsg) 326 } else { 327 s.Require().NoError(err) 328 resp := &sdk.TxResponse{} 329 s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp), out.String()) 330 } 331 }) 332 } 333 } 334 335 func (s *CLITestSuite) TestNewCmdVote() { 336 val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) 337 338 testCases := []struct { 339 name string 340 args []string 341 expectErrMsg string 342 }{ 343 { 344 "vote for invalid proposal", 345 []string{ 346 "abc", 347 "yes", 348 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 349 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 350 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 351 fmt.Sprintf("--metadata=%s", "AQ=="), 352 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 353 }, 354 "proposal-id abc not a valid int, please input a valid proposal-id", 355 }, 356 { 357 "invalid vote", 358 []string{ 359 "1", 360 "AYE", 361 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 362 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 363 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 364 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 365 }, 366 "'AYE' is not a valid vote option", 367 }, 368 { 369 "valid vote", 370 []string{ 371 "1", 372 "yes", 373 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 374 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 375 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 376 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 377 }, 378 "", 379 }, 380 { 381 "valid vote with metadata", 382 []string{ 383 "1", 384 "yes", 385 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 386 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 387 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 388 fmt.Sprintf("--metadata=%s", "AQ=="), 389 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 390 }, 391 "", 392 }, 393 } 394 395 for _, tc := range testCases { 396 tc := tc 397 s.Run(tc.name, func() { 398 cmd := cli.NewCmdVote() 399 out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) 400 401 if tc.expectErrMsg != "" { 402 s.Require().Error(err) 403 s.Require().Contains(err.Error(), tc.expectErrMsg) 404 } else { 405 s.Require().NoError(err) 406 resp := &sdk.TxResponse{} 407 s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp), out.String()) 408 } 409 }) 410 } 411 } 412 413 func (s *CLITestSuite) TestNewCmdWeightedVote() { 414 val := testutil.CreateKeyringAccounts(s.T(), s.kr, 1) 415 416 testCases := []struct { 417 name string 418 args []string 419 expectErrMsg string 420 }{ 421 { 422 "vote for invalid proposal", 423 []string{ 424 "abc", 425 "yes", 426 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 427 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 428 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 429 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 430 }, 431 "proposal-id abc not a valid int, please input a valid proposal-id", 432 }, 433 { 434 "invalid vote", 435 []string{ 436 "1", 437 "AYE", 438 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 439 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 440 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 441 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 442 }, 443 "'AYE' is not a valid vote option", 444 }, 445 { 446 "valid vote", 447 []string{ 448 "1", 449 "yes", 450 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 451 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 452 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 453 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 454 }, 455 "", 456 }, 457 { 458 "valid vote with metadata", 459 []string{ 460 "1", 461 "yes", 462 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 463 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 464 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 465 fmt.Sprintf("--metadata=%s", "AQ=="), 466 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 467 }, 468 "", 469 }, 470 { 471 "invalid valid split vote string", 472 []string{ 473 "1", 474 "yes/0.6,no/0.3,abstain/0.05,no_with_veto/0.05", 475 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 476 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 477 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 478 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 479 }, 480 "'yes/0.6' is not a valid vote option", 481 }, 482 { 483 "valid split vote", 484 []string{ 485 "1", 486 "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05", 487 fmt.Sprintf("--%s=%s", flags.FlagFrom, val[0].Address.String()), 488 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 489 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), 490 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))).String()), 491 }, 492 "", 493 }, 494 } 495 496 for _, tc := range testCases { 497 tc := tc 498 s.Run(tc.name, func() { 499 cmd := cli.NewCmdWeightedVote() 500 out, err := clitestutil.ExecTestCLICmd(s.clientCtx, cmd, tc.args) 501 if tc.expectErrMsg != "" { 502 s.Require().Error(err) 503 s.Require().Contains(err.Error(), tc.expectErrMsg) 504 } else { 505 s.Require().NoError(err) 506 resp := &sdk.TxResponse{} 507 s.Require().NoError(s.clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp), out.String()) 508 } 509 }) 510 } 511 }