cosmossdk.io/client/v2@v2.0.0-beta.1/autocli/query_test.go (about) 1 package autocli 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/spf13/cobra" 11 "google.golang.org/protobuf/testing/protocmp" 12 "google.golang.org/protobuf/types/known/durationpb" 13 "google.golang.org/protobuf/types/known/timestamppb" 14 "gotest.tools/v3/assert" 15 "gotest.tools/v3/golden" 16 17 autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" 18 queryv1beta1 "cosmossdk.io/api/cosmos/base/query/v1beta1" 19 basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1" 20 "cosmossdk.io/client/v2/internal/testpb" 21 ) 22 23 var buildModuleQueryCommand = func(moduleName string, b *Builder) (*cobra.Command, error) { 24 cmd := topLevelCmd(moduleName, fmt.Sprintf("Querying commands for the %s module", moduleName)) 25 26 err := b.AddQueryServiceCommands(cmd, testCmdDesc) 27 return cmd, err 28 } 29 30 var buildModuleQueryCommandOptional = func(moduleName string, b *Builder) (*cobra.Command, error) { 31 cmd := topLevelCmd(moduleName, fmt.Sprintf("Querying commands for the %s module", moduleName)) 32 33 err := b.AddQueryServiceCommands(cmd, testCmdDescOptional) 34 return cmd, err 35 } 36 37 var buildModuleVargasOptional = func(moduleName string, b *Builder) (*cobra.Command, error) { 38 cmd := topLevelCmd(moduleName, fmt.Sprintf("Querying commands for the %s module", moduleName)) 39 40 err := b.AddQueryServiceCommands(cmd, testCmdDescInvalidOptAndVargas) 41 return cmd, err 42 } 43 44 var testCmdDesc = &autocliv1.ServiceCommandDescriptor{ 45 Service: testpb.Query_ServiceDesc.ServiceName, 46 RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 47 { 48 RpcMethod: "Echo", 49 Use: "echo [pos1] [pos2] [pos3...]", 50 Version: "1.0", 51 Alias: []string{"e"}, 52 SuggestFor: []string{"eco"}, 53 Example: "echo 1 abc {}", 54 Short: "echo echos the value provided by the user", 55 Long: "echo echos the value provided by the user as a proto JSON object with populated with the provided fields and positional arguments", 56 PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 57 { 58 ProtoField: "positional1", 59 }, 60 { 61 ProtoField: "positional2", 62 }, 63 { 64 ProtoField: "positional3_varargs", 65 Varargs: true, 66 }, 67 }, 68 FlagOptions: map[string]*autocliv1.FlagOptions{ 69 "u32": { 70 Name: "uint32", 71 Shorthand: "u", 72 Usage: "some random uint32", 73 }, 74 "i32": { 75 Usage: "some random int32", 76 DefaultValue: "3", 77 }, 78 "u64": { 79 Usage: "some random uint64", 80 DefaultValue: "5", 81 }, 82 "deprecated_field": { 83 Deprecated: "don't use this", 84 }, 85 "shorthand_deprecated_field": { 86 Shorthand: "s", 87 Deprecated: "bad idea", 88 }, 89 "hidden_bool": { 90 Hidden: true, 91 }, 92 "a_coin": { 93 Usage: "some random coin", 94 }, 95 "duration": { 96 Usage: "some random duration", 97 }, 98 "bz": { 99 Usage: "some bytes", 100 }, 101 "map_string_string": { 102 Usage: "some map of string to string", 103 }, 104 "map_string_uint32": { 105 Usage: "some map of string to int32", 106 }, 107 "map_string_coin": { 108 Usage: "some map of string to coin", 109 }, 110 }, 111 }, 112 }, 113 SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{ 114 // we test the sub-command functionality using the same service with different options 115 "deprecatedecho": { 116 Service: testpb.Query_ServiceDesc.ServiceName, 117 RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 118 { 119 RpcMethod: "Echo", 120 Deprecated: "don't use this", 121 }, 122 }, 123 }, 124 "skipecho": { 125 Service: testpb.Query_ServiceDesc.ServiceName, 126 RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 127 { 128 RpcMethod: "Echo", 129 Skip: true, 130 }, 131 }, 132 }, 133 }, 134 } 135 136 var testCmdDescOptional = &autocliv1.ServiceCommandDescriptor{ 137 Service: testpb.Query_ServiceDesc.ServiceName, 138 RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 139 { 140 RpcMethod: "Echo", 141 Use: "echo [pos1] [pos2] [pos3...]", 142 Version: "1.0", 143 Alias: []string{"e"}, 144 SuggestFor: []string{"eco"}, 145 Example: "echo 1 abc {}", 146 Short: "echo echos the value provided by the user", 147 Long: "echo echos the value provided by the user as a proto JSON object with populated with the provided fields and positional arguments", 148 PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 149 { 150 ProtoField: "positional1", 151 }, 152 { 153 ProtoField: "positional2", 154 Optional: true, 155 }, 156 }, 157 }, 158 }, 159 } 160 161 var testCmdDescInvalidOptAndVargas = &autocliv1.ServiceCommandDescriptor{ 162 Service: testpb.Query_ServiceDesc.ServiceName, 163 RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 164 { 165 RpcMethod: "Echo", 166 Use: "echo [pos1] [pos2] [pos3...]", 167 Version: "1.0", 168 Alias: []string{"e"}, 169 SuggestFor: []string{"eco"}, 170 Example: "echo 1 abc {}", 171 Short: "echo echos the value provided by the user", 172 Long: "echo echos the value provided by the user as a proto JSON object with populated with the provided fields and positional arguments", 173 PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 174 { 175 ProtoField: "positional1", 176 }, 177 { 178 ProtoField: "positional2", 179 Optional: true, 180 }, 181 { 182 ProtoField: "positional3_varargs", 183 Varargs: true, 184 }, 185 }, 186 }, 187 }, 188 } 189 190 func TestCoin(t *testing.T) { 191 fixture := initFixture(t) 192 193 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 194 "echo", 195 "1", 196 "abc", 197 "1234foo,4321bar", 198 "100uatom", 199 "--a-coin", "100000foo", 200 ) 201 assert.ErrorContains(t, err, "coin flag must be a single coin, specific multiple coins with multiple flags or spaces") 202 203 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 204 "echo", 205 "1", 206 "abc", 207 "1234foo", 208 "4321bar", 209 "100uatom", 210 "--a-coin", "100000foo", 211 "--coins", "100000bar", 212 "--coins", "100uatom", 213 ) 214 assert.NilError(t, err) 215 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 216 expectedResp := &testpb.EchoResponse{ 217 Request: &testpb.EchoRequest{ 218 Positional1: 1, 219 Positional2: "abc", 220 Positional3Varargs: []*basev1beta1.Coin{ 221 {Amount: "1234", Denom: "foo"}, 222 {Amount: "4321", Denom: "bar"}, 223 {Amount: "100", Denom: "uatom"}, 224 }, 225 ACoin: &basev1beta1.Coin{ 226 Amount: "100000", 227 Denom: "foo", 228 }, 229 Coins: []*basev1beta1.Coin{ 230 {Amount: "100000", Denom: "bar"}, 231 {Amount: "100", Denom: "uatom"}, 232 }, 233 Page: &queryv1beta1.PageRequest{}, 234 I32: 3, 235 U64: 5, 236 }, 237 } 238 assert.DeepEqual(t, fixture.conn.lastResponse.(*testpb.EchoResponse), expectedResp, protocmp.Transform()) 239 } 240 241 func TestOptional(t *testing.T) { 242 fixture := initFixture(t) 243 244 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommandOptional, 245 "echo", 246 "1", 247 "abc", 248 ) 249 assert.NilError(t, err) 250 request := fixture.conn.lastRequest.(*testpb.EchoRequest) 251 assert.Equal(t, request.Positional2, "abc") 252 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 253 254 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommandOptional, 255 "echo", 256 "1", 257 ) 258 assert.NilError(t, err) 259 260 request = fixture.conn.lastRequest.(*testpb.EchoRequest) 261 assert.Equal(t, request.Positional2, "") 262 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 263 264 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommandOptional, 265 "echo", 266 "1", 267 "abc", 268 "extra-arg", 269 ) 270 assert.ErrorContains(t, err, "accepts between 1 and 2 arg(s), received 3") 271 272 _, err = runCmd(fixture.conn, fixture.b, buildModuleVargasOptional, 273 "echo", 274 "1", 275 "abc", 276 "extra-arg", 277 ) 278 assert.ErrorContains(t, err, "optional positional argument positional2 must be the last argument") 279 } 280 281 func TestMap(t *testing.T) { 282 fixture := initFixture(t) 283 284 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 285 "echo", 286 "1", 287 "abc", 288 "1234foo", 289 "4321bar", 290 "--map-string-uint32", "bar=123", 291 "--map-string-string", "val=foo", 292 "--map-string-coin", "baz=100000foo", 293 "--map-string-coin", "sec=100000bar", 294 "--map-string-coin", "multi=100000bar,flag=100000foo", 295 ) 296 assert.NilError(t, err) 297 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 298 299 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 300 "echo", 301 "1", 302 "abc", 303 "1234foo", 304 "4321bar", 305 "--map-string-uint32", "bar=123", 306 "--map-string-coin", "baz,100000foo", 307 "--map-string-coin", "sec=100000bar", 308 ) 309 assert.ErrorContains(t, err, "invalid argument \"baz,100000foo\" for \"--map-string-coin\" flag: invalid format, expected key=value") 310 311 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 312 "echo", 313 "1", 314 "abc", 315 "1234foo", 316 "4321bar", 317 "--map-string-uint32", "bar=not-unint32", 318 "--map-string-coin", "baz=100000foo", 319 "--map-string-coin", "sec=100000bar", 320 ) 321 assert.ErrorContains(t, err, "invalid argument \"bar=not-unint32\" for \"--map-string-uint32\" flag: strconv.ParseUint: parsing \"not-unint32\": invalid syntax") 322 323 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 324 "echo", 325 "1", 326 "abc", 327 "1234foo", 328 "4321bar", 329 "--map-string-uint32", "bar=123.9", 330 "--map-string-coin", "baz=100000foo", 331 "--map-string-coin", "sec=100000bar", 332 ) 333 assert.ErrorContains(t, err, "invalid argument \"bar=123.9\" for \"--map-string-uint32\" flag: strconv.ParseUint: parsing \"123.9\": invalid syntax") 334 } 335 336 // TestEveything tests all the different types of flags are correctly read and as well as correctly returned 337 // This tests the flag binding and the message building 338 func TestEverything(t *testing.T) { 339 fixture := initFixture(t) 340 341 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 342 "echo", 343 "1", 344 "abc", 345 "123.123123124foo", 346 "4321bar", 347 "--a-bool", 348 "--an-enum", "one", 349 "--a-message", `{"bar":"abc", "baz":-3}`, 350 "--duration", "4h3s", 351 "--uint32", "27", 352 "--u64", "3267246890", 353 "--i32", "-253", 354 "--i64", "-234602347", 355 "--str", "def", 356 "--timestamp", "2019-01-02T00:01:02Z", 357 "--a-coin", "100000foo", 358 "--an-address", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", 359 "--a-validator-address", "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", 360 "--a-consensus-address", "cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr", 361 "--bz", "c2RncXdlZndkZ3NkZw==", 362 "--page-count-total", 363 "--page-key", "MTIzNTQ4N3NnaGRhcw==", 364 "--page-limit", "1000", 365 "--page-offset", "10", 366 "--page-reverse", 367 "--bools", "true", 368 "--bools", "false,false,true", 369 "--enums", "one", 370 "--enums", "five", 371 "--enums", "two", 372 "--strings", "abc", 373 "--strings", "xyz", 374 "--strings", "xyz,qrs", 375 "--durations", "3s", 376 "--durations", "5s", 377 "--durations", "10h", 378 "--some-messages", "{}", 379 "--some-messages", `{"bar":"baz"}`, 380 "--some-messages", `{"baz":-1}`, 381 "--uints", "1,2,3", 382 "--uints", "4", 383 ) 384 assert.NilError(t, err) 385 386 expectedResp := &testpb.EchoResponse{ 387 Request: &testpb.EchoRequest{ 388 Positional1: 1, 389 Positional2: "abc", 390 Positional3Varargs: []*basev1beta1.Coin{ 391 {Amount: "123.123123124", Denom: "foo"}, 392 {Amount: "4321", Denom: "bar"}, 393 }, 394 ABool: true, 395 AnEnum: testpb.Enum_ENUM_ONE, 396 AMessage: &testpb.AMessage{ 397 Bar: "abc", 398 Baz: -3, 399 }, 400 Duration: durationpb.New(4*time.Hour + 3*time.Second), 401 U32: 27, 402 U64: 3267246890, 403 I32: -253, 404 I64: -234602347, 405 Str: "def", 406 Timestamp: ×tamppb.Timestamp{ 407 Seconds: 1546387262, 408 }, 409 ACoin: &basev1beta1.Coin{ 410 Amount: "100000", 411 Denom: "foo", 412 }, 413 AnAddress: "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", 414 AValidatorAddress: "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", 415 AConsensusAddress: "cosmosvalcons16vm0nx49eam4q0xasdnwdzsdl6ymgyjt757sgr", 416 Bz: []byte("sdgqwefwdgsdg"), 417 Page: &queryv1beta1.PageRequest{ 418 CountTotal: true, 419 Key: []byte("1235487sghdas"), 420 Limit: 1000, 421 Offset: 10, 422 Reverse: true, 423 }, 424 Bools: []bool{true, false, false, true}, 425 Enums: []testpb.Enum{testpb.Enum_ENUM_ONE, testpb.Enum_ENUM_FIVE, testpb.Enum_ENUM_TWO}, 426 Strings: []string{ 427 "abc", 428 "xyz", 429 "xyz", 430 "qrs", 431 }, 432 Durations: []*durationpb.Duration{ 433 durationpb.New(3 * time.Second), 434 durationpb.New(5 * time.Second), 435 durationpb.New(10 * time.Hour), 436 }, 437 SomeMessages: []*testpb.AMessage{ 438 {}, 439 {Bar: "baz"}, 440 {Baz: -1}, 441 }, 442 Uints: []uint32{1, 2, 3, 4}, 443 }, 444 } 445 446 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 447 assert.DeepEqual(t, fixture.conn.lastResponse.(*testpb.EchoResponse), expectedResp, protocmp.Transform()) 448 } 449 450 func TestPubKeyParsingConsensusAddress(t *testing.T) { 451 fixture := initFixture(t) 452 453 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 454 "echo", 455 "1", "abc", "1foo", 456 "--a-consensus-address", "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"j8qdbR+AlH/V6aBTCSWXRvX3JUESF2bV+SEzndBhF0o=\"}", 457 "-u", "27", // shorthand 458 ) 459 assert.NilError(t, err) 460 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 461 } 462 463 func TestJSONParsing(t *testing.T) { 464 fixture := initFixture(t) 465 466 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 467 "echo", 468 "1", "abc", "1foo", 469 "--some-messages", `{"bar":"baz"}`, 470 "-u", "27", // shorthand 471 ) 472 assert.NilError(t, err) 473 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 474 475 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 476 "echo", 477 "1", "abc", "1foo", 478 "--some-messages", "testdata/some_message.json", 479 "-u", "27", // shorthand 480 ) 481 assert.NilError(t, err) 482 assert.DeepEqual(t, fixture.conn.lastRequest, fixture.conn.lastResponse.(*testpb.EchoResponse).Request, protocmp.Transform()) 483 } 484 485 func TestOptions(t *testing.T) { 486 fixture := initFixture(t) 487 488 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 489 "echo", 490 "1", "abc", "123foo", 491 "-u", "27", // shorthand 492 "--u64", "5", // no opt default value 493 ) 494 assert.NilError(t, err) 495 496 lastReq := fixture.conn.lastRequest.(*testpb.EchoRequest) 497 assert.Equal(t, uint32(27), lastReq.U32) // shorthand got set 498 assert.Equal(t, int32(3), lastReq.I32) // default value got set 499 assert.Equal(t, uint64(5), lastReq.U64) // no opt default value got set 500 } 501 502 func TestBinaryFlag(t *testing.T) { 503 // Create a temporary file with some content 504 tempFile, err := os.Open("testdata/file.test") 505 if err != nil { 506 t.Fatal(err) 507 } 508 content := []byte("this is just a test file") 509 if err := tempFile.Close(); err != nil { 510 t.Fatal(err) 511 } 512 513 // Test cases 514 tests := []struct { 515 name string 516 input string 517 expected []byte 518 hasError bool 519 err string 520 }{ 521 { 522 name: "Valid file path with extension", 523 input: tempFile.Name(), 524 expected: content, 525 hasError: false, 526 err: "", 527 }, 528 { 529 name: "Valid hex-encoded string", 530 input: "68656c6c6f20776f726c64", 531 expected: []byte("hello world"), 532 hasError: false, 533 err: "", 534 }, 535 { 536 name: "Valid base64-encoded string", 537 input: "SGVsbG8gV29ybGQ=", 538 expected: []byte("Hello World"), 539 hasError: false, 540 err: "", 541 }, 542 { 543 name: "Invalid input (not a file path or encoded string)", 544 input: "not a file or encoded string", 545 expected: nil, 546 hasError: true, 547 err: "input string is neither a valid file path, hex, or base64 encoded", 548 }, 549 } 550 551 // Run test cases 552 fixture := initFixture(t) 553 for _, tc := range tests { 554 t.Run(tc.name, func(t *testing.T) { 555 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 556 "echo", 557 "1", "abc", `100foo`, 558 "--bz", tc.input, 559 ) 560 if tc.hasError { 561 assert.ErrorContains(t, err, tc.err) 562 } else { 563 assert.NilError(t, err) 564 lastReq := fixture.conn.lastRequest.(*testpb.EchoRequest) 565 assert.DeepEqual(t, tc.expected, lastReq.Bz) 566 } 567 }) 568 } 569 } 570 571 func TestAddressValidation(t *testing.T) { 572 fixture := initFixture(t) 573 574 _, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 575 "echo", 576 "1", "abc", "1foo", 577 "--an-address", "cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", 578 ) 579 assert.NilError(t, err) 580 581 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 582 "echo", 583 "1", "abc", "1foo", 584 "--an-address", "regen1y74p8wyy4enfhfn342njve6cjmj5c8dtlqj7ule2", 585 ) 586 assert.ErrorContains(t, err, "invalid account address") 587 588 _, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 589 "echo", 590 "1", "abc", "1foo", 591 "--an-address", "cosmps1BAD_ENCODING", 592 ) 593 assert.ErrorContains(t, err, "invalid account address") 594 } 595 596 func TestOutputFormat(t *testing.T) { 597 fixture := initFixture(t) 598 599 out, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 600 "echo", 601 "1", "abc", "1foo", 602 "--output", "json", 603 ) 604 assert.NilError(t, err) 605 assert.Assert(t, strings.Contains(out.String(), "{")) 606 607 out, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, 608 "echo", 609 "1", "abc", "1foo", 610 "--output", "text", 611 ) 612 assert.NilError(t, err) 613 assert.Assert(t, strings.Contains(out.String(), " positional1: 1")) 614 } 615 616 func TestHelpQuery(t *testing.T) { 617 fixture := initFixture(t) 618 619 out, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "-h") 620 assert.NilError(t, err) 621 golden.Assert(t, out.String(), "help-toplevel.golden") 622 623 out, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "echo", "-h") 624 assert.NilError(t, err) 625 golden.Assert(t, out.String(), "help-echo.golden") 626 627 out, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "deprecatedecho", "echo", "-h") 628 assert.NilError(t, err) 629 golden.Assert(t, out.String(), "help-deprecated.golden") 630 631 out, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "skipecho", "-h") 632 assert.NilError(t, err) 633 golden.Assert(t, out.String(), "help-skip.golden") 634 } 635 636 func TestDeprecatedQuery(t *testing.T) { 637 fixture := initFixture(t) 638 639 out, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "echo", 640 "1", "abc", "--deprecated-field", "foo") 641 assert.NilError(t, err) 642 assert.Assert(t, strings.Contains(out.String(), "--deprecated-field has been deprecated")) 643 644 out, err = runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "echo", 645 "1", "abc", "-s", "foo") 646 assert.NilError(t, err) 647 assert.Assert(t, strings.Contains(out.String(), "--shorthand-deprecated-field has been deprecated")) 648 } 649 650 func TestBuildCustomQueryCommand(t *testing.T) { 651 b := &Builder{} 652 customCommandCalled := false 653 654 appOptions := AppOptions{ 655 ModuleOptions: map[string]*autocliv1.ModuleOptions{ 656 "test": { 657 Query: testCmdDesc, 658 }, 659 }, 660 } 661 662 cmd, err := b.BuildQueryCommand(appOptions, map[string]*cobra.Command{ 663 "test": {Use: "test", Run: func(cmd *cobra.Command, args []string) { 664 customCommandCalled = true 665 }}, 666 }) 667 assert.NilError(t, err) 668 cmd.SetArgs([]string{"test", "query"}) 669 assert.NilError(t, cmd.Execute()) 670 assert.Assert(t, customCommandCalled) 671 } 672 673 func TestNotFoundErrorsQuery(t *testing.T) { 674 fixture := initFixture(t) 675 b := fixture.b 676 b.AddQueryConnFlags = nil 677 b.AddTxConnFlags = nil 678 679 buildModuleQueryCommand := func(moduleName string, cmdDescriptor *autocliv1.ServiceCommandDescriptor) (*cobra.Command, error) { 680 cmd := topLevelCmd("query", "Querying subcommands") 681 err := b.AddMsgServiceCommands(cmd, cmdDescriptor) 682 return cmd, err 683 } 684 685 // bad service 686 _, err := buildModuleQueryCommand("test", &autocliv1.ServiceCommandDescriptor{Service: "foo"}) 687 assert.ErrorContains(t, err, "can't find service foo") 688 689 // bad method 690 _, err = buildModuleQueryCommand("test", &autocliv1.ServiceCommandDescriptor{ 691 Service: testpb.Query_ServiceDesc.ServiceName, 692 RpcCommandOptions: []*autocliv1.RpcCommandOptions{{RpcMethod: "bar"}}, 693 }) 694 assert.ErrorContains(t, err, "rpc method \"bar\" not found") 695 696 // bad positional field 697 _, err = buildModuleQueryCommand("test", &autocliv1.ServiceCommandDescriptor{ 698 Service: testpb.Query_ServiceDesc.ServiceName, 699 RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 700 { 701 RpcMethod: "Echo", 702 PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 703 { 704 ProtoField: "foo", 705 }, 706 }, 707 }, 708 }, 709 }) 710 assert.ErrorContains(t, err, "can't find field foo") 711 712 // bad flag field 713 _, err = buildModuleQueryCommand("test", &autocliv1.ServiceCommandDescriptor{ 714 Service: testpb.Query_ServiceDesc.ServiceName, 715 RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 716 { 717 RpcMethod: "Echo", 718 FlagOptions: map[string]*autocliv1.FlagOptions{ 719 "baz": {}, 720 }, 721 }, 722 }, 723 }) 724 assert.ErrorContains(t, err, "can't find field baz") 725 } 726 727 func TestDurationMarshal(t *testing.T) { 728 fixture := initFixture(t) 729 730 out, err := runCmd(fixture.conn, fixture.b, buildModuleQueryCommand, "echo", "1", "abc", "--duration", "1s") 731 assert.NilError(t, err) 732 fmt.Println(out.String()) 733 assert.Assert(t, strings.Contains(out.String(), "duration: 1s")) 734 }