1package transit 2 3import ( 4 "context" 5 "encoding/json" 6 "reflect" 7 "testing" 8 9 "github.com/hashicorp/vault/sdk/logical" 10 "github.com/mitchellh/mapstructure" 11) 12 13// Case1: Ensure that batch encryption did not affect the normal flow of 14// encrypting the plaintext with a pre-existing key. 15func TestTransit_BatchEncryptionCase1(t *testing.T) { 16 var resp *logical.Response 17 var err error 18 19 b, s := createBackendWithStorage(t) 20 21 // Create the policy 22 policyReq := &logical.Request{ 23 Operation: logical.UpdateOperation, 24 Path: "keys/existing_key", 25 Storage: s, 26 } 27 resp, err = b.HandleRequest(context.Background(), policyReq) 28 if err != nil || (resp != nil && resp.IsError()) { 29 t.Fatalf("err:%v resp:%#v", err, resp) 30 } 31 32 plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" // "the quick brown fox" 33 34 encData := map[string]interface{}{ 35 "plaintext": plaintext, 36 } 37 38 encReq := &logical.Request{ 39 Operation: logical.UpdateOperation, 40 Path: "encrypt/existing_key", 41 Storage: s, 42 Data: encData, 43 } 44 resp, err = b.HandleRequest(context.Background(), encReq) 45 if err != nil || (resp != nil && resp.IsError()) { 46 t.Fatalf("err:%v resp:%#v", err, resp) 47 } 48 49 keyVersion := resp.Data["key_version"].(int) 50 if keyVersion != 1 { 51 t.Fatalf("unexpected key version; got: %d, expected: %d", keyVersion, 1) 52 } 53 54 ciphertext := resp.Data["ciphertext"] 55 56 decData := map[string]interface{}{ 57 "ciphertext": ciphertext, 58 } 59 decReq := &logical.Request{ 60 Operation: logical.UpdateOperation, 61 Path: "decrypt/existing_key", 62 Storage: s, 63 Data: decData, 64 } 65 resp, err = b.HandleRequest(context.Background(), decReq) 66 if err != nil || (resp != nil && resp.IsError()) { 67 t.Fatalf("err:%v resp:%#v", err, resp) 68 } 69 70 if resp.Data["plaintext"] != plaintext { 71 t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) 72 } 73} 74 75// Case2: Ensure that batch encryption did not affect the normal flow of 76// encrypting the plaintext with the key upserted. 77func TestTransit_BatchEncryptionCase2(t *testing.T) { 78 var resp *logical.Response 79 var err error 80 b, s := createBackendWithStorage(t) 81 82 // Upsert the key and encrypt the data 83 plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" 84 85 encData := map[string]interface{}{ 86 "plaintext": plaintext, 87 } 88 89 encReq := &logical.Request{ 90 Operation: logical.CreateOperation, 91 Path: "encrypt/upserted_key", 92 Storage: s, 93 Data: encData, 94 } 95 resp, err = b.HandleRequest(context.Background(), encReq) 96 if err != nil || (resp != nil && resp.IsError()) { 97 t.Fatalf("err:%v resp:%#v", err, resp) 98 } 99 100 keyVersion := resp.Data["key_version"].(int) 101 if keyVersion != 1 { 102 t.Fatalf("unexpected key version; got: %d, expected: %d", keyVersion, 1) 103 } 104 105 ciphertext := resp.Data["ciphertext"] 106 decData := map[string]interface{}{ 107 "ciphertext": ciphertext, 108 } 109 110 policyReq := &logical.Request{ 111 Operation: logical.ReadOperation, 112 Path: "keys/upserted_key", 113 Storage: s, 114 } 115 116 resp, err = b.HandleRequest(context.Background(), policyReq) 117 if err != nil || (resp != nil && resp.IsError()) { 118 t.Fatalf("err:%v resp:%#v", err, resp) 119 } 120 121 decReq := &logical.Request{ 122 Operation: logical.UpdateOperation, 123 Path: "decrypt/upserted_key", 124 Storage: s, 125 Data: decData, 126 } 127 resp, err = b.HandleRequest(context.Background(), decReq) 128 if err != nil || (resp != nil && resp.IsError()) { 129 t.Fatalf("err:%v resp:%#v", err, resp) 130 } 131 132 if resp.Data["plaintext"] != plaintext { 133 t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) 134 } 135} 136 137// Case3: If batch encryption input is not base64 encoded, it should fail. 138func TestTransit_BatchEncryptionCase3(t *testing.T) { 139 var err error 140 141 b, s := createBackendWithStorage(t) 142 143 batchInput := `[{"plaintext":"dGhlIHF1aWNrIGJyb3duIGZveA=="}]` 144 batchData := map[string]interface{}{ 145 "batch_input": batchInput, 146 } 147 148 batchReq := &logical.Request{ 149 Operation: logical.CreateOperation, 150 Path: "encrypt/upserted_key", 151 Storage: s, 152 Data: batchData, 153 } 154 _, err = b.HandleRequest(context.Background(), batchReq) 155 if err == nil { 156 t.Fatal("expected an error") 157 } 158} 159 160// Case4: Test batch encryption with an existing key 161func TestTransit_BatchEncryptionCase4(t *testing.T) { 162 var resp *logical.Response 163 var err error 164 165 b, s := createBackendWithStorage(t) 166 167 policyReq := &logical.Request{ 168 Operation: logical.UpdateOperation, 169 Path: "keys/existing_key", 170 Storage: s, 171 } 172 resp, err = b.HandleRequest(context.Background(), policyReq) 173 if err != nil || (resp != nil && resp.IsError()) { 174 t.Fatalf("err:%v resp:%#v", err, resp) 175 } 176 177 batchInput := []interface{}{ 178 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, 179 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, 180 } 181 182 batchData := map[string]interface{}{ 183 "batch_input": batchInput, 184 } 185 batchReq := &logical.Request{ 186 Operation: logical.UpdateOperation, 187 Path: "encrypt/existing_key", 188 Storage: s, 189 Data: batchData, 190 } 191 resp, err = b.HandleRequest(context.Background(), batchReq) 192 if err != nil || (resp != nil && resp.IsError()) { 193 t.Fatalf("err:%v resp:%#v", err, resp) 194 } 195 196 batchResponseItems := resp.Data["batch_results"].([]EncryptBatchResponseItem) 197 198 decReq := &logical.Request{ 199 Operation: logical.UpdateOperation, 200 Path: "decrypt/existing_key", 201 Storage: s, 202 } 203 204 plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" 205 206 for _, item := range batchResponseItems { 207 if item.KeyVersion != 1 { 208 t.Fatalf("unexpected key version; got: %d, expected: %d", item.KeyVersion, 1) 209 } 210 211 decReq.Data = map[string]interface{}{ 212 "ciphertext": item.Ciphertext, 213 } 214 resp, err = b.HandleRequest(context.Background(), decReq) 215 if err != nil || (resp != nil && resp.IsError()) { 216 t.Fatalf("err:%v resp:%#v", err, resp) 217 } 218 219 if resp.Data["plaintext"] != plaintext { 220 t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) 221 } 222 } 223} 224 225// Case5: Test batch encryption with an existing derived key 226func TestTransit_BatchEncryptionCase5(t *testing.T) { 227 var resp *logical.Response 228 var err error 229 230 b, s := createBackendWithStorage(t) 231 232 policyData := map[string]interface{}{ 233 "derived": true, 234 } 235 236 policyReq := &logical.Request{ 237 Operation: logical.UpdateOperation, 238 Path: "keys/existing_key", 239 Storage: s, 240 Data: policyData, 241 } 242 243 resp, err = b.HandleRequest(context.Background(), policyReq) 244 if err != nil || (resp != nil && resp.IsError()) { 245 t.Fatalf("err:%v resp:%#v", err, resp) 246 } 247 248 batchInput := []interface{}{ 249 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, 250 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, 251 } 252 253 batchData := map[string]interface{}{ 254 "batch_input": batchInput, 255 } 256 257 batchReq := &logical.Request{ 258 Operation: logical.UpdateOperation, 259 Path: "encrypt/existing_key", 260 Storage: s, 261 Data: batchData, 262 } 263 resp, err = b.HandleRequest(context.Background(), batchReq) 264 if err != nil || (resp != nil && resp.IsError()) { 265 t.Fatalf("err:%v resp:%#v", err, resp) 266 } 267 268 batchResponseItems := resp.Data["batch_results"].([]EncryptBatchResponseItem) 269 270 decReq := &logical.Request{ 271 Operation: logical.UpdateOperation, 272 Path: "decrypt/existing_key", 273 Storage: s, 274 } 275 276 plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" 277 278 for _, item := range batchResponseItems { 279 if item.KeyVersion != 1 { 280 t.Fatalf("unexpected key version; got: %d, expected: %d", item.KeyVersion, 1) 281 } 282 283 decReq.Data = map[string]interface{}{ 284 "ciphertext": item.Ciphertext, 285 "context": "dmlzaGFsCg==", 286 } 287 resp, err = b.HandleRequest(context.Background(), decReq) 288 if err != nil || (resp != nil && resp.IsError()) { 289 t.Fatalf("err:%v resp:%#v", err, resp) 290 } 291 292 if resp.Data["plaintext"] != plaintext { 293 t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) 294 } 295 } 296} 297 298// Case6: Test batch encryption with an upserted non-derived key 299func TestTransit_BatchEncryptionCase6(t *testing.T) { 300 var resp *logical.Response 301 var err error 302 303 b, s := createBackendWithStorage(t) 304 305 batchInput := []interface{}{ 306 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, 307 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, 308 } 309 310 batchData := map[string]interface{}{ 311 "batch_input": batchInput, 312 } 313 batchReq := &logical.Request{ 314 Operation: logical.CreateOperation, 315 Path: "encrypt/upserted_key", 316 Storage: s, 317 Data: batchData, 318 } 319 resp, err = b.HandleRequest(context.Background(), batchReq) 320 if err != nil || (resp != nil && resp.IsError()) { 321 t.Fatalf("err:%v resp:%#v", err, resp) 322 } 323 324 batchResponseItems := resp.Data["batch_results"].([]EncryptBatchResponseItem) 325 326 decReq := &logical.Request{ 327 Operation: logical.UpdateOperation, 328 Path: "decrypt/upserted_key", 329 Storage: s, 330 } 331 332 plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" 333 334 for _, responseItem := range batchResponseItems { 335 var item EncryptBatchResponseItem 336 if err := mapstructure.Decode(responseItem, &item); err != nil { 337 t.Fatal(err) 338 } 339 340 if item.KeyVersion != 1 { 341 t.Fatalf("unexpected key version; got: %d, expected: %d", item.KeyVersion, 1) 342 } 343 344 decReq.Data = map[string]interface{}{ 345 "ciphertext": item.Ciphertext, 346 } 347 resp, err = b.HandleRequest(context.Background(), decReq) 348 if err != nil || (resp != nil && resp.IsError()) { 349 t.Fatalf("err:%v resp:%#v", err, resp) 350 } 351 352 if resp.Data["plaintext"] != plaintext { 353 t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) 354 } 355 } 356} 357 358// Case7: Test batch encryption with an upserted derived key 359func TestTransit_BatchEncryptionCase7(t *testing.T) { 360 var resp *logical.Response 361 var err error 362 363 b, s := createBackendWithStorage(t) 364 365 batchInput := []interface{}{ 366 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, 367 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, 368 } 369 370 batchData := map[string]interface{}{ 371 "batch_input": batchInput, 372 } 373 batchReq := &logical.Request{ 374 Operation: logical.CreateOperation, 375 Path: "encrypt/upserted_key", 376 Storage: s, 377 Data: batchData, 378 } 379 resp, err = b.HandleRequest(context.Background(), batchReq) 380 if err != nil || (resp != nil && resp.IsError()) { 381 t.Fatalf("err:%v resp:%#v", err, resp) 382 } 383 384 batchResponseItems := resp.Data["batch_results"].([]EncryptBatchResponseItem) 385 386 decReq := &logical.Request{ 387 Operation: logical.UpdateOperation, 388 Path: "decrypt/upserted_key", 389 Storage: s, 390 } 391 392 plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" 393 394 for _, item := range batchResponseItems { 395 if item.KeyVersion != 1 { 396 t.Fatalf("unexpected key version; got: %d, expected: %d", item.KeyVersion, 1) 397 } 398 399 decReq.Data = map[string]interface{}{ 400 "ciphertext": item.Ciphertext, 401 "context": "dmlzaGFsCg==", 402 } 403 resp, err = b.HandleRequest(context.Background(), decReq) 404 if err != nil || (resp != nil && resp.IsError()) { 405 t.Fatalf("err:%v resp:%#v", err, resp) 406 } 407 408 if resp.Data["plaintext"] != plaintext { 409 t.Fatalf("bad: plaintext. Expected: %q, Actual: %q", plaintext, resp.Data["plaintext"]) 410 } 411 } 412} 413 414// Case8: If plaintext is not base64 encoded, encryption should fail 415func TestTransit_BatchEncryptionCase8(t *testing.T) { 416 var resp *logical.Response 417 var err error 418 419 b, s := createBackendWithStorage(t) 420 421 // Create the policy 422 policyReq := &logical.Request{ 423 Operation: logical.UpdateOperation, 424 Path: "keys/existing_key", 425 Storage: s, 426 } 427 resp, err = b.HandleRequest(context.Background(), policyReq) 428 if err != nil || (resp != nil && resp.IsError()) { 429 t.Fatalf("err:%v resp:%#v", err, resp) 430 } 431 432 batchInput := []interface{}{ 433 map[string]interface{}{"plaintext": "simple_plaintext"}, 434 } 435 batchData := map[string]interface{}{ 436 "batch_input": batchInput, 437 } 438 batchReq := &logical.Request{ 439 Operation: logical.UpdateOperation, 440 Path: "encrypt/existing_key", 441 Storage: s, 442 Data: batchData, 443 } 444 resp, err = b.HandleRequest(context.Background(), batchReq) 445 if err != nil || (resp != nil && resp.IsError()) { 446 t.Fatalf("err:%v resp:%#v", err, resp) 447 } 448 449 plaintext := "simple plaintext" 450 451 encData := map[string]interface{}{ 452 "plaintext": plaintext, 453 } 454 455 encReq := &logical.Request{ 456 Operation: logical.UpdateOperation, 457 Path: "encrypt/existing_key", 458 Storage: s, 459 Data: encData, 460 } 461 resp, err = b.HandleRequest(context.Background(), encReq) 462 if err == nil { 463 t.Fatal("expected an error") 464 } 465} 466 467// Case9: If both plaintext and batch inputs are supplied, plaintext should be 468// ignored. 469func TestTransit_BatchEncryptionCase9(t *testing.T) { 470 var resp *logical.Response 471 var err error 472 473 b, s := createBackendWithStorage(t) 474 475 batchInput := []interface{}{ 476 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, 477 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, 478 } 479 plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" 480 batchData := map[string]interface{}{ 481 "batch_input": batchInput, 482 "plaintext": plaintext, 483 } 484 batchReq := &logical.Request{ 485 Operation: logical.CreateOperation, 486 Path: "encrypt/upserted_key", 487 Storage: s, 488 Data: batchData, 489 } 490 resp, err = b.HandleRequest(context.Background(), batchReq) 491 if err != nil || (resp != nil && resp.IsError()) { 492 t.Fatalf("err:%v resp:%#v", err, resp) 493 } 494 495 _, ok := resp.Data["ciphertext"] 496 if ok { 497 t.Fatal("ciphertext field should not be set") 498 } 499} 500 501// Case10: Inconsistent presence of 'context' in batch input should be caught 502func TestTransit_BatchEncryptionCase10(t *testing.T) { 503 var err error 504 505 b, s := createBackendWithStorage(t) 506 507 batchInput := []interface{}{ 508 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}, 509 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, 510 } 511 512 batchData := map[string]interface{}{ 513 "batch_input": batchInput, 514 } 515 516 batchReq := &logical.Request{ 517 Operation: logical.CreateOperation, 518 Path: "encrypt/upserted_key", 519 Storage: s, 520 Data: batchData, 521 } 522 _, err = b.HandleRequest(context.Background(), batchReq) 523 if err == nil { 524 t.Fatalf("expected an error") 525 } 526} 527 528// Case11: Incorrect inputs for context and nonce should not fail the operation 529func TestTransit_BatchEncryptionCase11(t *testing.T) { 530 var err error 531 532 b, s := createBackendWithStorage(t) 533 534 batchInput := []interface{}{ 535 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "dmlzaGFsCg=="}, 536 map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "context": "not-encoded"}, 537 } 538 539 batchData := map[string]interface{}{ 540 "batch_input": batchInput, 541 } 542 batchReq := &logical.Request{ 543 Operation: logical.CreateOperation, 544 Path: "encrypt/upserted_key", 545 Storage: s, 546 Data: batchData, 547 } 548 _, err = b.HandleRequest(context.Background(), batchReq) 549 if err != nil { 550 t.Fatal(err) 551 } 552} 553 554// Case12: Invalid batch input 555func TestTransit_BatchEncryptionCase12(t *testing.T) { 556 var err error 557 b, s := createBackendWithStorage(t) 558 559 batchInput := []interface{}{ 560 map[string]interface{}{}, 561 "unexpected_interface", 562 } 563 564 batchData := map[string]interface{}{ 565 "batch_input": batchInput, 566 } 567 batchReq := &logical.Request{ 568 Operation: logical.CreateOperation, 569 Path: "encrypt/upserted_key", 570 Storage: s, 571 Data: batchData, 572 } 573 _, err = b.HandleRequest(context.Background(), batchReq) 574 if err == nil { 575 t.Fatalf("expected an error") 576 } 577} 578 579// Test that the fast path function decodeBatchRequestItems behave like mapstructure.Decode() to decode []BatchRequestItem. 580func TestTransit_decodeBatchRequestItems(t *testing.T) { 581 tests := []struct { 582 name string 583 src interface{} 584 dest []BatchRequestItem 585 }{ 586 // basic edge cases of nil values 587 {name: "nil-nil", src: nil, dest: nil}, 588 {name: "nil-empty", src: nil, dest: []BatchRequestItem{}}, 589 {name: "empty-nil", src: []interface{}{}, dest: nil}, 590 { 591 name: "src-nil", 592 src: []interface{}{map[string]interface{}{}}, 593 dest: nil, 594 }, 595 // empty src & dest 596 { 597 name: "src-dest", 598 src: []interface{}{map[string]interface{}{}}, 599 dest: []BatchRequestItem{}, 600 }, 601 // empty src but with already populated dest, mapstructure discard pre-populated data. 602 { 603 name: "src-dest_pre_filled", 604 src: []interface{}{map[string]interface{}{}}, 605 dest: []BatchRequestItem{{}}, 606 }, 607 // two test per properties to test valid and invalid input 608 { 609 name: "src_plaintext-dest", 610 src: []interface{}{map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}}, 611 dest: []BatchRequestItem{}, 612 }, 613 { 614 name: "src_plaintext_invalid-dest", 615 src: []interface{}{map[string]interface{}{"plaintext": 666}}, 616 dest: []BatchRequestItem{}, 617 }, 618 { 619 name: "src_ciphertext-dest", 620 src: []interface{}{map[string]interface{}{"ciphertext": "dGhlIHF1aWNrIGJyb3duIGZveA=="}}, 621 dest: []BatchRequestItem{}, 622 }, 623 { 624 name: "src_ciphertext_invalid-dest", 625 src: []interface{}{map[string]interface{}{"ciphertext": 666}}, 626 dest: []BatchRequestItem{}, 627 }, 628 { 629 name: "src_key_version-dest", 630 src: []interface{}{map[string]interface{}{"key_version": 1}}, 631 dest: []BatchRequestItem{}, 632 }, 633 { 634 name: "src_key_version_invalid-dest", 635 src: []interface{}{map[string]interface{}{"key_version": "666"}}, 636 dest: []BatchRequestItem{}, 637 }, 638 { 639 name: "src_key_version_invalid-number-dest", 640 src: []interface{}{map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "key_version": json.Number("1.1")}}, 641 dest: []BatchRequestItem{}, 642 }, 643 { 644 name: "src_nonce-dest", 645 src: []interface{}{map[string]interface{}{"nonce": "dGVzdGNvbnRleHQ="}}, 646 dest: []BatchRequestItem{}, 647 }, 648 { 649 name: "src_nonce_invalid-dest", 650 src: []interface{}{map[string]interface{}{"nonce": 666}}, 651 dest: []BatchRequestItem{}, 652 }, 653 { 654 name: "src_context-dest", 655 src: []interface{}{map[string]interface{}{"context": "dGVzdGNvbnRleHQ="}}, 656 dest: []BatchRequestItem{}, 657 }, 658 { 659 name: "src_context_invalid-dest", 660 src: []interface{}{map[string]interface{}{"context": 666}}, 661 dest: []BatchRequestItem{}, 662 }, 663 { 664 name: "src_multi_order-dest", 665 src: []interface{}{ 666 map[string]interface{}{"context": "1"}, 667 map[string]interface{}{"context": "2"}, 668 map[string]interface{}{"context": "3"}, 669 }, 670 dest: []BatchRequestItem{}, 671 }, 672 { 673 name: "src_multi_with_invalid-dest", 674 src: []interface{}{ 675 map[string]interface{}{"context": "1"}, 676 map[string]interface{}{"context": "2", "key_version": "666"}, 677 map[string]interface{}{"context": "3"}, 678 }, 679 dest: []BatchRequestItem{}, 680 }, 681 { 682 name: "src_multi_with_multi_invalid-dest", 683 src: []interface{}{ 684 map[string]interface{}{"context": "1"}, 685 map[string]interface{}{"context": "2", "key_version": "666"}, 686 map[string]interface{}{"context": "3", "key_version": "1337"}, 687 }, 688 dest: []BatchRequestItem{}, 689 }, 690 { 691 name: "src_plaintext-nil-nonce", 692 src: []interface{}{map[string]interface{}{"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA==", "nonce": "null"}}, 693 dest: []BatchRequestItem{}, 694 }, 695 } 696 for _, tt := range tests { 697 t.Run(tt.name, func(t *testing.T) { 698 expectedDest := append(tt.dest[:0:0], tt.dest...) // copy of the dest state 699 expectedErr := mapstructure.Decode(tt.src, &expectedDest) 700 701 gotErr := decodeBatchRequestItems(tt.src, &tt.dest) 702 gotDest := tt.dest 703 704 if !reflect.DeepEqual(expectedErr, gotErr) { 705 t.Errorf("decodeBatchRequestItems unexpected error value, want: '%v', got: '%v'", expectedErr, gotErr) 706 } 707 708 if !reflect.DeepEqual(expectedDest, gotDest) { 709 t.Errorf("decodeBatchRequestItems unexpected dest value, want: '%v', got: '%v'", expectedDest, gotDest) 710 } 711 }) 712 } 713} 714