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  }