github.com/lbryio/lbcd@v0.22.119/btcjson/help_test.go (about) 1 // Copyright (c) 2014 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package btcjson_test 6 7 import ( 8 "reflect" 9 "testing" 10 11 "github.com/lbryio/lbcd/btcjson" 12 ) 13 14 // TestHelpReflectInternals ensures the various help functions which deal with 15 // reflect types work as expected for various Go types. 16 func TestHelpReflectInternals(t *testing.T) { 17 t.Parallel() 18 19 tests := []struct { 20 name string 21 reflectType reflect.Type 22 indentLevel int 23 key string 24 examples []string 25 isComplex bool 26 help string 27 isInvalid bool 28 }{ 29 { 30 name: "int", 31 reflectType: reflect.TypeOf(int(0)), 32 key: "json-type-numeric", 33 examples: []string{"n"}, 34 help: "n (json-type-numeric) fdk", 35 }, 36 { 37 name: "*int", 38 reflectType: reflect.TypeOf((*int)(nil)), 39 key: "json-type-value", 40 examples: []string{"n"}, 41 help: "n (json-type-value) fdk", 42 isInvalid: true, 43 }, 44 { 45 name: "int8", 46 reflectType: reflect.TypeOf(int8(0)), 47 key: "json-type-numeric", 48 examples: []string{"n"}, 49 help: "n (json-type-numeric) fdk", 50 }, 51 { 52 name: "int16", 53 reflectType: reflect.TypeOf(int16(0)), 54 key: "json-type-numeric", 55 examples: []string{"n"}, 56 help: "n (json-type-numeric) fdk", 57 }, 58 { 59 name: "int32", 60 reflectType: reflect.TypeOf(int32(0)), 61 key: "json-type-numeric", 62 examples: []string{"n"}, 63 help: "n (json-type-numeric) fdk", 64 }, 65 { 66 name: "int64", 67 reflectType: reflect.TypeOf(int64(0)), 68 key: "json-type-numeric", 69 examples: []string{"n"}, 70 help: "n (json-type-numeric) fdk", 71 }, 72 { 73 name: "uint", 74 reflectType: reflect.TypeOf(uint(0)), 75 key: "json-type-numeric", 76 examples: []string{"n"}, 77 help: "n (json-type-numeric) fdk", 78 }, 79 { 80 name: "uint8", 81 reflectType: reflect.TypeOf(uint8(0)), 82 key: "json-type-numeric", 83 examples: []string{"n"}, 84 help: "n (json-type-numeric) fdk", 85 }, 86 { 87 name: "uint16", 88 reflectType: reflect.TypeOf(uint16(0)), 89 key: "json-type-numeric", 90 examples: []string{"n"}, 91 help: "n (json-type-numeric) fdk", 92 }, 93 { 94 name: "uint32", 95 reflectType: reflect.TypeOf(uint32(0)), 96 key: "json-type-numeric", 97 examples: []string{"n"}, 98 help: "n (json-type-numeric) fdk", 99 }, 100 { 101 name: "uint64", 102 reflectType: reflect.TypeOf(uint64(0)), 103 key: "json-type-numeric", 104 examples: []string{"n"}, 105 help: "n (json-type-numeric) fdk", 106 }, 107 { 108 name: "float32", 109 reflectType: reflect.TypeOf(float32(0)), 110 key: "json-type-numeric", 111 examples: []string{"n.nnn"}, 112 help: "n.nnn (json-type-numeric) fdk", 113 }, 114 { 115 name: "float64", 116 reflectType: reflect.TypeOf(float64(0)), 117 key: "json-type-numeric", 118 examples: []string{"n.nnn"}, 119 help: "n.nnn (json-type-numeric) fdk", 120 }, 121 { 122 name: "string", 123 reflectType: reflect.TypeOf(""), 124 key: "json-type-string", 125 examples: []string{`"json-example-string"`}, 126 help: "\"json-example-string\" (json-type-string) fdk", 127 }, 128 { 129 name: "bool", 130 reflectType: reflect.TypeOf(true), 131 key: "json-type-bool", 132 examples: []string{"json-example-bool"}, 133 help: "json-example-bool (json-type-bool) fdk", 134 }, 135 { 136 name: "array of int", 137 reflectType: reflect.TypeOf([1]int{0}), 138 key: "json-type-arrayjson-type-numeric", 139 examples: []string{"[n,...]"}, 140 help: "[n,...] (json-type-arrayjson-type-numeric) fdk", 141 }, 142 { 143 name: "slice of int", 144 reflectType: reflect.TypeOf([]int{0}), 145 key: "json-type-arrayjson-type-numeric", 146 examples: []string{"[n,...]"}, 147 help: "[n,...] (json-type-arrayjson-type-numeric) fdk", 148 }, 149 { 150 name: "struct", 151 reflectType: reflect.TypeOf(struct{}{}), 152 key: "json-type-object", 153 examples: []string{"{", "}\t\t"}, 154 isComplex: true, 155 help: "{\n} ", 156 }, 157 { 158 name: "struct indent level 1", 159 reflectType: reflect.TypeOf(struct{ field int }{}), 160 indentLevel: 1, 161 key: "json-type-object", 162 examples: []string{ 163 " \"field\": n,\t(json-type-numeric)\t-field", 164 " },\t\t", 165 }, 166 help: "{\n" + 167 " \"field\": n, (json-type-numeric) -field\n" + 168 "} ", 169 isComplex: true, 170 }, 171 { 172 name: "array of struct indent level 0", 173 reflectType: func() reflect.Type { 174 type s struct { 175 field int 176 } 177 return reflect.TypeOf([]s{}) 178 }(), 179 key: "json-type-arrayjson-type-object", 180 examples: []string{ 181 "[{", 182 " \"field\": n,\t(json-type-numeric)\ts-field", 183 "},...]", 184 }, 185 help: "[{\n" + 186 " \"field\": n, (json-type-numeric) s-field\n" + 187 "},...]", 188 isComplex: true, 189 }, 190 { 191 name: "array of struct indent level 1", 192 reflectType: func() reflect.Type { 193 type s struct { 194 field int 195 } 196 return reflect.TypeOf([]s{}) 197 }(), 198 indentLevel: 1, 199 key: "json-type-arrayjson-type-object", 200 examples: []string{ 201 " \"field\": n,\t(json-type-numeric)\ts-field", 202 " },...],\t\t", 203 }, 204 help: "[{\n" + 205 " \"field\": n, (json-type-numeric) s-field\n" + 206 "},...]", 207 isComplex: true, 208 }, 209 { 210 name: "map", 211 reflectType: reflect.TypeOf(map[string]string{}), 212 key: "json-type-object", 213 examples: []string{"{", 214 " \"fdk--key\": fdk--value, (json-type-object) fdk--desc", 215 " ...", "}", 216 }, 217 help: "{\n" + 218 " \"fdk--key\": fdk--value, (json-type-object) fdk--desc\n" + 219 " ...\n" + 220 "}", 221 isComplex: true, 222 }, 223 { 224 name: "complex", 225 reflectType: reflect.TypeOf(complex64(0)), 226 key: "json-type-value", 227 examples: []string{"json-example-unknown"}, 228 help: "json-example-unknown (json-type-value) fdk", 229 isInvalid: true, 230 }, 231 } 232 233 xT := func(key string) string { 234 return key 235 } 236 237 t.Logf("Running %d tests", len(tests)) 238 for i, test := range tests { 239 // Ensure the description key is the expected value. 240 key := btcjson.TstReflectTypeToJSONType(xT, test.reflectType) 241 if key != test.key { 242 t.Errorf("Test #%d (%s) unexpected key - got: %v, "+ 243 "want: %v", i, test.name, key, test.key) 244 continue 245 } 246 247 // Ensure the generated example is as expected. 248 examples, isComplex := btcjson.TstReflectTypeToJSONExample(xT, 249 test.reflectType, test.indentLevel, "fdk") 250 if isComplex != test.isComplex { 251 t.Errorf("Test #%d (%s) unexpected isComplex - got: %v, "+ 252 "want: %v", i, test.name, isComplex, 253 test.isComplex) 254 continue 255 } 256 if len(examples) != len(test.examples) { 257 t.Errorf("Test #%d (%s) unexpected result length - "+ 258 "got: %v, want: %v", i, test.name, len(examples), 259 len(test.examples)) 260 continue 261 } 262 for j, example := range examples { 263 if example != test.examples[j] { 264 t.Errorf("Test #%d (%s) example #%d unexpected "+ 265 "example - got: %v, want: %v", i, 266 test.name, j, example, test.examples[j]) 267 continue 268 } 269 } 270 271 // Ensure the generated result type help is as expected. 272 helpText := btcjson.TstResultTypeHelp(xT, test.reflectType, "fdk") 273 if helpText != test.help { 274 t.Errorf("Test #%d (%s) unexpected result help - "+ 275 "got: %v, want: %v", i, test.name, helpText, 276 test.help) 277 continue 278 } 279 280 isValid := btcjson.TstIsValidResultType(test.reflectType.Kind()) 281 if isValid != !test.isInvalid { 282 t.Errorf("Test #%d (%s) unexpected result type validity "+ 283 "- got: %v", i, test.name, isValid) 284 continue 285 } 286 } 287 } 288 289 // TestResultStructHelp ensures the expected help text format is returned for 290 // various Go struct types. 291 func TestResultStructHelp(t *testing.T) { 292 t.Parallel() 293 294 tests := []struct { 295 name string 296 reflectType reflect.Type 297 expected []string 298 }{ 299 { 300 name: "empty struct", 301 reflectType: func() reflect.Type { 302 type s struct{} 303 return reflect.TypeOf(s{}) 304 }(), 305 expected: nil, 306 }, 307 { 308 name: "struct with primitive field", 309 reflectType: func() reflect.Type { 310 type s struct { 311 field int 312 } 313 return reflect.TypeOf(s{}) 314 }(), 315 expected: []string{ 316 "\"field\": n,\t(json-type-numeric)\ts-field", 317 }, 318 }, 319 { 320 name: "struct with primitive field and json tag", 321 reflectType: func() reflect.Type { 322 type s struct { 323 Field int `json:"f"` 324 } 325 return reflect.TypeOf(s{}) 326 }(), 327 expected: []string{ 328 "\"f\": n,\t(json-type-numeric)\ts-f", 329 }, 330 }, 331 { 332 name: "struct with array of primitive field", 333 reflectType: func() reflect.Type { 334 type s struct { 335 field []int 336 } 337 return reflect.TypeOf(s{}) 338 }(), 339 expected: []string{ 340 "\"field\": [n,...],\t(json-type-arrayjson-type-numeric)\ts-field", 341 }, 342 }, 343 { 344 name: "struct with sub-struct field", 345 reflectType: func() reflect.Type { 346 type s2 struct { 347 subField int 348 } 349 type s struct { 350 field s2 351 } 352 return reflect.TypeOf(s{}) 353 }(), 354 expected: []string{ 355 "\"field\": {\t(json-type-object)\ts-field", 356 "{", 357 " \"subfield\": n,\t(json-type-numeric)\ts2-subfield", 358 "}\t\t", 359 }, 360 }, 361 { 362 name: "struct with sub-struct field pointer", 363 reflectType: func() reflect.Type { 364 type s2 struct { 365 subField int 366 } 367 type s struct { 368 field *s2 369 } 370 return reflect.TypeOf(s{}) 371 }(), 372 expected: []string{ 373 "\"field\": {\t(json-type-object)\ts-field", 374 "{", 375 " \"subfield\": n,\t(json-type-numeric)\ts2-subfield", 376 "}\t\t", 377 }, 378 }, 379 { 380 name: "struct with array of structs field", 381 reflectType: func() reflect.Type { 382 type s2 struct { 383 subField int 384 } 385 type s struct { 386 field []s2 387 } 388 return reflect.TypeOf(s{}) 389 }(), 390 expected: []string{ 391 "\"field\": [{\t(json-type-arrayjson-type-object)\ts-field", 392 "[{", 393 " \"subfield\": n,\t(json-type-numeric)\ts2-subfield", 394 "},...]", 395 }, 396 }, 397 } 398 399 xT := func(key string) string { 400 return key 401 } 402 403 t.Logf("Running %d tests", len(tests)) 404 for i, test := range tests { 405 results := btcjson.TstResultStructHelp(xT, test.reflectType, 0) 406 if len(results) != len(test.expected) { 407 t.Errorf("Test #%d (%s) unexpected result length - "+ 408 "got: %v, want: %v", i, test.name, len(results), 409 len(test.expected)) 410 continue 411 } 412 for j, result := range results { 413 if result != test.expected[j] { 414 t.Errorf("Test #%d (%s) result #%d unexpected "+ 415 "result - got: %v, want: %v", i, 416 test.name, j, result, test.expected[j]) 417 continue 418 } 419 } 420 } 421 } 422 423 // TestHelpArgInternals ensures the various help functions which deal with 424 // arguments work as expected for various argument types. 425 func TestHelpArgInternals(t *testing.T) { 426 t.Parallel() 427 428 tests := []struct { 429 name string 430 method string 431 reflectType reflect.Type 432 defaults map[int]reflect.Value 433 help string 434 }{ 435 { 436 name: "command with no args", 437 method: "test", 438 reflectType: func() reflect.Type { 439 type s struct{} 440 return reflect.TypeOf((*s)(nil)) 441 }(), 442 defaults: nil, 443 help: "", 444 }, 445 { 446 name: "command with one required arg", 447 method: "test", 448 reflectType: func() reflect.Type { 449 type s struct { 450 Field int 451 } 452 return reflect.TypeOf((*s)(nil)) 453 }(), 454 defaults: nil, 455 help: "1. field (json-type-numeric, help-required) test-field\n", 456 }, 457 { 458 name: "command with one optional arg, no default", 459 method: "test", 460 reflectType: func() reflect.Type { 461 type s struct { 462 Optional *int 463 } 464 return reflect.TypeOf((*s)(nil)) 465 }(), 466 defaults: nil, 467 help: "1. optional (json-type-numeric, help-optional) test-optional\n", 468 }, 469 { 470 name: "command with one optional arg with default", 471 method: "test", 472 reflectType: func() reflect.Type { 473 type s struct { 474 Optional *string 475 } 476 return reflect.TypeOf((*s)(nil)) 477 }(), 478 defaults: func() map[int]reflect.Value { 479 defVal := "test" 480 return map[int]reflect.Value{ 481 0: reflect.ValueOf(&defVal), 482 } 483 }(), 484 help: "1. optional (json-type-string, help-optional, help-default=\"test\") test-optional\n", 485 }, 486 { 487 name: "command with struct field", 488 method: "test", 489 reflectType: func() reflect.Type { 490 type s2 struct { 491 F int8 492 } 493 type s struct { 494 Field s2 495 } 496 return reflect.TypeOf((*s)(nil)) 497 }(), 498 defaults: nil, 499 help: "1. field (json-type-object, help-required) test-field\n" + 500 "{\n" + 501 " \"f\": n, (json-type-numeric) s2-f\n" + 502 "} \n", 503 }, 504 { 505 name: "command with map field", 506 method: "test", 507 reflectType: func() reflect.Type { 508 type s struct { 509 Field map[string]float64 510 } 511 return reflect.TypeOf((*s)(nil)) 512 }(), 513 defaults: nil, 514 help: "1. field (json-type-object, help-required) test-field\n" + 515 "{\n" + 516 " \"test-field--key\": test-field--value, (json-type-object) test-field--desc\n" + 517 " ...\n" + 518 "}\n", 519 }, 520 { 521 name: "command with slice of primitives field", 522 method: "test", 523 reflectType: func() reflect.Type { 524 type s struct { 525 Field []int64 526 } 527 return reflect.TypeOf((*s)(nil)) 528 }(), 529 defaults: nil, 530 help: "1. field (json-type-arrayjson-type-numeric, help-required) test-field\n", 531 }, 532 { 533 name: "command with slice of structs field", 534 method: "test", 535 reflectType: func() reflect.Type { 536 type s2 struct { 537 F int64 538 } 539 type s struct { 540 Field []s2 541 } 542 return reflect.TypeOf((*s)(nil)) 543 }(), 544 defaults: nil, 545 help: "1. field (json-type-arrayjson-type-object, help-required) test-field\n" + 546 "[{\n" + 547 " \"f\": n, (json-type-numeric) s2-f\n" + 548 "},...]\n", 549 }, 550 } 551 552 xT := func(key string) string { 553 return key 554 } 555 556 t.Logf("Running %d tests", len(tests)) 557 for i, test := range tests { 558 help := btcjson.TstArgHelp(xT, test.reflectType, test.defaults, 559 test.method) 560 if help != test.help { 561 t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+ 562 "want:\n%v", i, test.name, help, test.help) 563 continue 564 } 565 } 566 } 567 568 // TestMethodHelp ensures the method help function works as expected for various 569 // command structs. 570 func TestMethodHelp(t *testing.T) { 571 t.Parallel() 572 573 tests := []struct { 574 name string 575 method string 576 reflectType reflect.Type 577 defaults map[int]reflect.Value 578 resultTypes []interface{} 579 help string 580 }{ 581 { 582 name: "command with no args or results", 583 method: "test", 584 reflectType: func() reflect.Type { 585 type s struct{} 586 return reflect.TypeOf((*s)(nil)) 587 }(), 588 help: "test\n\ntest--synopsis\n\n" + 589 "help-arguments:\nhelp-arguments-none\n\n" + 590 "help-result:\nhelp-result-nothing\n", 591 }, 592 { 593 name: "command with no args and one primitive result", 594 method: "test", 595 reflectType: func() reflect.Type { 596 type s struct{} 597 return reflect.TypeOf((*s)(nil)) 598 }(), 599 resultTypes: []interface{}{(*int64)(nil)}, 600 help: "test\n\ntest--synopsis\n\n" + 601 "help-arguments:\nhelp-arguments-none\n\n" + 602 "help-result:\nn (json-type-numeric) test--result0\n", 603 }, 604 { 605 name: "command with no args and two results", 606 method: "test", 607 reflectType: func() reflect.Type { 608 type s struct{} 609 return reflect.TypeOf((*s)(nil)) 610 }(), 611 resultTypes: []interface{}{(*int64)(nil), nil}, 612 help: "test\n\ntest--synopsis\n\n" + 613 "help-arguments:\nhelp-arguments-none\n\n" + 614 "help-result (test--condition0):\nn (json-type-numeric) test--result0\n\n" + 615 "help-result (test--condition1):\nhelp-result-nothing\n", 616 }, 617 { 618 name: "command with primitive arg and no results", 619 method: "test", 620 reflectType: func() reflect.Type { 621 type s struct { 622 Field bool 623 } 624 return reflect.TypeOf((*s)(nil)) 625 }(), 626 help: "test field\n\ntest--synopsis\n\n" + 627 "help-arguments:\n1. field (json-type-bool, help-required) test-field\n\n" + 628 "help-result:\nhelp-result-nothing\n", 629 }, 630 { 631 name: "command with primitive optional and no results", 632 method: "test", 633 reflectType: func() reflect.Type { 634 type s struct { 635 Field *bool 636 } 637 return reflect.TypeOf((*s)(nil)) 638 }(), 639 help: "test (field)\n\ntest--synopsis\n\n" + 640 "help-arguments:\n1. field (json-type-bool, help-optional) test-field\n\n" + 641 "help-result:\nhelp-result-nothing\n", 642 }, 643 } 644 645 xT := func(key string) string { 646 return key 647 } 648 649 t.Logf("Running %d tests", len(tests)) 650 for i, test := range tests { 651 help := btcjson.TestMethodHelp(xT, test.reflectType, 652 test.defaults, test.method, test.resultTypes) 653 if help != test.help { 654 t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+ 655 "want:\n%v", i, test.name, help, test.help) 656 continue 657 } 658 } 659 } 660 661 // TestGenerateHelpErrors ensures the GenerateHelp function returns the expected 662 // errors. 663 func TestGenerateHelpErrors(t *testing.T) { 664 t.Parallel() 665 666 tests := []struct { 667 name string 668 method string 669 resultTypes []interface{} 670 err btcjson.Error 671 }{ 672 { 673 name: "unregistered command", 674 method: "boguscommand", 675 err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod}, 676 }, 677 { 678 name: "non-pointer result type", 679 method: "help", 680 resultTypes: []interface{}{0}, 681 err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, 682 }, 683 { 684 name: "invalid result type", 685 method: "help", 686 resultTypes: []interface{}{(*complex64)(nil)}, 687 err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType}, 688 }, 689 { 690 name: "missing description", 691 method: "help", 692 resultTypes: []interface{}{(*string)(nil), nil}, 693 err: btcjson.Error{ErrorCode: btcjson.ErrMissingDescription}, 694 }, 695 } 696 697 t.Logf("Running %d tests", len(tests)) 698 for i, test := range tests { 699 _, err := btcjson.GenerateHelp(test.method, nil, 700 test.resultTypes...) 701 if reflect.TypeOf(err) != reflect.TypeOf(test.err) { 702 t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+ 703 "want %T", i, test.name, err, err, test.err) 704 continue 705 } 706 gotErrorCode := err.(btcjson.Error).ErrorCode 707 if gotErrorCode != test.err.ErrorCode { 708 t.Errorf("Test #%d (%s) mismatched error code - got "+ 709 "%v (%v), want %v", i, test.name, gotErrorCode, 710 err, test.err.ErrorCode) 711 continue 712 } 713 } 714 } 715 716 // TestGenerateHelp performs a very basic test to ensure GenerateHelp is working 717 // as expected. The internal are testd much more thoroughly in other tests, so 718 // there is no need to add more tests here. 719 func TestGenerateHelp(t *testing.T) { 720 t.Parallel() 721 722 descs := map[string]string{ 723 "help--synopsis": "test", 724 "help-command": "test", 725 } 726 help, err := btcjson.GenerateHelp("help", descs) 727 if err != nil { 728 t.Fatalf("GenerateHelp: unexpected error: %v", err) 729 } 730 wantHelp := "help (\"command\")\n\n" + 731 "test\n\nArguments:\n1. command (string, optional) test\n\n" + 732 "Result:\nNothing\n" 733 if help != wantHelp { 734 t.Fatalf("GenerateHelp: unexpected help - got\n%v\nwant\n%v", 735 help, wantHelp) 736 } 737 }