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