github.com/greenpau/go-authcrunch@v1.1.4/pkg/kms/config_test.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kms 16 17 import ( 18 "fmt" 19 "github.com/google/go-cmp/cmp" 20 "github.com/greenpau/go-authcrunch/internal/tests" 21 "github.com/greenpau/go-authcrunch/pkg/errors" 22 "os" 23 "testing" 24 ) 25 26 func TestValidateCryptoKeyConfig(t *testing.T) { 27 var testcases = []struct { 28 name string 29 config *CryptoKeyConfig 30 shouldErr bool 31 err error 32 }{ 33 { 34 name: "default shared key in default context for verify", 35 config: &CryptoKeyConfig{ 36 ID: "0", 37 Usage: "verify", 38 TokenName: "foobar token", 39 Source: "config", 40 Algorithm: "hmac", 41 Secret: "foobar", 42 TokenLifetime: 900, 43 parsed: true, 44 }, 45 }, 46 { 47 name: "invalid key usage", 48 config: &CryptoKeyConfig{ 49 ID: "0", 50 Usage: "both", 51 TokenName: "foobar token", 52 Source: "config", 53 Algorithm: "hmac", 54 Secret: "foobar", 55 TokenLifetime: 900, 56 parsed: true, 57 }, 58 shouldErr: true, 59 err: fmt.Errorf("key usage %q is invalid", "both"), 60 }, 61 { 62 name: "empty key usage", 63 config: &CryptoKeyConfig{ 64 ID: "0", 65 TokenName: "foobar token", 66 Source: "config", 67 Algorithm: "hmac", 68 Secret: "foobar", 69 TokenLifetime: 900, 70 parsed: true, 71 }, 72 shouldErr: true, 73 err: fmt.Errorf("key usage is not set"), 74 }, 75 { 76 name: "invalid key source", 77 config: &CryptoKeyConfig{ 78 ID: "0", 79 Usage: "verify", 80 TokenName: "foobar token", 81 Source: "foo", 82 Algorithm: "hmac", 83 Secret: "foobar", 84 TokenLifetime: 900, 85 parsed: true, 86 }, 87 shouldErr: true, 88 err: fmt.Errorf("key source %q is invalid", "foo"), 89 }, 90 { 91 name: "empty key source", 92 config: &CryptoKeyConfig{ 93 ID: "0", 94 Usage: "verify", 95 TokenName: "foobar token", 96 Algorithm: "hmac", 97 Secret: "foobar", 98 TokenLifetime: 900, 99 parsed: true, 100 }, 101 shouldErr: true, 102 err: fmt.Errorf("key source not found"), 103 }, 104 { 105 name: "invalid key algo", 106 config: &CryptoKeyConfig{ 107 ID: "0", 108 Usage: "verify", 109 TokenName: "foobar token", 110 Source: "config", 111 Algorithm: "foo", 112 Secret: "foobar", 113 TokenLifetime: 900, 114 parsed: true, 115 }, 116 shouldErr: true, 117 err: fmt.Errorf("key algorithm %q is invalid", "foo"), 118 }, 119 { 120 name: "empty source type for env", 121 config: &CryptoKeyConfig{ 122 ID: "cb315f43c868", 123 Usage: "verify", 124 Source: "env", 125 EnvVarName: "JWT_SECRET_KEY", 126 TokenName: "access_token", 127 TokenLifetime: 900, 128 parsed: true, 129 validated: true, 130 }, 131 shouldErr: true, 132 err: fmt.Errorf("key source type for env not set"), 133 }, 134 { 135 name: "invalid source type for env", 136 config: &CryptoKeyConfig{ 137 ID: "cb315f43c868", 138 Usage: "verify", 139 Source: "env", 140 EnvVarName: "JWT_SECRET_KEY", 141 EnvVarType: "foo", 142 TokenName: "access_token", 143 TokenLifetime: 900, 144 parsed: true, 145 validated: true, 146 }, 147 shouldErr: true, 148 err: fmt.Errorf("key source type %q for env is invalid", "foo"), 149 }, 150 } 151 for _, tc := range testcases { 152 t.Run(tc.name, func(t *testing.T) { 153 msgs := []string{fmt.Sprintf("test name: %s", tc.name)} 154 msgs = append(msgs, fmt.Sprintf("config: %v", tc.config)) 155 err := tc.config.validate() 156 if tests.EvalErrWithLog(t, err, nil, tc.shouldErr, tc.err, msgs) { 157 return 158 } 159 }) 160 } 161 } 162 163 func TestParseCryptoKeyConfigs(t *testing.T) { 164 var testcases = []struct { 165 name string 166 config string 167 env map[string]string 168 want map[string]interface{} 169 shouldErr bool 170 err error 171 }{ 172 { 173 name: "default shared key in default context for verify", 174 config: ` 175 crypto default token lifetime 2400 176 crypto key token name "foobar token" 177 crypto key verify foobar 178 `, 179 want: map[string]interface{}{ 180 "config_count": 1, 181 "configs": []*CryptoKeyConfig{ 182 { 183 ID: "0", 184 Usage: "verify", 185 TokenName: "foobar token", 186 Source: "config", 187 Algorithm: "hmac", 188 Secret: "foobar", 189 TokenLifetime: 2400, 190 parsed: true, 191 validated: true, 192 }, 193 }, 194 }, 195 }, 196 { 197 name: "default shared key in default context for both sign and verify", 198 config: ` 199 crypto key token name "foobar token" 200 crypto key token lifetime 1800 201 crypto key sign-verify foobar 202 `, 203 want: map[string]interface{}{ 204 "config_count": 1, 205 "configs": []*CryptoKeyConfig{ 206 { 207 ID: "0", 208 Usage: "sign-verify", 209 TokenName: "foobar token", 210 Source: "config", 211 Algorithm: "hmac", 212 Secret: "foobar", 213 TokenLifetime: 1800, 214 parsed: true, 215 validated: true, 216 }, 217 }, 218 }, 219 }, 220 { 221 name: "multiple shared keys in default context", 222 config: ` 223 crypto key token name "foobar token" 224 crypto key verify foobar 225 crypto key abc123 token name foobar_token 226 crypto key abc123 verify foobar 227 `, 228 want: map[string]interface{}{ 229 "config_count": 2, 230 "configs": []*CryptoKeyConfig{ 231 { 232 ID: "0", 233 Usage: "verify", 234 TokenName: "foobar token", 235 Source: "config", 236 Algorithm: "hmac", 237 Secret: "foobar", 238 TokenLifetime: 900, 239 parsed: true, 240 validated: true, 241 }, 242 { 243 Seq: 1, 244 ID: "abc123", 245 Usage: "verify", 246 TokenName: "foobar_token", 247 Source: "config", 248 Algorithm: "hmac", 249 Secret: "foobar", 250 TokenLifetime: 900, 251 parsed: true, 252 validated: true, 253 }, 254 }, 255 }, 256 }, 257 { 258 name: "multiple shared keys in with implicit token name config", 259 config: ` 260 crypto key verify foobar 261 crypto key abc123 verify foobar 262 `, 263 want: map[string]interface{}{ 264 "config_count": 2, 265 "configs": []*CryptoKeyConfig{ 266 { 267 ID: "0", 268 Usage: "verify", 269 Source: "config", 270 Algorithm: "hmac", 271 Secret: "foobar", 272 TokenName: "access_token", 273 TokenLifetime: 900, 274 parsed: true, 275 validated: true, 276 }, 277 { 278 Seq: 1, 279 ID: "abc123", 280 Usage: "verify", 281 Source: "config", 282 Algorithm: "hmac", 283 Secret: "foobar", 284 TokenName: "access_token", 285 TokenLifetime: 900, 286 parsed: true, 287 validated: true, 288 }, 289 }, 290 }, 291 }, 292 { 293 name: "multiple shared keys in with explicit default token name config", 294 config: ` 295 crypto default token name jwt_token 296 crypto key verify foobar 297 crypto key abc123 verify foobar 298 crypto key abc123 token name foobar_token 299 `, 300 want: map[string]interface{}{ 301 "config_count": 2, 302 "configs": []*CryptoKeyConfig{ 303 { 304 ID: "0", 305 Usage: "verify", 306 Source: "config", 307 Algorithm: "hmac", 308 Secret: "foobar", 309 TokenName: "jwt_token", 310 TokenLifetime: 900, 311 parsed: true, 312 validated: true, 313 }, 314 { 315 Seq: 1, 316 ID: "abc123", 317 Usage: "verify", 318 TokenName: "foobar_token", 319 Source: "config", 320 Algorithm: "hmac", 321 Secret: "foobar", 322 TokenLifetime: 900, 323 parsed: true, 324 validated: true, 325 }, 326 }, 327 }, 328 }, 329 { 330 name: "single default shared key", 331 config: ` 332 crypto key verify foobar 333 `, 334 want: map[string]interface{}{ 335 "config_count": 1, 336 "configs": []*CryptoKeyConfig{ 337 { 338 ID: "0", 339 Usage: "verify", 340 Source: "config", 341 Algorithm: "hmac", 342 Secret: "foobar", 343 TokenName: "access_token", 344 TokenLifetime: 900, 345 parsed: true, 346 validated: true, 347 }, 348 }, 349 }, 350 }, 351 { 352 name: "multiple default shared keys", 353 config: ` 354 crypto key sign foobar 355 crypto key sign barfoo 356 `, 357 shouldErr: true, 358 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 359 `crypto key sign barfoo`, 360 `duplicate key id`, 361 ), 362 }, 363 { 364 name: "load key from file path", 365 config: ` 366 crypto key k9738a405e99 verify from file /path/to/file 367 `, 368 want: map[string]interface{}{ 369 "config_count": 1, 370 "configs": []*CryptoKeyConfig{ 371 { 372 ID: "k9738a405e99", 373 Usage: "verify", 374 Source: "config", 375 FilePath: "/path/to/file", 376 TokenName: "access_token", 377 TokenLifetime: 900, 378 parsed: true, 379 validated: true, 380 }, 381 }, 382 }, 383 }, 384 { 385 name: "load private-public key pair from separate file paths", 386 config: ` 387 crypto key k9738a405e99 sign from file ./../../testdata/rskeys/test_2_pri.pem 388 crypto key k9738a405e99 verify from file ./../../testdata/rskeys/test_2_pub.pem 389 `, 390 want: map[string]interface{}{ 391 "config_count": 2, 392 "configs": []*CryptoKeyConfig{ 393 { 394 ID: "k9738a405e99", 395 Usage: "sign", 396 TokenName: "access_token", 397 Source: "config", 398 FilePath: "./../../testdata/rskeys/test_2_pri.pem", 399 TokenLifetime: 900, 400 parsed: true, 401 validated: true, 402 }, 403 { 404 Seq: 1, 405 ID: "k9738a405e99", 406 Usage: "verify", 407 TokenName: "access_token", 408 Source: "config", 409 FilePath: "./../../testdata/rskeys/test_2_pub.pem", 410 TokenLifetime: 900, 411 parsed: true, 412 validated: true, 413 }, 414 }, 415 }, 416 }, 417 { 418 name: "load keys from directory path", 419 config: ` 420 crypto key k9738a405e99 verify from directory /path/to/dir 421 `, 422 want: map[string]interface{}{ 423 "config_count": 1, 424 "configs": []*CryptoKeyConfig{ 425 { 426 ID: "k9738a405e99", 427 Usage: "verify", 428 Source: "config", 429 DirPath: "/path/to/dir", 430 TokenName: "access_token", 431 TokenLifetime: 900, 432 parsed: true, 433 validated: true, 434 }, 435 }, 436 }, 437 }, 438 { 439 name: "shared secret embedded in environment variable", 440 config: ` 441 crypto key cb315f43c868 verify from env JWT_SHARED_SECRET 442 `, 443 env: map[string]string{ 444 "JWT_SHARED_SECRET": "foobar", 445 }, 446 want: map[string]interface{}{ 447 "config_count": 1, 448 "configs": []*CryptoKeyConfig{ 449 { 450 ID: "cb315f43c868", 451 Usage: "verify", 452 Source: "env", 453 EnvVarName: "JWT_SHARED_SECRET", 454 EnvVarValue: "foobar", 455 EnvVarType: "key", 456 TokenName: "access_token", 457 TokenLifetime: 900, 458 parsed: true, 459 validated: true, 460 }, 461 }, 462 }, 463 }, 464 { 465 name: "empty env variable value", 466 config: ` 467 crypto key cb315f43c868 verify from env JWT_SHARED_SECRET 468 `, 469 env: map[string]string{ 470 "JWT_SHARED_SECRET": " ", 471 }, 472 shouldErr: true, 473 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 474 "crypto key cb315f43c868 verify from env JWT_SHARED_SECRET", 475 errors.ErrCryptoKeyConfigEmptyEnvVar.WithArgs("JWT_SHARED_SECRET"), 476 ), 477 }, 478 { 479 name: "load key from the value in JWT_SECRET_KEY environment variable", 480 config: ` 481 crypto key cb315f43c868 verify from env JWT_SECRET_KEY as key 482 `, 483 env: map[string]string{ 484 "JWT_SECRET_KEY": "----BEGIN RSA ...", 485 }, 486 want: map[string]interface{}{ 487 "config_count": 1, 488 "configs": []*CryptoKeyConfig{ 489 { 490 ID: "cb315f43c868", 491 Usage: "verify", 492 Source: "env", 493 EnvVarName: "JWT_SECRET_KEY", 494 EnvVarValue: "----BEGIN RSA ...", 495 EnvVarType: "key", 496 TokenName: "access_token", 497 TokenLifetime: 900, 498 parsed: true, 499 validated: true, 500 }, 501 }, 502 }, 503 }, 504 { 505 name: "load key from the file named in JWT_SECRET_FILE environment variable", 506 config: ` 507 crypto key cb315f43c868 verify from env JWT_SECRET_FILE as file 508 `, 509 env: map[string]string{ 510 "JWT_SECRET_FILE": "/path/to/file", 511 }, 512 want: map[string]interface{}{ 513 "config_count": 1, 514 "configs": []*CryptoKeyConfig{ 515 { 516 ID: "cb315f43c868", 517 Usage: "verify", 518 Source: "env", 519 EnvVarName: "JWT_SECRET_FILE", 520 EnvVarValue: "/path/to/file", 521 EnvVarType: "file", 522 TokenName: "access_token", 523 TokenLifetime: 900, 524 parsed: true, 525 validated: true, 526 }, 527 }, 528 }, 529 }, 530 { 531 name: "load keys from the files in the directory named in JWT_SECRET_DIR environment variable", 532 config: ` 533 crypto key cb315f43c868 verify from env JWT_SECRET_DIR as directory 534 `, 535 env: map[string]string{ 536 "JWT_SECRET_DIR": "/path/to/dir", 537 }, 538 want: map[string]interface{}{ 539 "config_count": 1, 540 "configs": []*CryptoKeyConfig{ 541 { 542 ID: "cb315f43c868", 543 Usage: "verify", 544 Source: "env", 545 EnvVarName: "JWT_SECRET_DIR", 546 EnvVarValue: "/path/to/dir", 547 EnvVarType: "directory", 548 TokenName: "access_token", 549 TokenLifetime: 900, 550 parsed: true, 551 validated: true, 552 }, 553 }, 554 }, 555 }, 556 { 557 name: "config entry is too short", 558 config: ` 559 crypto key 560 `, 561 shouldErr: true, 562 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 563 "crypto key", "entry is too short", 564 ), 565 }, 566 { 567 name: "config entry without closing quote", 568 config: ` 569 crypto key "foo 570 `, 571 shouldErr: true, 572 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 573 `crypto key "foo`, 574 `parse error on line 1, column 16: extraneous or missing " in quoted-field`, 575 ), 576 }, 577 { 578 name: "config entry with invalid default token setting", 579 config: ` 580 crypto default token foo bar 581 `, 582 shouldErr: true, 583 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 584 `crypto default token foo bar`, 585 `unknown default token setting`, 586 ), 587 }, 588 { 589 name: "config entry with too short default token setting", 590 config: ` 591 crypto default token lifetime 592 `, 593 shouldErr: true, 594 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 595 `crypto default token lifetime`, 596 `default token setting too short`, 597 ), 598 }, 599 { 600 name: "config entry with invalid default token lifetime", 601 config: ` 602 crypto default token lifetime abc123 603 `, 604 shouldErr: true, 605 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 606 `crypto default token lifetime abc123`, 607 `strconv.Atoi: parsing "abc123": invalid syntax`, 608 ), 609 }, 610 { 611 name: "config entry with unknown default setting", 612 config: ` 613 crypto default foo bar foobar 614 `, 615 shouldErr: true, 616 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 617 `crypto default foo bar foobar`, 618 `unknown default setting`, 619 ), 620 }, 621 { 622 name: "invalid config entry", 623 config: ` 624 crypto foo bar foo bar 625 `, 626 shouldErr: true, 627 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 628 `crypto foo bar foo bar`, 629 `bad syntax`, 630 ), 631 }, 632 { 633 name: "bad key token syntax", 634 config: ` 635 crypto key 123 verify foobar 636 crypto key 123 token foo 637 `, 638 shouldErr: true, 639 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 640 `crypto key 123 token foo`, 641 `token must be followed by its attributes`, 642 ), 643 }, 644 { 645 name: "reserved keyword must not be last", 646 config: ` 647 crypto key 123 verify foobar 648 crypto key 123 token 649 `, 650 shouldErr: true, 651 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 652 `crypto key 123 token`, 653 `reserved keyword must not be last`, 654 ), 655 }, 656 { 657 name: "key with invalid token lifetime", 658 config: ` 659 crypto key 123 verify foobar 660 crypto key 123 token lifetime abc123 661 `, 662 shouldErr: true, 663 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 664 `crypto key 123 token lifetime abc123`, 665 `strconv.Atoi: parsing "abc123": invalid syntax`, 666 ), 667 }, 668 { 669 name: "key with unknown key token setting", 670 config: ` 671 crypto key 123 verify foobar 672 crypto key 123 token foo bar 673 `, 674 shouldErr: true, 675 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 676 `crypto key 123 token foo bar`, 677 `unknown key token setting`, 678 ), 679 }, 680 { 681 name: "key with usage with bad syntax", 682 config: ` 683 crypto key 123 verify foo bar 684 `, 685 shouldErr: true, 686 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 687 `crypto key 123 verify foo bar`, 688 `bad syntax`, 689 ), 690 }, 691 { 692 name: "bad syntax", 693 config: ` 694 crypto key 123 verify foo bar 695 `, 696 shouldErr: true, 697 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 698 `crypto key 123 verify foo bar`, 699 `bad syntax`, 700 ), 701 }, 702 { 703 name: "invalid from config", 704 config: ` 705 crypto key 123 verify from foo /path/to/file 706 `, 707 shouldErr: true, 708 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 709 `crypto key 123 verify from foo /path/to/file`, 710 `bad syntax`, 711 ), 712 }, 713 { 714 name: "invalid from env", 715 config: ` 716 crypto key 123 verify from env JWT_SECRET_FILE as foo 717 `, 718 shouldErr: true, 719 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 720 `crypto key 123 verify from env JWT_SECRET_FILE as foo`, 721 `bad syntax`, 722 ), 723 }, 724 { 725 name: "invalid from env as", 726 config: ` 727 crypto key 123 verify from env JWT_SECRET_FILE foo bar 728 `, 729 shouldErr: true, 730 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 731 `crypto key 123 verify from env JWT_SECRET_FILE foo bar`, 732 `bad syntax`, 733 ), 734 }, 735 { 736 name: "invalid from env as file and empty env var", 737 config: ` 738 crypto key 123 verify from env JWT_SECRET_FILE as file 739 `, 740 shouldErr: true, 741 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 742 `crypto key 123 verify from env JWT_SECRET_FILE as file`, 743 `environment variable JWT_SECRET_FILE has empty value`, 744 ), 745 }, 746 { 747 name: "invalid from env as file and too long", 748 config: ` 749 crypto key 123 verify from env JWT_SECRET_FILE as file foo 750 `, 751 shouldErr: true, 752 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 753 `crypto key 123 verify from env JWT_SECRET_FILE as file foo`, 754 `bad syntax`, 755 ), 756 }, 757 { 758 name: "invalid key argument", 759 config: ` 760 crypto key 123 foo foo foo foo foo 761 `, 762 shouldErr: true, 763 err: errors.ErrCryptoKeyConfigEntryInvalid.WithArgs( 764 `crypto key 123 foo foo foo foo foo`, 765 `invalid argument`, 766 ), 767 }, 768 { 769 name: "without key configs", 770 config: ` 771 crypto default token name foo 772 `, 773 shouldErr: true, 774 err: errors.ErrCryptoKeyConfigNoConfigFound, 775 }, 776 { 777 name: "with validate error", 778 config: ` 779 crypto key 123 token name foo 780 `, 781 shouldErr: true, 782 err: errors.ErrCryptoKeyConfigKeyInvalid.WithArgs(0, "key usage is not set"), 783 }, 784 { 785 name: "load mix static and private keys", 786 config: ` 787 crypto key token name usertoken 788 crypto key verify foobar 789 crypto key k9738a405e99 verify from file ./../../testdata/rskeys/test_2_pub.pem 790 `, 791 want: map[string]interface{}{ 792 "config_count": 2, 793 "configs": []*CryptoKeyConfig{ 794 { 795 ID: "0", 796 Usage: "verify", 797 TokenName: "usertoken", 798 Source: "config", 799 Algorithm: "hmac", 800 TokenLifetime: 900, 801 Secret: "foobar", 802 parsed: true, 803 validated: true, 804 }, 805 { 806 ID: "k9738a405e99", 807 Seq: 1, 808 Usage: "verify", 809 TokenName: "access_token", 810 Source: "config", 811 FilePath: "./../../testdata/rskeys/test_2_pub.pem", 812 TokenLifetime: 900, 813 parsed: true, 814 validated: true, 815 }, 816 }, 817 }, 818 }, 819 { 820 name: "load mix static and private keys with default token name", 821 config: ` 822 crypto default token name usertoken 823 crypto key verify foobar 824 crypto key k9738a405e99 verify from file ./../../testdata/rskeys/test_2_pub.pem 825 `, 826 want: map[string]interface{}{ 827 "config_count": 2, 828 "configs": []*CryptoKeyConfig{ 829 { 830 ID: "0", 831 Usage: "verify", 832 TokenName: "usertoken", 833 Source: "config", 834 Algorithm: "hmac", 835 TokenLifetime: 900, 836 Secret: "foobar", 837 parsed: true, 838 validated: true, 839 }, 840 { 841 ID: "k9738a405e99", 842 Seq: 1, 843 Usage: "verify", 844 TokenName: "usertoken", 845 Source: "config", 846 FilePath: "./../../testdata/rskeys/test_2_pub.pem", 847 TokenLifetime: 900, 848 parsed: true, 849 validated: true, 850 }, 851 }, 852 }, 853 }, 854 } 855 for _, tc := range testcases { 856 t.Run(tc.name, func(t *testing.T) { 857 msgs := []string{fmt.Sprintf("test name: %s", tc.name)} 858 msgs = append(msgs, fmt.Sprintf("config: %s", tc.config)) 859 for k, v := range tc.env { 860 msgs = append(msgs, fmt.Sprintf("env: %s = %s", k, v)) 861 os.Setenv(k, v) 862 defer os.Unsetenv(k) 863 } 864 configs, err := ParseCryptoKeyConfigs(tc.config) 865 if tests.EvalErrWithLog(t, err, nil, tc.shouldErr, tc.err, msgs) { 866 return 867 } 868 got := make(map[string]interface{}) 869 got["config_count"] = len(configs) 870 got["configs"] = configs 871 872 for i, c := range configs { 873 msgs = append(msgs, fmt.Sprintf("config %d: %s", i, c.ToString())) 874 } 875 876 if diff := cmp.Diff(tc.want, got, cmp.AllowUnexported(CryptoKeyConfig{})); diff != "" { 877 tests.WriteLog(t, msgs) 878 t.Fatalf("output mismatch (-want +got):\n%s", diff) 879 } 880 }) 881 } 882 }