github.com/tomwright/dasel@v1.27.3/internal/command/put_internal_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/tomwright/dasel/storage" 7 "io" 8 "reflect" 9 "strings" 10 "testing" 11 ) 12 13 func TestParseValue(t *testing.T) { 14 tests := []struct { 15 Value string 16 ValueType string 17 Out interface{} 18 Err error 19 }{ 20 {Value: "a", ValueType: "string", Out: "a"}, 21 {Value: "1", ValueType: "string", Out: "1"}, 22 {Value: "1", ValueType: "int", Out: int64(1)}, 23 {Value: "a", ValueType: "int", Err: fmt.Errorf("could not parse int [%s]: strconv.ParseInt: parsing \"%s\": invalid syntax", "a", "a")}, 24 {Value: "true", ValueType: "string", Out: "true"}, 25 {Value: "false", ValueType: "string", Out: "false"}, 26 {Value: "true", ValueType: "bool", Out: true}, 27 {Value: "false", ValueType: "bool", Out: false}, 28 {Value: "a", ValueType: "bool", Err: fmt.Errorf("could not parse bool [%s]: unhandled value", "a")}, 29 {Value: "a", ValueType: "bad", Err: fmt.Errorf("unhandled type: %s", "bad")}, 30 } 31 32 for _, testCase := range tests { 33 tc := testCase 34 t.Run(fmt.Sprintf("%s_%s", tc.Value, tc.ValueType), func(t *testing.T) { 35 got, err := parseValue(tc.Value, tc.ValueType) 36 if tc.Err == nil && err != nil { 37 t.Errorf("expected err %v, got %v", tc.Err, err) 38 return 39 } 40 if tc.Err != nil && err == nil { 41 t.Errorf("expected err %v, got %v", tc.Err, err) 42 return 43 } 44 if tc.Err != nil && err != nil && err.Error() != tc.Err.Error() { 45 t.Errorf("expected err %v, got %v", tc.Err, err) 46 return 47 } 48 if !reflect.DeepEqual(tc.Out, got) { 49 t.Errorf("expected result %v, got %v", tc.Out, got) 50 } 51 }) 52 } 53 } 54 55 func TestShouldReadFromStdin(t *testing.T) { 56 if exp, got := false, shouldReadFromStdin("asd"); exp != got { 57 t.Errorf("expected %v, got %v", exp, got) 58 } 59 if exp, got := true, shouldReadFromStdin(""); exp != got { 60 t.Errorf("expected %v, got %v", exp, got) 61 } 62 } 63 64 func TestGetReadParser(t *testing.T) { 65 tests := []struct { 66 File string 67 Parser string 68 Out storage.Parser 69 Err error 70 }{ 71 {File: "a.json", Out: &storage.JSONParser{}}, 72 {Parser: "json", Out: &storage.JSONParser{}}, 73 {File: "a.yaml", Out: &storage.YAMLParser{}}, 74 {File: "a.yml", Out: &storage.YAMLParser{}}, 75 {Parser: "yaml", Out: &storage.YAMLParser{}}, 76 {File: "a.txt", Err: fmt.Errorf("could not get read parser from filename: unknown parser: .txt")}, 77 {Parser: "txt", Err: fmt.Errorf("could not get read parser: unknown parser: txt")}, 78 } 79 80 for _, testCase := range tests { 81 tc := testCase 82 t.Run("Test", func(t *testing.T) { 83 got, err := getReadParser(tc.File, tc.Parser, "") 84 if tc.Err == nil && err != nil { 85 t.Errorf("expected err %v, got %v", tc.Err, err) 86 return 87 } 88 if tc.Err != nil && err == nil { 89 t.Errorf("expected err %v, got %v", tc.Err, err) 90 return 91 } 92 if tc.Err != nil && err != nil && err.Error() != tc.Err.Error() { 93 t.Errorf("expected err %v, got %v", tc.Err, err) 94 return 95 } 96 if tc.Out != got { 97 t.Errorf("expected result %v, got %v", tc.Out, got) 98 } 99 }) 100 } 101 } 102 103 func putTest(in string, parser string, selector string, value string, valueType string, out string, expErr error, initOptions ...func(options *genericPutOptions)) func(t *testing.T) { 104 return func(t *testing.T) { 105 outputBuffer := bytes.NewBuffer([]byte{}) 106 107 opts := genericPutOptions{ 108 Parser: parser, 109 Selector: selector, 110 Value: value, 111 ValueType: valueType, 112 Reader: strings.NewReader(in), 113 Writer: outputBuffer, 114 } 115 116 for _, initOption := range initOptions { 117 initOption(&opts) 118 } 119 120 err := runGenericPutCommand(opts, nil) 121 122 if expErr == nil && err != nil { 123 t.Errorf("expected err %v, got %v", expErr, err) 124 return 125 } 126 if expErr != nil && err == nil { 127 t.Errorf("expected err %v, got %v", expErr, err) 128 return 129 } 130 if expErr != nil && err != nil && err.Error() != expErr.Error() { 131 t.Errorf("expected err %v, got %v", expErr, err) 132 return 133 } 134 135 output, err := io.ReadAll(outputBuffer) 136 if err != nil { 137 t.Errorf("unexpected error reading output buffer: %s", err) 138 return 139 } 140 141 out = strings.TrimSpace(out) 142 got := strings.TrimSpace(string(output)) 143 144 if out != got { 145 t.Errorf("expected result:\n%s\ngot:\n%s", out, got) 146 } 147 } 148 } 149 150 func putObjectTest(in string, parser string, selector string, values []string, valueTypes []string, out string, expErr error) func(t *testing.T) { 151 return func(t *testing.T) { 152 outputBuffer := bytes.NewBuffer([]byte{}) 153 154 err := runPutObjectCommand(putObjectOpts{ 155 Parser: parser, 156 Selector: selector, 157 InputValues: values, 158 InputTypes: valueTypes, 159 Reader: strings.NewReader(in), 160 Writer: outputBuffer, 161 }, nil) 162 163 if expErr == nil && err != nil { 164 t.Errorf("expected err %v, got %v", expErr, err) 165 return 166 } 167 if expErr != nil && err == nil { 168 t.Errorf("expected err %v, got %v", expErr, err) 169 return 170 } 171 if expErr != nil && err != nil && err.Error() != expErr.Error() { 172 t.Errorf("expected err %v, got %v", expErr, err) 173 return 174 } 175 176 output, err := io.ReadAll(outputBuffer) 177 if err != nil { 178 t.Errorf("unexpected error reading output buffer: %s", err) 179 return 180 } 181 182 out = strings.TrimSpace(out) 183 got := strings.TrimSpace(string(output)) 184 185 if out != got { 186 t.Errorf("expected result:\n%s\ngot:\n%s", out, got) 187 } 188 } 189 } 190 191 func TestPut_MissingParserFlag(t *testing.T) { 192 err := runGenericPutCommand(genericPutOptions{}, nil) 193 if err == nil || err.Error() != "read parser flag required when reading from stdin" { 194 t.Errorf("unexpected error: %v", err) 195 } 196 } 197 198 func TestPut_InvalidVarType(t *testing.T) { 199 err := runGenericPutCommand(genericPutOptions{Parser: "yaml", ValueType: "int", Value: "asd", Reader: bytes.NewBuffer([]byte{})}, nil) 200 if err == nil || err.Error() != "could not parse int [asd]: strconv.ParseInt: parsing \"asd\": invalid syntax" { 201 t.Errorf("unexpected error: %v", err) 202 } 203 } 204 205 func TestPut_FailedWrite(t *testing.T) { 206 err := runGenericPutCommand(genericPutOptions{Parser: "yaml", ValueType: "string", Selector: ".name", Value: "asd", Reader: bytes.NewBuffer([]byte{}), Writer: &failingWriter{}}, nil) 207 if err == nil || err.Error() != "could not write output: could not write to output file: could not write data: i am meant to fail at writing" { 208 t.Errorf("unexpected error: %v", err) 209 } 210 } 211 212 func TestPut_ObjectMissingReadParserFlag(t *testing.T) { 213 err := runPutObjectCommand(putObjectOpts{}, nil) 214 if err == nil || err.Error() != "read parser flag required when reading from stdin" { 215 t.Errorf("unexpected error: %v", err) 216 } 217 } 218 219 func TestPut_ObjectMissingWriteParserFlag(t *testing.T) { 220 err := runPutObjectCommand(putObjectOpts{ 221 ReadParser: "yaml", 222 WriteParser: "bad", 223 Reader: bytes.NewReader([]byte(`name: Tom`)), 224 Writer: new(bytes.Buffer), 225 Selector: ".name", 226 InputTypes: []string{"string"}, 227 InputValues: []string{"name=Tom"}, 228 }, nil) 229 if err == nil || err.Error() != "could not get write parser: unknown parser: bad" { 230 t.Errorf("unexpected error: %v", err) 231 } 232 } 233 234 func TestPut_DocumentMissingReadParserFlag(t *testing.T) { 235 err := runPutDocumentCommand(putDocumentOpts{}, nil) 236 if err == nil || err.Error() != "read parser flag required when reading from stdin" { 237 t.Errorf("unexpected error: %v", err) 238 } 239 } 240 241 func TestPut_DocumentMissingWriteParserFlag(t *testing.T) { 242 err := runPutDocumentCommand(putDocumentOpts{ 243 ReadParser: "yaml", 244 WriteParser: "bad", 245 Reader: bytes.NewReader([]byte(`name: Tom`)), 246 Writer: new(bytes.Buffer), 247 Selector: ".name", 248 DocumentString: "first: Tom", 249 DocumentParser: "yaml", 250 }, nil) 251 if err == nil || err.Error() != "could not get write parser: unknown parser: bad" { 252 t.Errorf("unexpected error: %v", err) 253 } 254 } 255 256 func TestPut_ObjectInvalidTypes(t *testing.T) { 257 err := runPutObjectCommand(putObjectOpts{ 258 File: "../../tests/assets/example.json", 259 InputTypes: []string{"string"}, 260 InputValues: []string{"x", "y"}, 261 }, nil) 262 if err == nil || err.Error() != "exactly 2 types are required, got 1" { 263 t.Errorf("unexpected error: %v", err) 264 } 265 } 266 267 func TestPut_JSON(t *testing.T) { 268 t.Run("SingleProperty", putTest(`{ 269 "details": { 270 "age": 27, 271 "name": "Tom" 272 }, 273 "id": "1111" 274 }`, "json", ".id", "2222", "string", `{ 275 "details": { 276 "age": 27, 277 "name": "Tom" 278 }, 279 "id": "2222" 280 }`, nil)) 281 282 t.Run("SinglePropertyCompact", putTest(`{ 283 "details": { 284 "age": 27, 285 "name": "Tom" 286 }, 287 "id": "1111" 288 }`, "json", ".id", "2222", "string", `{"details":{"age":27,"name":"Tom"},"id":"2222"}`, nil, 289 func(options *genericPutOptions) { 290 options.Compact = true 291 })) 292 293 t.Run("ObjectPropertyString", putTest(`{ 294 "details": { 295 "age": 27, 296 "name": "Tom" 297 }, 298 "id": "1111" 299 }`, "json", ".details.name", "Frank", "string", `{ 300 "details": { 301 "age": 27, 302 "name": "Frank" 303 }, 304 "id": "1111" 305 }`, nil)) 306 307 t.Run("ObjectPropertyInt", putTest(`{ 308 "details": { 309 "age": 20, 310 "name": "Tom" 311 }, 312 "id": "1111" 313 }`, "json", ".details.age", "27", "int", `{ 314 "details": { 315 "age": 27, 316 "name": "Tom" 317 }, 318 "id": "1111" 319 }`, nil)) 320 321 t.Run("IndexString", putTest(`{ 322 "numbers": [ 323 "one", 324 "two", 325 "three" 326 ] 327 }`, "json", ".numbers.[1]", "four", "string", `{ 328 "numbers": [ 329 "one", 330 "four", 331 "three" 332 ] 333 }`, nil)) 334 335 t.Run("DynamicString", putTest(`{ 336 "numbers": [ 337 "one", 338 "two", 339 "three" 340 ] 341 }`, "json", ".numbers.(value=three)", "four", "string", `{ 342 "numbers": [ 343 "one", 344 "two", 345 "four" 346 ] 347 }`, nil)) 348 349 t.Run("DynamicInt", putTest(`{ 350 "numbers": [ 351 1, 352 2, 353 3 354 ] 355 }`, "json", ".numbers.(value=3)", "4", "int", `{ 356 "numbers": [ 357 1, 358 2, 359 4 360 ] 361 }`, nil)) 362 363 t.Run("IndexInt", putTest(`{ 364 "numbers": [ 365 1, 366 2, 367 3 368 ] 369 }`, "json", ".numbers.[1]", "4", "int", `{ 370 "numbers": [ 371 1, 372 4, 373 3 374 ] 375 }`, nil)) 376 377 t.Run("DynamicString", putTest(`{ 378 "numbers": [ 379 { 380 "number": "one", 381 "rank": 1 382 }, 383 { 384 "number": "two", 385 "rank": 2 386 }, 387 { 388 "number": "three", 389 "rank": 3 390 } 391 ] 392 }`, "json", ".numbers.(number=two).number", "four", "string", `{ 393 "numbers": [ 394 { 395 "number": "one", 396 "rank": 1 397 }, 398 { 399 "number": "four", 400 "rank": 2 401 }, 402 { 403 "number": "three", 404 "rank": 3 405 } 406 ] 407 }`, nil)) 408 409 t.Run("DynamicInt", putTest(`{ 410 "numbers": [ 411 { 412 "rank": 1, 413 "number": "one" 414 }, 415 { 416 "rank": 2, 417 "number": "two" 418 }, 419 { 420 "rank": 3, 421 "number": "three" 422 } 423 ] 424 }`, "json", ".numbers.(rank=2).rank", "4", "int", `{ 425 "numbers": [ 426 { 427 "number": "one", 428 "rank": 1 429 }, 430 { 431 "number": "two", 432 "rank": 4 433 }, 434 { 435 "number": "three", 436 "rank": 3 437 } 438 ] 439 }`, nil)) 440 441 t.Run("DynamicMapInt", putTest(`{ 442 "numbers": { 443 "high": 3, 444 "low": 1, 445 "mid": 2 446 } 447 }`, "json", ".numbers.(.=3)", "5", "int", `{ 448 "numbers": { 449 "high": 5, 450 "low": 1, 451 "mid": 2 452 } 453 }`, nil)) 454 455 t.Run("OverwriteObject", putObjectTest(`{ 456 "numbers": [ 457 { 458 "rank": 1, 459 "number": "one" 460 }, 461 { 462 "rank": 2, 463 "number": "two" 464 }, 465 { 466 "rank": 3, 467 "number": "three" 468 } 469 ] 470 }`, "json", ".numbers.[0]", []string{"number=five", "rank=5"}, []string{"string", "int"}, `{ 471 "numbers": [ 472 { 473 "number": "five", 474 "rank": 5 475 }, 476 { 477 "number": "two", 478 "rank": 2 479 }, 480 { 481 "number": "three", 482 "rank": 3 483 } 484 ] 485 }`, nil)) 486 487 t.Run("AppendObject", putObjectTest(`{ 488 "numbers": [ 489 { 490 "rank": 1, 491 "number": "one" 492 }, 493 { 494 "rank": 2, 495 "number": "two" 496 }, 497 { 498 "rank": 3, 499 "number": "three" 500 } 501 ] 502 }`, "json", ".numbers.[]", []string{"number=five", "rank=5"}, []string{"string", "int"}, `{ 503 "numbers": [ 504 { 505 "number": "one", 506 "rank": 1 507 }, 508 { 509 "number": "two", 510 "rank": 2 511 }, 512 { 513 "number": "three", 514 "rank": 3 515 }, 516 { 517 "number": "five", 518 "rank": 5 519 } 520 ] 521 }`, nil)) 522 } 523 524 func TestPut_YAML(t *testing.T) { 525 t.Run("SingleProperty", putTest(` 526 details: 527 age: 27 528 name: Tom 529 id: 1111 530 `, "yaml", ".id", "2222", "string", ` 531 details: 532 age: 27 533 name: Tom 534 id: "2222" 535 `, nil)) 536 537 t.Run("ObjectPropertyString", putTest(` 538 details: 539 age: 27 540 name: Tom 541 id: 1111 542 `, "yaml", ".details.name", "Frank", "string", `details: 543 age: 27 544 name: Frank 545 id: 1111 546 `, nil)) 547 548 t.Run("ObjectPropertyInt", putTest(` 549 details: 550 age: 20 551 name: Tom 552 id: 1111 553 `, "yaml", ".details.age", "27", "int", ` 554 details: 555 age: 27 556 name: Tom 557 id: 1111 558 `, nil)) 559 560 t.Run("IndexString", putTest(` 561 numbers: 562 - one 563 - two 564 - three 565 `, "yaml", ".numbers.[1]", "four", "string", ` 566 numbers: 567 - one 568 - four 569 - three 570 `, nil)) 571 572 t.Run("DynamicString", putTest(` 573 numbers: 574 - one 575 - two 576 - three 577 `, "yaml", ".numbers.(value=three)", "four", "string", ` 578 numbers: 579 - one 580 - two 581 - four 582 `, nil)) 583 584 t.Run("DynamicInt", putTest(` 585 numbers: 586 - 1 587 - 2 588 - 3 589 `, "yaml", ".numbers.(value=3)", "4", "int", ` 590 numbers: 591 - 1 592 - 2 593 - 4 594 `, nil)) 595 596 t.Run("IndexInt", putTest(` 597 numbers: 598 - 1 599 - 2 600 - 3 601 `, "yaml", ".numbers.[1]", "4", "int", ` 602 numbers: 603 - 1 604 - 4 605 - 3 606 `, nil)) 607 608 t.Run("DynamicString", putTest(` 609 numbers: 610 - number: one 611 rank: 1 612 - number: two 613 rank: 2 614 - number: three 615 rank: 3 616 `, "yaml", ".numbers.(number=two).number", "four", "string", ` 617 numbers: 618 - number: one 619 rank: 1 620 - number: four 621 rank: 2 622 - number: three 623 rank: 3 624 `, nil)) 625 626 t.Run("DynamicInt", putTest(` 627 numbers: 628 - number: one 629 rank: 1 630 - number: two 631 rank: 2 632 - number: three 633 rank: 3 634 `, "yaml", ".numbers.(rank=2).rank", "4", "int", ` 635 numbers: 636 - number: one 637 rank: 1 638 - number: two 639 rank: 4 640 - number: three 641 rank: 3 642 `, nil)) 643 644 t.Run("OverwriteObject", putObjectTest(` 645 numbers: 646 - number: one 647 rank: 1 648 - number: two 649 rank: 2 650 - number: three 651 rank: 3 652 `, "yaml", ".numbers.[0]", []string{"number=five", "rank=5"}, []string{"string", "int"}, ` 653 numbers: 654 - number: five 655 rank: 5 656 - number: two 657 rank: 2 658 - number: three 659 rank: 3 660 `, nil)) 661 662 t.Run("AppendObject", putObjectTest(` 663 numbers: 664 - number: one 665 rank: 1 666 - number: two 667 rank: 2 668 - number: three 669 rank: 3 670 `, "yaml", ".numbers.[]", []string{"number=five", "rank=5"}, []string{"string", "int"}, ` 671 numbers: 672 - number: one 673 rank: 1 674 - number: two 675 rank: 2 676 - number: three 677 rank: 3 678 - number: five 679 rank: 5 680 `, nil)) 681 682 } 683 684 func TestPut_TOML(t *testing.T) { 685 t.Run("SingleProperty", putTest(` 686 id = "1111" 687 688 [details] 689 age = 27 690 name = "Tom" 691 `, "toml", ".id", "2222", "string", ` 692 id = "2222" 693 694 [details] 695 age = 27 696 name = "Tom" 697 `, nil)) 698 699 t.Run("ObjectPropertyString", putTest(` 700 id = "1111" 701 702 [details] 703 age = 27 704 name = "Tom" 705 `, "toml", ".details.name", "Frank", "string", ` 706 id = "1111" 707 708 [details] 709 age = 27 710 name = "Frank" 711 `, nil)) 712 713 t.Run("ObjectPropertyInt", putTest(` 714 id = "1111" 715 716 [details] 717 age = 20 718 name = "Tom" 719 `, "toml", ".details.age", "27", "int", ` 720 id = "1111" 721 722 [details] 723 age = 27 724 name = "Tom" 725 `, nil)) 726 727 t.Run("IndexString", putTest(` 728 numbers = ["one", "two", "three"] 729 `, "toml", ".numbers.[1]", "four", "string", ` 730 numbers = ["one", "four", "three"] 731 `, nil)) 732 733 t.Run("DynamicString", putTest(` 734 numbers = ["one", "two", "three"] 735 `, "toml", ".numbers.(value=three)", "four", "string", ` 736 numbers = ["one", "two", "four"] 737 `, nil)) 738 739 t.Run("DynamicInt", putTest(` 740 numbers = [1, 2, 3] 741 `, "toml", ".numbers.(value=3)", "4", "int", ` 742 numbers = [1, 2, 4] 743 `, nil)) 744 745 t.Run("IndexInt", putTest(` 746 numbers = [1, 2, 3] 747 `, "toml", ".numbers.[1]", "4", "int", ` 748 numbers = [1, 4, 3] 749 `, nil)) 750 751 t.Run("DynamicString", putTest(` 752 [[numbers]] 753 number = "one" 754 rank = 1 755 756 [[numbers]] 757 number = "two" 758 rank = 2 759 760 [[numbers]] 761 number = "three" 762 rank = 3 763 `, "toml", ".numbers.(number=two).number", "four", "string", ` 764 [[numbers]] 765 number = "one" 766 rank = 1 767 768 [[numbers]] 769 number = "four" 770 rank = 2 771 772 [[numbers]] 773 number = "three" 774 rank = 3 775 `, nil)) 776 777 t.Run("DynamicInt", putTest(` 778 [[numbers]] 779 number = "one" 780 rank = 1 781 782 [[numbers]] 783 number = "two" 784 rank = 2 785 786 [[numbers]] 787 number = "three" 788 rank = 3 789 `, "toml", ".numbers.(rank=2).rank", "4", "int", ` 790 [[numbers]] 791 number = "one" 792 rank = 1 793 794 [[numbers]] 795 number = "two" 796 rank = 4 797 798 [[numbers]] 799 number = "three" 800 rank = 3 801 `, nil)) 802 803 t.Run("OverwriteObject", putObjectTest(` 804 [[numbers]] 805 number = "one" 806 rank = 1 807 808 [[numbers]] 809 number = "two" 810 rank = 2 811 812 [[numbers]] 813 number = "three" 814 rank = 3 815 `, "toml", ".numbers.[0]", []string{"number=five", "rank=5"}, []string{"string", "int"}, ` 816 [[numbers]] 817 number = "five" 818 rank = 5 819 820 [[numbers]] 821 number = "two" 822 rank = 2 823 824 [[numbers]] 825 number = "three" 826 rank = 3 827 `, nil)) 828 829 t.Run("AppendObject", putObjectTest(` 830 [[numbers]] 831 number = "one" 832 rank = 1 833 834 [[numbers]] 835 number = "two" 836 rank = 2 837 838 [[numbers]] 839 number = "three" 840 rank = 3 841 `, "toml", ".numbers.[]", []string{"number=five", "rank=5"}, []string{"string", "int"}, ` 842 [[numbers]] 843 number = "one" 844 rank = 1 845 846 [[numbers]] 847 number = "two" 848 rank = 2 849 850 [[numbers]] 851 number = "three" 852 rank = 3 853 854 [[numbers]] 855 number = "five" 856 rank = 5 857 `, nil)) 858 }