github.com/kubeshop/testkube@v1.17.23/pkg/tcl/expressionstcl/parse_test.go (about)

     1  // Copyright 2024 Testkube.
     2  //
     3  // Licensed as a Testkube Pro file under the Testkube Community
     4  // License (the "License"); you may not use this file except in compliance with
     5  // the License. You may obtain a copy of the License at
     6  //
     7  //     https://github.com/kubeshop/testkube/blob/main/licenses/TCL.txt
     8  
     9  package expressionstcl
    10  
    11  import (
    12  	"errors"
    13  	"fmt"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func TestCompileBasic(t *testing.T) {
    21  	assert.Equal(t, "value", must(MustCompile(`"value"`).Static().StringValue()))
    22  }
    23  
    24  func TestCompileTernary(t *testing.T) {
    25  	assert.Equal(t, "value", must(MustCompile(`true ? "value" : "another"`).Static().StringValue()))
    26  	assert.Equal(t, "another", must(MustCompile(`false ? "value" : "another"`).Static().StringValue()))
    27  	assert.Equal(t, "another", must(MustCompile(`5 == 3 ? "value" : "another"`).Static().StringValue()))
    28  	assert.Equal(t, "another", must(MustCompile(`5 == 3 && 2 == 4 ? "value" : "another"`).Static().StringValue()))
    29  	assert.Equal(t, "another", must(MustCompile(`5 == 3 || 2 == 4 ? "value" : "another"`).Static().StringValue()))
    30  	assert.Equal(t, "value", must(MustCompile(`3 == 3 || 2 == 4 ? "value" : "another"`).Static().StringValue()))
    31  	assert.Equal(t, "xyz", must(MustCompile(`false ? "value" : true ? "xyz" :"another"`).Static().StringValue()))
    32  	assert.Equal(t, "xyz", must(MustCompile(`false ? "value" : (true ? "xyz" :"another")`).Static().StringValue()))
    33  	assert.Equal(t, 5.78, must(MustCompile(`false ? 3 : (true ? 5.78 : 2)`).Static().FloatValue()))
    34  }
    35  
    36  func TestCompileMath(t *testing.T) {
    37  	assert.Equal(t, 5.0, must(MustCompile(`2 + 3`).Static().FloatValue()))
    38  	assert.Equal(t, 0.6, must(MustCompile(`3 / 5`).Static().FloatValue()))
    39  	assert.Equal(t, true, must(MustCompile(`3 <> 5`).Static().BoolValue()))
    40  	assert.Equal(t, true, must(MustCompile(`3 != 5`).Static().BoolValue()))
    41  	assert.Equal(t, false, must(MustCompile(`3 == 5`).Static().BoolValue()))
    42  	assert.Equal(t, false, must(MustCompile(`3 = 5`).Static().BoolValue()))
    43  	assert.Equal(t, `"3/5:"+(a+b)+"/"+5`, MustCompile(`3 + "/" + 5 + ":" + (a + b) + "/" + 5`).String())
    44  	assert.Equal(t, `(a+"/")+b+":"+(a+1)+"/"+b`, MustCompile(`a + "/" + b + ":" + (a + 1) + "/" + b`).String())
    45  }
    46  
    47  func TestCompileLogical(t *testing.T) {
    48  	assert.Equal(t, "true", MustCompile(`!(false && r1)`).String())
    49  	assert.Equal(t, "false", MustCompile(`!true && r1`).String())
    50  	assert.Equal(t, "r1", MustCompile(`true && r1`).String())
    51  	assert.Equal(t, "r1", MustCompile(`!true || r1`).String())
    52  	assert.Equal(t, "true", MustCompile(`true || r1`).String())
    53  	assert.Equal(t, "11", MustCompile(`5 - -3 * 2`).String())
    54  	assert.Equal(t, "r1&&false", MustCompile(`r1 && false`).String())
    55  	assert.Equal(t, "bool(r1)", MustCompile(`bool(r1) && true`).String())
    56  	assert.Equal(t, "false", MustCompile(`bool(r1) && false`).String())
    57  	assert.Equal(t, "r1||false", MustCompile(`r1 || false`).String())
    58  	assert.Equal(t, "bool(r1)", MustCompile(`bool(r1) || false`).String())
    59  	assert.Equal(t, "r1||true", MustCompile(`r1 || true`).String())
    60  	assert.Equal(t, "true", MustCompile(`bool(r1) || true`).String())
    61  }
    62  
    63  func TestCompileMathOperationsPrecedence(t *testing.T) {
    64  	assert.Equal(t, 7.0, must(MustCompile(`1 + 2 * 3`).Static().FloatValue()))
    65  	assert.Equal(t, 11.0, must(MustCompile(`1 + (2 * 3) + 4`).Static().FloatValue()))
    66  	assert.Equal(t, 11.0, must(MustCompile(`1 + 2 * 3 + 4`).Static().FloatValue()))
    67  	assert.Equal(t, 30.0, must(MustCompile(`1 + 2 * 3 * 4 + 5`).Static().FloatValue()))
    68  	assert.Equal(t, true, must(MustCompile(`1 + 2 * 3 * 4 + 5 <> 3`).Static().BoolValue()))
    69  
    70  	assert.Equal(t, false, must(MustCompile(`1 + 2 * 3 * 4 + 5 == 3`).Static().BoolValue()))
    71  	assert.Equal(t, true, must(MustCompile(`1 + 2 * 3 * 4 + 5 = 30`).Static().BoolValue()))
    72  	assert.Equal(t, false, must(MustCompile(`1 + 2 * 3 * 4 + 5 <> 30`).Static().BoolValue()))
    73  	assert.Equal(t, false, must(MustCompile(`1 + 2 * 3 * 4 + 5 <> 20 + 10`).Static().BoolValue()))
    74  	assert.Equal(t, true, must(MustCompile(`1 + 2 * 3 * 4 + 5 = 20 + 10`).Static().BoolValue()))
    75  	assert.Equal(t, false, must(MustCompile(`1 + 2 * 3 * 4 + 5 <> 20 + 10`).Static().BoolValue()))
    76  	assert.Equal(t, true, must(MustCompile(`1 + 2 * 3 * 4 + 5 = 2 + 3 * 6 + 10`).Static().BoolValue()))
    77  	assert.Equal(t, false, must(MustCompile(`1 + 2 * 3 * 4 + 5 <> 2 + 3 * 6 + 10`).Static().BoolValue()))
    78  	assert.Equal(t, 8.0, must(MustCompile(`5 + 3 / 3 * 3`).Static().FloatValue()))
    79  	assert.Equal(t, true, must(MustCompile(`5 + 3 / 3 * 3 = 8`).Static().BoolValue()))
    80  	assert.Equal(t, 8.0, must(MustCompile(`5 + 3 * 3 / 3`).Static().FloatValue()))
    81  	assert.Equal(t, true, must(MustCompile(`5 + 3 * 3 / 3 = 8`).Static().BoolValue()))
    82  	assert.Equal(t, true, must(MustCompile(`5 + 3 * 3 / 3 = 2 + 3 * 2`).Static().BoolValue()))
    83  	assert.Equal(t, false, must(MustCompile(`5 + 3 * 3 / 3 = 3 + 3 * 2`).Static().BoolValue()))
    84  
    85  	assert.Equal(t, false, must(MustCompile(`true && false || false && true`).Static().BoolValue()))
    86  	assert.Equal(t, true, must(MustCompile(`true && false || true`).Static().BoolValue()))
    87  	assert.Equal(t, int64(0), must(MustCompile(`1 && 0 && 2`).Static().IntValue()))
    88  	assert.Equal(t, int64(2), must(MustCompile(`1 && 0 || 2`).Static().IntValue()))
    89  	assert.Equal(t, int64(1), must(MustCompile(`1 || 0 || 2`).Static().IntValue()))
    90  
    91  	assert.Equal(t, true, must(MustCompile(`10 > 2 && 5 <= 5`).Static().BoolValue()))
    92  	assert.Equal(t, false, must(MustCompile(`10 > 2 && 5 < 5`).Static().BoolValue()))
    93  	assert.Error(t, errOnly(Compile(`10 > 2 > 3`)))
    94  
    95  	assert.Equal(t, 817.0, must(MustCompile(`1 + 2 * 3 ** 4 * 5 + 6`).Static().FloatValue()))
    96  	assert.Equal(t, 4.5, must(MustCompile(`72 / 2 ** 4`).Static().FloatValue()))
    97  	assert.InDelta(t, 3.6, must(MustCompile(`3 * 5.2 % 4`).Static().FloatValue()), 0.00001)
    98  
    99  	assert.Equal(t, true, must(MustCompile(`!0 && 500`).Static().BoolValue()))
   100  	assert.Equal(t, false, must(MustCompile(`!5 && 500`).Static().BoolValue()))
   101  
   102  	assert.Equal(t, "(A+B*(C+D)/E*F)+G<>H**I*J**K", MustCompile(`A + B * (C + D) / E * F + G <> H ** I * J ** K`).String())
   103  }
   104  
   105  func TestBuildTemplate(t *testing.T) {
   106  	assert.Equal(t, "abc", MustCompile(`"abc"`).Template())
   107  	assert.Equal(t, "abcdef", MustCompile(`"abc" + "def"`).Template())
   108  	assert.Equal(t, "abc9", MustCompile(`"abc" + 9`).Template())
   109  	assert.Equal(t, "abc{{env.xyz}}", MustCompile(`"abc" + env.xyz`).Template())
   110  	assert.Equal(t, "{{env.xyz}}abc", MustCompile(`env.xyz + "abc"`).Template())
   111  	assert.Equal(t, "{{env.xyz+env.abc}}abc", MustCompile(`env.xyz + env.abc + "abc"`).Template())
   112  	assert.Equal(t, "{{env.xyz+env.abc}}abc", MustCompile(`env.xyz + env.abc + "abc"`).Template())
   113  	assert.Equal(t, "{{3+env.xyz+env.abc}}", MustCompile(`3 + env.xyz + env.abc`).Template())
   114  	assert.Equal(t, "3{{env.xyz}}{{env.abc}}", MustCompile(`string(3) + env.xyz + env.abc`).Template())
   115  	assert.Equal(t, "3{{env.xyz+env.abc}}", MustCompile(`string(3) + (env.xyz + env.abc)`).Template())
   116  	assert.Equal(t, "3{{env.xyz}}{{env.abc}}", MustCompile(`"3" + env.xyz + env.abc`).Template())
   117  	assert.Equal(t, "3{{env.xyz+env.abc}}", MustCompile(`"3" + (env.xyz + env.abc)`).Template())
   118  }
   119  
   120  func TestCompileTemplate(t *testing.T) {
   121  	assert.Equal(t, `""`, MustCompileTemplate(``).String())
   122  	assert.Equal(t, `"abc"`, MustCompileTemplate(`abc`).String())
   123  	assert.Equal(t, `"abcxyz5"`, MustCompileTemplate(`abc{{ "xyz" }}{{ 5 }}`).String())
   124  	assert.Equal(t, `"abc50"`, MustCompileTemplate(`abc{{ 5 + 45 }}`).String())
   125  	assert.Equal(t, `"abc50def"`, MustCompileTemplate(`abc{{ 5 + 45 }}def`).String())
   126  	assert.Equal(t, `"abc50def"+string(env.abc*5)+"20"`, MustCompileTemplate(`abc{{ 5 + 45 }}def{{env.abc * 5}}20`).String())
   127  
   128  	assert.Equal(t, `abc50def`, must(MustCompileTemplate(`abc{{ 5 + 45 }}def`).Static().StringValue()))
   129  }
   130  
   131  func TestCompilePartialResolution(t *testing.T) {
   132  	vm := NewMachine().
   133  		Register("someint", 555).
   134  		Register("somestring", "foo").
   135  		RegisterAccessor(func(name string) (interface{}, bool) {
   136  			if strings.HasPrefix(name, "env.") {
   137  				return "[placeholder:" + name[4:] + "]", true
   138  			}
   139  			return nil, false
   140  		}).
   141  		RegisterAccessor(func(name string) (interface{}, bool) {
   142  			if strings.HasPrefix(name, "secrets.") {
   143  				return MustCompile("secret(" + name[8:] + ")"), true
   144  			}
   145  			return nil, false
   146  		}).
   147  		RegisterFunction("mainEndpoint", func(values ...StaticValue) (interface{}, bool, error) {
   148  			if len(values) != 0 {
   149  				return nil, true, errors.New("the mainEndpoint should have no parameters")
   150  			}
   151  			return MustCompile(`env.apiUrl`), true, nil
   152  		})
   153  
   154  	assert.Equal(t, `555`, must(MustCompile(`someint`).Resolve(vm)).String())
   155  	assert.Equal(t, `"[placeholder:name]"`, must(MustCompile(`env.name`).Resolve(vm)).String())
   156  	assert.Equal(t, `secret(name)`, must(MustCompile(`secrets.name`).Resolve(vm)).String())
   157  	assert.Equal(t, `"[placeholder:apiUrl]"`, must(MustCompile(`mainEndpoint()`).Resolve(vm)).String())
   158  }
   159  
   160  func TestCompileResolution(t *testing.T) {
   161  	vm := NewMachine().
   162  		Register("someint", 555).
   163  		Register("somestring", "foo").
   164  		RegisterAccessor(func(name string) (interface{}, bool) {
   165  			if strings.HasPrefix(name, "env.") {
   166  				return "[placeholder:" + name[4:] + "]", true
   167  			}
   168  			return nil, false
   169  		}).
   170  		RegisterAccessor(func(name string) (interface{}, bool) {
   171  			if strings.HasPrefix(name, "secrets.") {
   172  				return MustCompile("secret(" + name[8:] + ")"), true
   173  			}
   174  			return nil, false
   175  		}).
   176  		RegisterFunction("mainEndpoint", func(values ...StaticValue) (interface{}, bool, error) {
   177  			if len(values) != 0 {
   178  				return nil, true, errors.New("the mainEndpoint should have no parameters")
   179  			}
   180  			return MustCompile(`env.apiUrl`), true, nil
   181  		})
   182  
   183  	assert.Equal(t, `555`, must(MustCompile(`someint`).Resolve(vm, FinalizerFail)).String())
   184  	assert.Equal(t, `"[placeholder:name]"`, must(MustCompile(`env.name`).Resolve(vm, FinalizerFail)).String())
   185  	assert.Error(t, errOnly(MustCompile(`secrets.name`).Resolve(vm, FinalizerFail)))
   186  	assert.Equal(t, `"[placeholder:apiUrl]"`, must(MustCompile(`mainEndpoint()`).Resolve(vm, FinalizerFail)).String())
   187  }
   188  
   189  func TestCircularResolution(t *testing.T) {
   190  	vm := NewMachine().
   191  		RegisterFunction("one", func(values ...StaticValue) (interface{}, bool, error) {
   192  			return MustCompile("two()"), true, nil
   193  		}).
   194  		RegisterFunction("two", func(values ...StaticValue) (interface{}, bool, error) {
   195  			return MustCompile("one()"), true, nil
   196  		}).
   197  		RegisterFunction("self", func(values ...StaticValue) (interface{}, bool, error) {
   198  			return MustCompile("self()"), true, nil
   199  		})
   200  
   201  	assert.Contains(t, fmt.Sprintf("%v", errOnly(MustCompile(`one()`).Resolve(vm, FinalizerFail))), "call stack exceeded")
   202  	assert.Contains(t, fmt.Sprintf("%v", errOnly(MustCompile(`self()`).Resolve(vm, FinalizerFail))), "call stack exceeded")
   203  }
   204  
   205  func TestMinusNumber(t *testing.T) {
   206  	assert.Equal(t, -4.0, must(MustCompile("-4").Static().FloatValue()))
   207  }
   208  
   209  func TestCompileMultilineString(t *testing.T) {
   210  	assert.Equal(t, `"\nabc\ndef\n"`, MustCompile(`"
   211  abc
   212  def
   213  "`).String())
   214  }
   215  
   216  func TestCompileEscapeTemplate(t *testing.T) {
   217  	assert.Equal(t, `foo{{"{{"}}barbaz{{"{{"}}`, MustCompileTemplate(`foo{{"{{bar"}}baz{{"{{"}}`).Template())
   218  }
   219  
   220  func TestCompileStandardLib(t *testing.T) {
   221  	assert.Equal(t, `false`, MustCompile(`bool(0)`).String())
   222  	assert.Equal(t, `true`, MustCompile(`bool(500)`).String())
   223  	assert.Equal(t, `"500"`, MustCompile(`string(500)`).String())
   224  	assert.Equal(t, `500`, MustCompile(`int(500)`).String())
   225  	assert.Equal(t, `500`, MustCompile(`int(500.888)`).String())
   226  	assert.Equal(t, `500`, MustCompile(`int("500")`).String())
   227  	assert.Equal(t, `500.44`, MustCompile(`float("500.44")`).String())
   228  	assert.Equal(t, `500`, MustCompile(`json("500")`).String())
   229  	assert.Equal(t, `{"a":500}`, MustCompile(`json("{\"a\": 500}")`).String())
   230  	assert.Equal(t, `"{\"a\":500}"`, MustCompile(`tojson({"a": 500})`).String())
   231  	assert.Equal(t, `"500.8"`, MustCompile(`tojson(500.8)`).String())
   232  	assert.Equal(t, `"\"500.8\""`, MustCompile(`tojson("500.8")`).String())
   233  	assert.Equal(t, `"abc"`, MustCompile(`shellquote("abc")`).String())
   234  	assert.Equal(t, `"'a b c'"`, MustCompile(`shellquote("a b c")`).String())
   235  	assert.Equal(t, `"'a b c' 'd e f'"`, MustCompile(`shellquote("a b c", "d e f")`).String())
   236  	assert.Equal(t, `"''"`, MustCompile(`shellquote(null)`).String())
   237  	assert.Equal(t, `["a","b","c","a b c"]`, MustCompile(`shellparse("a b c 'a b c'")`).String())
   238  	assert.Equal(t, `"abc  d"`, MustCompile(`trim("   abc  d  \n  ")`).String())
   239  	assert.Equal(t, `"abc"`, MustCompile(`yaml("\"abc\"")`).String())
   240  	assert.Equal(t, `{"foo":{"bar":"baz"}}`, MustCompile(`yaml("foo:\n  bar: 'baz'")`).String())
   241  	assert.Equal(t, `"foo:\n    bar: baz\n"`, MustCompile(`toyaml({"foo":{"bar":"baz"}})`).String())
   242  	assert.Equal(t, `{"a":["b","v"]}`, MustCompile(`yaml("
   243  a:
   244  - b
   245  - v
   246  ")`).String())
   247  	assert.Equal(t, `["a",10,["a",4]]`, MustCompile(`list("a", 10, ["a", 4])`).String())
   248  	assert.Equal(t, `"a,10,a,4"`, MustCompile(`join(["a",10,["a",4]])`).String())
   249  	assert.Equal(t, `"a---10---a,4"`, MustCompile(`join(["a",10,["a",4]], "---")`).String())
   250  	assert.Equal(t, `[""]`, MustCompile(`split(null)`).String())
   251  	assert.Equal(t, `["a","b","c"]`, MustCompile(`split("a,b,c")`).String())
   252  	assert.Equal(t, `["a","b","c"]`, MustCompile(`split("a---b---c", "---")`).String())
   253  	assert.Equal(t, `5`, MustCompile(`len("abcde")`).String())
   254  	assert.Equal(t, `2`, MustCompile(`len(["a", "b"])`).String())
   255  	assert.Equal(t, `2`, MustCompile(`len({"a": "b", "b": "c"})`).String())
   256  	assert.Equal(t, `2`, MustCompile(`floor(2.6)`).String())
   257  	assert.Equal(t, `2`, MustCompile(`ceil(1.6)`).String())
   258  	assert.Equal(t, `2`, MustCompile(`round(1.6)`).String())
   259  	assert.Equal(t, `2`, MustCompile(`round(1.5)`).String())
   260  	assert.Equal(t, `1`, MustCompile(`round(1.4)`).String())
   261  	assert.Equal(t, `[[1,2],[3,4],[5]]`, MustCompile(`chunk([1,2,3,4,5], 2)`).String())
   262  	assert.Equal(t, `[2,4,6,8,10]`, MustCompile(`map([1,2,3,4,5], "_.value * 2")`).String())
   263  	assert.Equal(t, `[0,2,4,6,8]`, MustCompile(`map([10,20,30,40,50], "_.index * 2")`).String())
   264  	assert.Equal(t, `[2,4,6,8,10]`, MustCompile(`map([1,2,3,4,5], "_.value * 2")`).String())
   265  	assert.Equal(t, `[0,2,4,6,8]`, MustCompile(`map([10,20,30,40,50], "_.index * 2")`).String())
   266  	assert.Equal(t, `[3,4,5]`, MustCompile(`filter([1,2,3,4,5], "_.value > 2")`).String())
   267  	assert.Equal(t, `[5]`, MustCompile(`jq([1,2,3,4,5], ". | max")`).String())
   268  	assert.Equal(t, `[{"b":{"v":2}}]`, MustCompile(`jq([{"a":{"v": 1}},{"b":{"v": 2}}], ". | max_by(.v)")`).String())
   269  	assert.Equal(t, `[[3,4,5]]`, MustCompile(`jq([1,2,3,4,5], "map(select(. > 2))")`).String())
   270  	assert.Equal(t, `5`, MustCompile(`at([1,2,3,4,5], 4)`).String())
   271  	assert.Equal(t, `"value"`, MustCompile(`at({"x": "value"}, "x")`).String())
   272  	assert.Equal(t, `null`, MustCompile(`at({"x": "value"}, "unknown-key")`).String())
   273  	assert.Equal(t, `"abc"`, MustCompile(`eval("\"abc\"")`).String())
   274  	assert.Equal(t, `50`, MustCompile(`eval("5 * 10")`).String())
   275  	assert.Equal(t, `50*something`, MustCompile(`eval("5 * 10 * something")`).String())
   276  }
   277  
   278  func TestCompileWildcard_Unknown(t *testing.T) {
   279  	assert.Equal(t, `map(a.b.c,"_.value.d.e")`, MustCompile("a.b.c.*.d.e").String())
   280  	assert.Equal(t, `map(map(a.b.c,"_.value"),"_.value.d.e")`, MustCompile("a.b.c.*.*.d.e").String())
   281  }
   282  
   283  func TestCompileSpread(t *testing.T) {
   284  	assert.Equal(t, `"a b c 'a b c'"`, MustCompile(`shellquote(["a", "b", "c", "a b c"]...)`).String())
   285  	assert.Equal(t, `"a b c 'a b c'"`, MustCompile("shellquote(shellparse(\"a b c\n'a b c'\")...)").String())
   286  	assert.Equal(t, `"axb"`, MustCompile(`join([["a", "b"], "x"]...)`).String())
   287  }
   288  
   289  func TestCompileWildcard_Map(t *testing.T) {
   290  	vm := NewMachine().Register("a.b.c", []map[string]interface{}{
   291  		{"d": map[string]string{"e": "v1"}},
   292  		{"d": map[string]string{"e": "v2"}},
   293  	})
   294  	assert.Equal(t, `["v1","v2"]`, must(MustCompile("a.b.c.*.d.e").Resolve(vm)).String())
   295  }
   296  
   297  func TestCompileWildcard_Struct(t *testing.T) {
   298  	type S1 struct {
   299  		Else string `json:"e"`
   300  	}
   301  	type S2 struct {
   302  		Something S1 `json:"d"`
   303  	}
   304  	vm := NewMachine().Register("a.b.c", []S2{
   305  		{Something: S1{Else: "v1"}},
   306  		{Something: S1{Else: "v2"}},
   307  	})
   308  	assert.Equal(t, `["v1","v2"]`, must(MustCompile("a.b.c.*.d.e").Resolve(vm)).String())
   309  }
   310  
   311  func TestCompileWildcard_Inner(t *testing.T) {
   312  	type S1 struct {
   313  		Else string `json:"e"`
   314  	}
   315  	type S2 struct {
   316  		Something S1 `json:"d"`
   317  	}
   318  	vm := NewMachine().Register("a.b", map[string]interface{}{
   319  		"c": []S2{
   320  			{Something: S1{Else: "v1"}},
   321  			{Something: S1{Else: "v2"}},
   322  		},
   323  	})
   324  	assert.Equal(t, `["v1","v2"]`, must(MustCompile("a.b.c.*.d.e").Resolve(vm)).String())
   325  }
   326  
   327  func TestCompileInnerPath(t *testing.T) {
   328  	assert.Equal(t, `"v1"`, MustCompile(`["v1", "v2"].0`).String())
   329  	assert.Equal(t, `"v1abc"`, must(MustCompile(`map(["v1", "v2"], "_.value + \"abc\"").0`).Resolve()).String())
   330  	assert.Equal(t, `"v"`, must(MustCompile(`{"k": "v", "k2":"v2"}.k`).Resolve()).String())
   331  	assert.Equal(t, `"v"`, must(MustCompile(`{"k": {"a": "v"}, "k2":"v2"}.k.a`).Resolve()).String())
   332  }
   333  
   334  func TestCompileDetectAccessors(t *testing.T) {
   335  	assert.Equal(t, map[string]struct{}{"something": {}}, MustCompile(`something`).Accessors())
   336  	assert.Equal(t, map[string]struct{}{"something": {}, "other": {}, "another": {}}, MustCompile(`calling(something, 5 * (other + 3), !another)`).Accessors())
   337  }
   338  
   339  func TestCompileDetectFunctions(t *testing.T) {
   340  	assert.Equal(t, map[string]struct{}(nil), MustCompile(`something`).Functions())
   341  	assert.Equal(t, map[string]struct{}{"calling": {}, "something": {}, "string": {}, "a": {}}, MustCompile(`calling(something(), 45 + 2 + 10 + string(abc * a(c)))`).Functions())
   342  }
   343  
   344  func TestCompileImmutableNone(t *testing.T) {
   345  	assert.Same(t, None, NewValue(noneValue))
   346  	assert.Same(t, NewValue(noneValue), NewValue(noneValue))
   347  }