github.com/kubevela/workflow@v0.6.0/pkg/cue/model/value/value_test.go (about) 1 /* 2 Copyright 2022 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package value 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "testing" 23 24 "cuelang.org/go/cue" 25 "cuelang.org/go/cue/format" 26 cuejson "cuelang.org/go/pkg/encoding/json" 27 "github.com/kubevela/workflow/pkg/cue/model/sets" 28 29 "github.com/pkg/errors" 30 "github.com/stretchr/testify/require" 31 ) 32 33 func TestValueFill(t *testing.T) { 34 r := require.New(t) 35 src := ` 36 object: { 37 x: int 38 y: string 39 z: { 40 provider: string 41 do: string 42 } 43 } 44 ` 45 testVal1, err := NewValue(src, nil, "") 46 r.NoError(err) 47 err = testVal1.FillObject(12, "object", "x") 48 r.NoError(err) 49 err = testVal1.FillObject("y_string", "object", "y") 50 r.NoError(err) 51 z, err := testVal1.MakeValue(` 52 z: { 53 provider: string 54 do: "apply" 55 } 56 `) 57 r.NoError(err) 58 err = z.FillObject("kube", "z", "provider") 59 r.NoError(err) 60 err = testVal1.FillObject(z, "object") 61 r.NoError(err) 62 val1String, err := testVal1.String() 63 r.NoError(err) 64 65 expectedValString := `object: { 66 x: 12 67 y: "y_string" 68 z: { 69 provider: "kube" 70 do: "apply" 71 } 72 } 73 ` 74 r.Equal(val1String, expectedValString) 75 76 testVal2, err := NewValue(src, nil, "") 77 r.NoError(err) 78 err = testVal2.FillRaw(expectedValString) 79 r.NoError(err) 80 val2String, err := testVal1.String() 81 r.NoError(err) 82 r.Equal(val2String, expectedValString) 83 } 84 85 func TestStepByFields(t *testing.T) { 86 testCases := []struct { 87 base string 88 expected string 89 }{ 90 {base: ` 91 step1: {} 92 step2: {prefix: step1.value} 93 step3: {prefix: step2.value} 94 step4: {prefix: step3.value} 95 if step4.value > 100 { 96 step5: {prefix: step4.value} 97 } 98 `, 99 expected: `step1: { 100 value: 100 101 } 102 step2: { 103 prefix: 100 104 value: 101 105 } 106 step3: { 107 prefix: 101 108 value: 102 109 } 110 step5: { 111 prefix: 103 112 value: 104 113 } 114 step4: { 115 prefix: 102 116 value: 103 117 } 118 `}, 119 120 {base: ` 121 step1: {} 122 step2: {prefix: step1.value} 123 if step2.value > 100 { 124 step2_3: {prefix: step2.value} 125 } 126 step3: {prefix: step2.value} 127 `, 128 expected: `step1: { 129 value: 100 130 } 131 step2: { 132 prefix: 100 133 value: 101 134 } 135 step2_3: { 136 prefix: 101 137 value: 102 138 } 139 step3: { 140 prefix: 101 141 value: 103 142 } 143 `}, 144 } 145 146 for _, tCase := range testCases { 147 r := require.New(t) 148 val, err := NewValue(tCase.base, nil, "") 149 r.NoError(err) 150 number := 99 151 err = val.StepByFields(func(_ string, in *Value) (bool, error) { 152 number++ 153 return false, in.FillObject(map[string]interface{}{ 154 "value": number, 155 }) 156 }) 157 r.NoError(err) 158 str, err := val.String() 159 r.NoError(err) 160 r.Equal(str, tCase.expected) 161 } 162 163 r := require.New(t) 164 caseSkip := ` 165 step1: "1" 166 step2: "2" 167 step3: "3" 168 ` 169 val, err := NewValue(caseSkip, nil, "") 170 r.NoError(err) 171 inc := 0 172 err = val.StepByFields(func(_ string, in *Value) (bool, error) { 173 inc++ 174 s, err := in.CueValue().String() 175 r.NoError(err) 176 if s == "2" { 177 return true, nil 178 } 179 return false, nil 180 }) 181 r.NoError(err) 182 r.Equal(inc, 2) 183 184 inc = 0 185 err = val.StepByFields(func(_ string, in *Value) (bool, error) { 186 inc++ 187 s, err := in.CueValue().String() 188 r.NoError(err) 189 if s == "2" { 190 return false, errors.New("mock error") 191 } 192 return false, nil 193 }) 194 r.Error(err) 195 r.Equal(inc, 2) 196 197 inc = 0 198 err = val.StepByFields(func(_ string, in *Value) (bool, error) { 199 inc++ 200 s, err := in.CueValue().String() 201 r.NoError(err) 202 if s == "2" { 203 v, err := NewValue("v: 33", nil, "") 204 r.NoError(err) 205 *in = *v 206 } 207 return false, nil 208 }) 209 r.Error(err) 210 r.Equal(inc, 2) 211 } 212 213 func TestStepWithTag(t *testing.T) { 214 testCases := []struct { 215 base string 216 expected string 217 }{ 218 {base: ` 219 step1: {} 220 step2: {prefix: step1.value} 221 step3: {prefix: step2.value} 222 step4: {prefix: step3.value} 223 if step4.value > 100 { 224 step5: {} 225 } 226 step5: { 227 value: *100|int 228 } 229 `, 230 expected: `step1: { 231 value: 100 232 } @step(1) 233 step2: { 234 prefix: 100 235 value: 101 236 } @step(2) 237 step3: { 238 prefix: 101 239 value: 102 240 } @step(3) 241 step4: { 242 prefix: 102 243 value: 103 244 } @step(4) 245 step5: { 246 value: 104 247 } @step(5) 248 `}, {base: ` 249 step1: {} 250 step2: {prefix: step1.value} 251 if step2.value > 100 { 252 step2_3: {prefix: step2.value} 253 } 254 step3: {prefix: step2.value} 255 step4: {prefix: step3.value} 256 `, 257 expected: `step1: { 258 value: 100 259 } @step(1) 260 step2: { 261 prefix: 100 262 value: 101 263 } @step(2) 264 step3: { 265 prefix: 101 266 value: 103 267 } @step(4) 268 step2_3: { 269 prefix: 101 270 value: 102 271 } @step(3) 272 step4: { 273 prefix: 103 274 value: 104 275 } @step(5) 276 `}, {base: ` 277 step2: {prefix: step1.value} @step(2) 278 step1: {} @step(1) 279 step3: {prefix: step2.value} @step(4) 280 if step2.value > 100 { 281 step2_3: {prefix: step2.value} @step(3) 282 } 283 `, 284 expected: `step2: { 285 prefix: 100 286 value: 101 287 } @step(2) 288 step1: { 289 value: 100 290 } @step(1) 291 step2_3: { 292 prefix: 101 293 value: 102 294 } @step(3) 295 step3: { 296 prefix: 101 297 value: 103 298 } @step(4) 299 `}, 300 301 {base: ` 302 step2: {prefix: step1.value} 303 step1: {} @step(-1) 304 if step2.value > 100 { 305 step2_3: {prefix: step2.value} 306 } 307 step3: {prefix: step2.value} 308 `, 309 expected: `step2: { 310 prefix: 100 311 value: 101 312 } @step(1) 313 step1: { 314 value: 100 315 } @step(-1) 316 step2_3: { 317 prefix: 101 318 value: 102 319 } @step(2) 320 step3: { 321 prefix: 101 322 value: 103 323 } @step(3) 324 `}} 325 326 for i, tCase := range testCases { 327 r := require.New(t) 328 val, err := NewValue(tCase.base, nil, "", TagFieldOrder) 329 r.NoError(err) 330 number := 99 331 err = val.StepByFields(func(name string, in *Value) (bool, error) { 332 number++ 333 return false, in.FillObject(map[string]interface{}{ 334 "value": number, 335 }) 336 }) 337 r.NoError(err) 338 str, err := sets.ToString(val.CueValue()) 339 r.NoError(err) 340 r.Equal(str, tCase.expected, fmt.Sprintf("testPatch for case(no:%d) %s", i, str)) 341 } 342 } 343 344 func TestUnmarshal(t *testing.T) { 345 case1 := ` 346 provider: "kube" 347 do: "apply" 348 ` 349 out := struct { 350 Provider string `json:"provider"` 351 Do string `json:"do"` 352 }{} 353 354 r := require.New(t) 355 val, err := NewValue(case1, nil, "") 356 r.NoError(err) 357 err = val.UnmarshalTo(&out) 358 r.NoError(err) 359 r.Equal(out.Provider, "kube") 360 r.Equal(out.Do, "apply") 361 362 bt, err := val.CueValue().MarshalJSON() 363 r.NoError(err) 364 expectedJson, err := json.Marshal(out) 365 r.NoError(err) 366 r.Equal(string(bt), string(expectedJson)) 367 368 caseIncomplete := ` 369 provider: string 370 do: string 371 ` 372 val, err = NewValue(caseIncomplete, nil, "") 373 r.NoError(err) 374 err = val.UnmarshalTo(&out) 375 r.Error(err) 376 } 377 378 func TestStepByList(t *testing.T) { 379 r := require.New(t) 380 base := `[{step: 1},{step: 2}]` 381 v, err := NewValue(base, nil, "") 382 r.NoError(err) 383 var i int64 384 err = v.StepByList(func(name string, in *Value) (bool, error) { 385 i++ 386 num, err := in.CueValue().LookupPath(FieldPath("step")).Int64() 387 r.NoError(err) 388 r.Equal(num, i) 389 return false, nil 390 }) 391 r.NoError(err) 392 393 i = 0 394 err = v.StepByList(func(_ string, _ *Value) (bool, error) { 395 i++ 396 return true, nil 397 }) 398 r.NoError(err) 399 r.Equal(i, int64(1)) 400 401 i = 0 402 err = v.StepByList(func(_ string, _ *Value) (bool, error) { 403 i++ 404 return false, errors.New("mock error") 405 }) 406 r.Equal(err.Error(), "mock error") 407 r.Equal(i, int64(1)) 408 409 notListV, err := NewValue(`{}`, nil, "") 410 r.NoError(err) 411 err = notListV.StepByList(func(_ string, _ *Value) (bool, error) { 412 return false, nil 413 }) 414 r.Error(err) 415 } 416 417 func TestFieldPath(t *testing.T) { 418 testCases := []struct { 419 paths []string 420 expected cue.Path 421 }{ 422 { 423 paths: []string{""}, 424 expected: cue.ParsePath(""), 425 }, 426 { 427 paths: []string{`a.b`}, 428 expected: cue.ParsePath("a.b"), 429 }, 430 { 431 paths: []string{`a[0]`}, 432 expected: cue.ParsePath("a[0]"), 433 }, 434 { 435 paths: []string{`_a`}, 436 expected: cue.ParsePath("_a"), 437 }, 438 { 439 paths: []string{`#a`}, 440 expected: cue.ParsePath("#a"), 441 }, 442 { 443 paths: []string{`a`}, 444 expected: cue.ParsePath(`"a"`), 445 }, 446 { 447 paths: []string{`"1"`}, 448 expected: cue.MakePath(cue.Str("1")), 449 }, 450 { 451 paths: []string{`1`}, 452 expected: cue.MakePath(cue.Str("1")), 453 }, 454 { 455 paths: []string{`1`, `"#a"`, `b`}, 456 expected: cue.ParsePath(`"1".#a["b"]`), 457 }, 458 } 459 for i, tc := range testCases { 460 t.Run(fmt.Sprint(i), func(t *testing.T) { 461 r := require.New(t) 462 fp := FieldPath(tc.paths...) 463 r.Equal(tc.expected, fp) 464 }) 465 } 466 } 467 468 func TestValueFix(t *testing.T) { 469 testCases := []struct { 470 original string 471 expected string 472 }{ 473 { 474 original: ` 475 parameter: test: _ 476 // comment 477 y: { 478 for k, v in parameter.test.p { 479 "\(k)": v 480 } 481 }`, 482 expected: `{ 483 parameter: { 484 test: _ 485 } 486 // comment 487 y: { 488 for k, v in *parameter.test.p | {} { 489 "\(k)": v 490 } 491 } 492 }`, 493 }, 494 } 495 for i, tc := range testCases { 496 t.Run(fmt.Sprint(i), func(t *testing.T) { 497 r := require.New(t) 498 v, err := NewValue(tc.original, nil, "") 499 r.NoError(err) 500 b, err := format.Node(v.CueValue().Syntax(cue.Docs(true))) 501 r.NoError(err) 502 r.Equal(tc.expected, string(b)) 503 }) 504 } 505 } 506 507 func TestValue(t *testing.T) { 508 509 // Test NewValue with wrong cue format. 510 caseError := ` 511 provider: xxx 512 ` 513 r := require.New(t) 514 val, err := NewValue(caseError, nil, "") 515 r.NoError(err) 516 r.Error(val.Error()) 517 518 val, err = NewValue(":", nil, "") 519 r.Error(err) 520 r.Equal(val == nil, true) 521 522 // Test make error by Fill with wrong cue format. 523 caseOk := ` 524 provider: "kube" 525 do: "apply" 526 ` 527 val, err = NewValue(caseOk, nil, "") 528 r.NoError(err) 529 originCue := val.CueValue() 530 531 _, err = val.MakeValue(caseError) 532 r.Error(err) 533 _, err = val.MakeValue(":") 534 r.Error(err) 535 _, err = val.MakeValue("test: _|_") 536 r.Error(err) 537 err = val.FillRaw(caseError) 538 r.Error(err) 539 r.Equal(originCue, val.CueValue()) 540 cv, err := NewValue(caseOk, nil, "") 541 r.NoError(err) 542 err = val.FillObject(cv) 543 r.Error(err) 544 r.Equal(originCue, val.CueValue()) 545 546 // Test make error by Fill with cue eval error. 547 caseClose := ` 548 close({provider: int}) 549 ` 550 err = val.FillRaw(caseClose) 551 r.Error(err) 552 r.Equal(originCue, val.CueValue()) 553 cv, err = val.MakeValue(caseClose) 554 r.NoError(err) 555 err = val.FillObject(cv) 556 r.NoError(err) 557 r.Error(val.Error()) 558 559 _, err = val.LookupValue("abc") 560 r.Error(err) 561 562 providerValue, err := val.LookupValue("provider") 563 r.NoError(err) 564 err = providerValue.StepByFields(func(_ string, in *Value) (bool, error) { 565 return false, nil 566 }) 567 r.Error(err) 568 569 openSt := ` 570 #X: {...} 571 x: #X & { 572 name: "xxx" 573 age: 12 574 } 575 ` 576 val, err = NewValue(openSt, nil, "") 577 r.NoError(err) 578 x, _ := val.LookupValue("x") 579 xs, _ := x.String() 580 _, err = val.MakeValue(xs) 581 r.NoError(err) 582 } 583 584 func TestLookupValue(t *testing.T) { 585 testCases := []struct { 586 name string 587 str string 588 paths []string 589 }{ 590 { 591 name: "def", 592 str: ` 593 #x: "v" 594 `, 595 paths: []string{"#x"}, 596 }, 597 { 598 name: "def in def", 599 str: ` 600 #x: { 601 #y: "v" 602 } 603 `, 604 paths: []string{"#x", "#y"}, 605 }, 606 { 607 name: "num", 608 str: ` 609 "1": { 610 "2": "v" 611 } 612 `, 613 paths: []string{"1", "2"}, 614 }, 615 { 616 name: "invalid", 617 str: ` 618 "a-b": { 619 "b-c": "v" 620 } 621 `, 622 paths: []string{"a-b", "b-c"}, 623 }, 624 { 625 name: "concrete path", 626 str: ` 627 a: { 628 "b-c": "v" 629 } 630 `, 631 paths: []string{`a["b-c"]`}, 632 }, 633 { 634 name: "concrete path with num", 635 str: ` 636 a: [ 637 { 638 key: "v" 639 } 640 ] 641 `, 642 paths: []string{`a[0].key`}, 643 }, 644 } 645 646 for _, tc := range testCases { 647 t.Run(tc.name, func(t *testing.T) { 648 r := require.New(t) 649 v, err := NewValue(tc.str, nil, "") 650 r.NoError(err) 651 result, err := v.LookupValue(tc.paths...) 652 r.NoError(err) 653 r.NoError(result.Error()) 654 s, err := sets.ToString(result.v) 655 r.Equal(s, `"v" 656 `) 657 r.NoError(err) 658 }) 659 } 660 } 661 662 func TestValueError(t *testing.T) { 663 caseOk := ` 664 provider: "kube" 665 do: "apply" 666 ` 667 r := require.New(t) 668 val, err := NewValue(caseOk, nil, "") 669 r.NoError(err) 670 err = val.FillRaw(` 671 provider: "conflict"`) 672 r.Error(err) 673 674 val, err = NewValue(caseOk, nil, "") 675 r.NoError(err) 676 err = val.FillObject(map[string]string{ 677 "provider": "abc", 678 }) 679 r.NoError(err) 680 r.Error(val.Error()) 681 } 682 683 func TestField(t *testing.T) { 684 caseSrc := ` 685 name: "foo" 686 #name: "fly" 687 #age: 100 688 bottom: _|_ 689 ` 690 r := require.New(t) 691 val, err := NewValue(caseSrc, nil, "") 692 r.NoError(err) 693 694 name, err := val.Field("name") 695 r.NoError(err) 696 nameValue, err := name.String() 697 r.NoError(err) 698 r.Equal(nameValue, "foo") 699 700 dname, err := val.Field("#name") 701 r.NoError(err) 702 nameValue, err = dname.String() 703 r.NoError(err) 704 r.Equal(nameValue, "fly") 705 706 _, err = val.Field("age") 707 r.Error(err) 708 709 _, err = val.Field("bottom") 710 r.Error(err) 711 } 712 713 func TestProcessScript(t *testing.T) { 714 testCases := []struct { 715 src string 716 expect string 717 err string 718 }{ 719 { 720 src: `parameter: { 721 check: "status==\"ready\"" 722 } 723 724 wait: { 725 status: "ready" 726 continue: script(parameter.check) 727 }`, 728 expect: `parameter: { 729 check: "status==\"ready\"" 730 } 731 wait: { 732 status: "ready" 733 continue: true 734 } 735 `, 736 }, 737 { 738 src: `parameter: { 739 check: "status==\"ready\"" 740 } 741 742 wait: { 743 status: "ready" 744 continue: script("") 745 }`, 746 expect: ``, 747 err: "script parameter error", 748 }, 749 { 750 src: `parameter: { 751 check: "status=\"ready\"" 752 } 753 754 wait: { 755 status: "ready" 756 continue: script(parameter.check) 757 }`, 758 expect: ``, 759 err: "script value(status=\"ready\") is invalid CueLang", 760 }, 761 } 762 763 for _, tCase := range testCases { 764 r := require.New(t) 765 v, err := NewValue(tCase.src, nil, "", ProcessScript) 766 if tCase.err != "" { 767 r.Equal(err.Error(), tCase.err) 768 continue 769 } 770 r.NoError(err) 771 s, err := v.String() 772 r.NoError(err) 773 r.Equal(s, tCase.expect) 774 } 775 } 776 777 func TestLookupByScript(t *testing.T) { 778 testCases := []struct { 779 src string 780 script string 781 expect string 782 }{ 783 { 784 src: ` 785 traits: { 786 ingress: { 787 // +patchKey=name 788 test: [{name: "main", image: "busybox"}] 789 } 790 } 791 `, 792 script: `traits["ingress"]`, 793 expect: `// +patchKey=name 794 test: [{ 795 name: "main" 796 image: "busybox" 797 }] 798 `, 799 }, 800 { 801 src: ` 802 apply: containers: [{name: "main", image: "busybox"}] 803 `, 804 script: `apply.containers[0].image`, 805 expect: `"busybox" 806 `, 807 }, 808 { 809 src: ` 810 apply: workload: name: "main" 811 `, 812 script: ` 813 apply.workload.name`, 814 expect: `"main" 815 `, 816 }, 817 { 818 src: ` 819 apply: arr: ["abc","def"] 820 `, 821 script: ` 822 import "strings" 823 strings.Join(apply.arr,".")+"$"`, 824 expect: `"abc.def$" 825 `, 826 }, 827 } 828 829 for _, tCase := range testCases { 830 r := require.New(t) 831 srcV, err := NewValue(tCase.src, nil, "") 832 r.NoError(err) 833 v, err := srcV.LookupByScript(tCase.script) 834 r.NoError(err) 835 result, _ := v.String() 836 r.Equal(tCase.expect, result) 837 } 838 839 errorCases := []struct { 840 src string 841 script string 842 err string 843 }{ 844 { 845 src: ` 846 op: string 847 op: "help" 848 `, 849 script: `op(1`, 850 err: "parse script: expected ')', found 'EOF'", 851 }, 852 { 853 src: ` 854 op: string 855 op: "help" 856 `, 857 script: `oss`, 858 err: "failed to lookup value: var(path=oss) not exist", 859 }, 860 } 861 862 for _, tCase := range errorCases { 863 r := require.New(t) 864 srcV, err := NewValue(tCase.src, nil, "") 865 r.NoError(err) 866 _, err = srcV.LookupByScript(tCase.script) 867 r.Error(err, tCase.err) 868 r.Equal(err.Error(), tCase.err) 869 } 870 } 871 872 func TestGet(t *testing.T) { 873 caseOk := ` 874 strKey: "xxx" 875 intKey: 100 876 boolKey: true 877 ` 878 r := require.New(t) 879 val, err := NewValue(caseOk, nil, "") 880 r.NoError(err) 881 882 str, err := val.GetString("strKey") 883 r.NoError(err) 884 r.Equal(str, "xxx") 885 // err case 886 _, err = val.GetInt64("strKey") 887 r.Error(err) 888 889 intv, err := val.GetInt64("intKey") 890 r.NoError(err) 891 r.Equal(intv, int64(100)) 892 // err case 893 _, err = val.GetBool("intKey") 894 r.Error(err) 895 896 ok, err := val.GetBool("boolKey") 897 r.NoError(err) 898 r.Equal(ok, true) 899 // err case 900 _, err = val.GetString("boolKey") 901 r.Error(err) 902 } 903 904 func TestImports(t *testing.T) { 905 cont := ` 906 context: stepSessionID: "3w9qkdgn5w"` 907 v, err := NewValue(` 908 import ( 909 "vela/custom" 910 ) 911 912 id: custom.context.stepSessionID 913 914 `+cont, nil, cont) 915 r := require.New(t) 916 r.NoError(err) 917 id, err := v.GetString("id") 918 r.NoError(err) 919 r.Equal(id, "3w9qkdgn5w") 920 } 921 922 func TestOpenCompleteValue(t *testing.T) { 923 v, err := NewValue(` 924 x: 10 925 y: "100" 926 `, nil, "") 927 r := require.New(t) 928 r.NoError(err) 929 err = v.OpenCompleteValue() 930 r.NoError(err) 931 s, err := v.String() 932 r.NoError(err) 933 r.Equal(s, `x: *10 | _ 934 y: *"100" | _ 935 `) 936 } 937 938 func TestFillByScript(t *testing.T) { 939 testCases := []struct { 940 name string 941 raw string 942 path string 943 v string 944 expected string 945 }{ 946 { 947 name: "insert array", 948 raw: `a: ["hello"]`, 949 path: "a[1]", 950 v: `"world"`, 951 expected: `a: ["hello", "world", ...] 952 `}, 953 { 954 name: "insert array", 955 raw: `a: b: [{x: 100},...]`, 956 path: "a.b[1]", 957 v: `{name: "foo"}`, 958 expected: `a: { 959 b: [{ 960 x: 100 961 }, { 962 name: "foo" 963 }, ...] 964 } 965 `}, 966 { 967 name: "insert array to array", 968 raw: ` 969 a: b: c: [{x: 100}, {x: 101}, {x: 102}]`, 970 path: "a.b.c[0].value", 971 v: `"foo"`, 972 expected: `a: { 973 b: { 974 c: [{ 975 x: 100 976 value: "foo" 977 }, { 978 x: 101 979 }, { 980 x: 102 981 }, ...] 982 } 983 } 984 `, 985 }, 986 { 987 name: "insert nest array ", 988 raw: `a: b: [{x: y:[{name: "key"}]}]`, 989 path: "a.b[0].x.y[0].value", 990 v: `"foo"`, 991 expected: `a: { 992 b: [{ 993 x: { 994 y: [{ 995 name: "key" 996 value: "foo" 997 }, ...] 998 } 999 }, ...] 1000 } 1001 `, 1002 }, 1003 { 1004 name: "insert without array", 1005 raw: `a: b: [{x: y:[{name: "key"}]}]`, 1006 path: "a.c.x", 1007 v: `"foo"`, 1008 expected: `a: { 1009 b: [{ 1010 x: { 1011 y: [{ 1012 name: "key" 1013 }, ...] 1014 } 1015 }, ...] 1016 c: { 1017 x: "foo" 1018 } 1019 } 1020 `, 1021 }, 1022 { 1023 name: "path with string index", 1024 raw: `a: b: [{x: y:[{name: "key"}]}]`, 1025 path: "a.c[\"x\"]", 1026 v: `"foo"`, 1027 expected: `a: { 1028 b: [{ 1029 x: { 1030 y: [{ 1031 name: "key" 1032 }, ...] 1033 } 1034 }, ...] 1035 c: { 1036 x: "foo" 1037 } 1038 } 1039 `, 1040 }, 1041 } 1042 1043 for _, tCase := range testCases { 1044 r := require.New(t) 1045 v, err := NewValue(tCase.raw, nil, "") 1046 r.NoError(err) 1047 val, err := v.MakeValue(tCase.v) 1048 r.NoError(err) 1049 err = v.FillValueByScript(val, tCase.path) 1050 r.NoError(err) 1051 s, err := v.String() 1052 r.NoError(err) 1053 r.Equal(s, tCase.expected, tCase.name) 1054 } 1055 1056 errCases := []struct { 1057 name string 1058 raw string 1059 path string 1060 v string 1061 err string 1062 }{ 1063 { 1064 name: "invalid path", 1065 raw: `a: b: [{x: 100},...]`, 1066 path: "a.b[1]+1", 1067 v: `{name: "foo"}`, 1068 err: "invalid path: invalid label a.b[1]+1 ", 1069 }, 1070 { 1071 name: "invalid path [float]", 1072 raw: `a: b: [{x: 100},...]`, 1073 path: "a.b[0.1]", 1074 v: `{name: "foo"}`, 1075 err: "invalid path: invalid literal 0.1", 1076 }, 1077 { 1078 name: "conflict merge", 1079 raw: `a: b: [{x: y:[{name: "key"}]}]`, 1080 path: "a.b[0].x.y[0].name", 1081 v: `"foo"`, 1082 err: "a.b.0.x.y.0.name: conflicting values \"foo\" and \"key\"", 1083 }, 1084 } 1085 1086 for _, errCase := range errCases { 1087 r := require.New(t) 1088 v, err := NewValue(errCase.raw, nil, "") 1089 r.NoError(err) 1090 errV, err := v.MakeValue(errCase.v) 1091 r.NoError(err) 1092 err = v.FillValueByScript(errV, errCase.path) 1093 r.Equal(errCase.err, err.Error()) 1094 } 1095 } 1096 1097 func TestSetByScript(t *testing.T) { 1098 testCases := []struct { 1099 name string 1100 raw string 1101 path string 1102 v string 1103 expected string 1104 }{ 1105 { 1106 name: "insert array", 1107 raw: `a: ["hello"]`, 1108 path: "a[0]", 1109 v: `"world"`, 1110 expected: `a: ["world"] 1111 `}, 1112 { 1113 name: "insert array2", 1114 raw: `a: ["hello"]`, 1115 path: "a[1]", 1116 v: `"world"`, 1117 expected: `a: ["hello", "world"] 1118 `}, 1119 { 1120 name: "insert array3", 1121 raw: `a: b: [{x: 100}]`, 1122 path: "a.b[0]", 1123 v: `{name: "foo"}`, 1124 expected: `a: { 1125 b: [{ 1126 name: "foo" 1127 }] 1128 } 1129 `}, 1130 { 1131 name: "insert struct", 1132 raw: `a: {b: "hello"}`, 1133 path: "a.b", 1134 v: `"world"`, 1135 expected: `a: { 1136 b: "world" 1137 } 1138 `}, 1139 { 1140 name: "insert struct2", 1141 raw: `a: {b: "hello"}, c: {d: "world"}`, 1142 path: "c.d", 1143 v: `"hello"`, 1144 expected: `a: { 1145 b: "hello" 1146 } 1147 c: { 1148 d: "hello" 1149 } 1150 `}, 1151 { 1152 name: "insert array to array", 1153 raw: ` 1154 a: b: c: [{x: 100}, {x: 101}, {x: 102}]`, 1155 path: "a.b.c[0].value", 1156 v: `"foo"`, 1157 expected: `a: { 1158 b: { 1159 c: [{ 1160 x: 100 1161 value: "foo" 1162 }, { 1163 x: 101 1164 }, { 1165 x: 102 1166 }] 1167 } 1168 } 1169 `, 1170 }, 1171 { 1172 name: "insert nest array ", 1173 raw: `a: b: [{x: y:[{name: "key"}]}]`, 1174 path: "a.b[0].x.y[0].value", 1175 v: `"foo"`, 1176 expected: `a: { 1177 b: [{ 1178 x: { 1179 y: [{ 1180 name: "key" 1181 value: "foo" 1182 }] 1183 } 1184 }] 1185 } 1186 `, 1187 }, 1188 { 1189 name: "insert without array", 1190 raw: `a: b: [{x: y:[{name: "key"}]}]`, 1191 path: "a.c.x", 1192 v: `"foo"`, 1193 expected: `a: { 1194 b: [{ 1195 x: { 1196 y: [{ 1197 name: "key" 1198 }] 1199 } 1200 }] 1201 c: { 1202 x: "foo" 1203 } 1204 } 1205 `, 1206 }, 1207 { 1208 name: "path with string index", 1209 raw: `a: b: [{x: y:[{name: "key"}]}]`, 1210 path: "a.c[\"x\"]", 1211 v: `"foo"`, 1212 expected: `a: { 1213 b: [{ 1214 x: { 1215 y: [{ 1216 name: "key" 1217 }] 1218 } 1219 }] 1220 c: { 1221 x: "foo" 1222 } 1223 } 1224 `, 1225 }, 1226 } 1227 1228 for _, tCase := range testCases { 1229 r := require.New(t) 1230 v, err := NewValue(tCase.raw, nil, "") 1231 r.NoError(err) 1232 val, err := v.MakeValue(tCase.v) 1233 r.NoError(err) 1234 err = v.SetValueByScript(val, tCase.path) 1235 r.NoError(err, tCase.name) 1236 s, err := v.String() 1237 r.NoError(err) 1238 r.Equal(s, tCase.expected, tCase.name) 1239 } 1240 } 1241 1242 func TestSubstituteInStruct(t *testing.T) { 1243 base := ` 1244 value: { 1245 a: 1 1246 } 1247 ` 1248 r := require.New(t) 1249 val, err := NewValue(base, nil, "") 1250 r.NoError(err) 1251 expr, err := cuejson.Unmarshal([]byte(`{"b": 2}`)) 1252 r.NoError(err) 1253 err = val.SubstituteInStruct(expr, "value") 1254 r.NoError(err) 1255 s, err := val.String() 1256 r.NoError(err) 1257 r.Equal(s, `value: { 1258 b: 2 1259 } 1260 `) 1261 err = val.SubstituteInStruct(expr, "notfound") 1262 r.Error(err) 1263 1264 errBase := `1` 1265 val1, err := NewValue(errBase, nil, "") 1266 r.NoError(err) 1267 err = val1.SubstituteInStruct(expr, "value") 1268 r.Error(err) 1269 }