github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/config/interpolate_funcs_test.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/hil"
    11  	"github.com/hashicorp/hil/ast"
    12  )
    13  
    14  func TestInterpolateFuncList(t *testing.T) {
    15  	testFunction(t, testFunctionConfig{
    16  		Cases: []testFunctionCase{
    17  			// empty input returns empty list
    18  			{
    19  				`${list()}`,
    20  				[]interface{}{},
    21  				false,
    22  			},
    23  
    24  			// single input returns list of length 1
    25  			{
    26  				`${list("hello")}`,
    27  				[]interface{}{"hello"},
    28  				false,
    29  			},
    30  
    31  			// two inputs returns list of length 2
    32  			{
    33  				`${list("hello", "world")}`,
    34  				[]interface{}{"hello", "world"},
    35  				false,
    36  			},
    37  
    38  			// not a string input gives error
    39  			{
    40  				`${list("hello", 42)}`,
    41  				nil,
    42  				true,
    43  			},
    44  
    45  			// list of lists
    46  			{
    47  				`${list("${var.list}", "${var.list2}")}`,
    48  				[]interface{}{[]interface{}{"Hello", "World"}, []interface{}{"bar", "baz"}},
    49  				false,
    50  			},
    51  
    52  			// list of maps
    53  			{
    54  				`${list("${var.map}", "${var.map2}")}`,
    55  				[]interface{}{map[string]interface{}{"key": "bar"}, map[string]interface{}{"key2": "baz"}},
    56  				false,
    57  			},
    58  
    59  			// error on a heterogeneous list
    60  			{
    61  				`${list("first", "${var.list}")}`,
    62  				nil,
    63  				true,
    64  			},
    65  		},
    66  		Vars: map[string]ast.Variable{
    67  			"var.list": {
    68  				Type: ast.TypeList,
    69  				Value: []ast.Variable{
    70  					{
    71  						Type:  ast.TypeString,
    72  						Value: "Hello",
    73  					},
    74  					{
    75  						Type:  ast.TypeString,
    76  						Value: "World",
    77  					},
    78  				},
    79  			},
    80  			"var.list2": {
    81  				Type: ast.TypeList,
    82  				Value: []ast.Variable{
    83  					{
    84  						Type:  ast.TypeString,
    85  						Value: "bar",
    86  					},
    87  					{
    88  						Type:  ast.TypeString,
    89  						Value: "baz",
    90  					},
    91  				},
    92  			},
    93  
    94  			"var.map": {
    95  				Type: ast.TypeMap,
    96  				Value: map[string]ast.Variable{
    97  					"key": {
    98  						Type:  ast.TypeString,
    99  						Value: "bar",
   100  					},
   101  				},
   102  			},
   103  			"var.map2": {
   104  				Type: ast.TypeMap,
   105  				Value: map[string]ast.Variable{
   106  					"key2": {
   107  						Type:  ast.TypeString,
   108  						Value: "baz",
   109  					},
   110  				},
   111  			},
   112  		},
   113  	})
   114  }
   115  
   116  func TestInterpolateFuncMap(t *testing.T) {
   117  	testFunction(t, testFunctionConfig{
   118  		Cases: []testFunctionCase{
   119  			// empty input returns empty map
   120  			{
   121  				`${map()}`,
   122  				map[string]interface{}{},
   123  				false,
   124  			},
   125  
   126  			// odd args is error
   127  			{
   128  				`${map("odd")}`,
   129  				nil,
   130  				true,
   131  			},
   132  
   133  			// two args returns map w/ one k/v
   134  			{
   135  				`${map("hello", "world")}`,
   136  				map[string]interface{}{"hello": "world"},
   137  				false,
   138  			},
   139  
   140  			// four args get two k/v
   141  			{
   142  				`${map("hello", "world", "what's", "up?")}`,
   143  				map[string]interface{}{"hello": "world", "what's": "up?"},
   144  				false,
   145  			},
   146  
   147  			// map of lists is okay
   148  			{
   149  				`${map("hello", list("world"), "what's", list("up?"))}`,
   150  				map[string]interface{}{
   151  					"hello":  []interface{}{"world"},
   152  					"what's": []interface{}{"up?"},
   153  				},
   154  				false,
   155  			},
   156  
   157  			// map of maps is okay
   158  			{
   159  				`${map("hello", map("there", "world"), "what's", map("really", "up?"))}`,
   160  				map[string]interface{}{
   161  					"hello":  map[string]interface{}{"there": "world"},
   162  					"what's": map[string]interface{}{"really": "up?"},
   163  				},
   164  				false,
   165  			},
   166  
   167  			// keys have to be strings
   168  			{
   169  				`${map(list("listkey"), "val")}`,
   170  				nil,
   171  				true,
   172  			},
   173  
   174  			// types have to match
   175  			{
   176  				`${map("some", "strings", "also", list("lists"))}`,
   177  				nil,
   178  				true,
   179  			},
   180  
   181  			// duplicate keys are an error
   182  			{
   183  				`${map("key", "val", "key", "again")}`,
   184  				nil,
   185  				true,
   186  			},
   187  		},
   188  	})
   189  }
   190  
   191  func TestInterpolateFuncCompact(t *testing.T) {
   192  	testFunction(t, testFunctionConfig{
   193  		Cases: []testFunctionCase{
   194  			// empty string within array
   195  			{
   196  				`${compact(split(",", "a,,b"))}`,
   197  				[]interface{}{"a", "b"},
   198  				false,
   199  			},
   200  
   201  			// empty string at the end of array
   202  			{
   203  				`${compact(split(",", "a,b,"))}`,
   204  				[]interface{}{"a", "b"},
   205  				false,
   206  			},
   207  
   208  			// single empty string
   209  			{
   210  				`${compact(split(",", ""))}`,
   211  				[]interface{}{},
   212  				false,
   213  			},
   214  
   215  			// errrors on list of lists
   216  			{
   217  				`${compact(list(list("a"), list("b")))}`,
   218  				nil,
   219  				true,
   220  			},
   221  		},
   222  	})
   223  }
   224  
   225  func TestInterpolateFuncCidrHost(t *testing.T) {
   226  	testFunction(t, testFunctionConfig{
   227  		Cases: []testFunctionCase{
   228  			{
   229  				`${cidrhost("192.168.1.0/24", 5)}`,
   230  				"192.168.1.5",
   231  				false,
   232  			},
   233  			{
   234  				`${cidrhost("192.168.1.0/30", 255)}`,
   235  				nil,
   236  				true, // 255 doesn't fit in two bits
   237  			},
   238  			{
   239  				`${cidrhost("not-a-cidr", 6)}`,
   240  				nil,
   241  				true, // not a valid CIDR mask
   242  			},
   243  			{
   244  				`${cidrhost("10.256.0.0/8", 6)}`,
   245  				nil,
   246  				true, // can't have an octet >255
   247  			},
   248  		},
   249  	})
   250  }
   251  
   252  func TestInterpolateFuncCidrNetmask(t *testing.T) {
   253  	testFunction(t, testFunctionConfig{
   254  		Cases: []testFunctionCase{
   255  			{
   256  				`${cidrnetmask("192.168.1.0/24")}`,
   257  				"255.255.255.0",
   258  				false,
   259  			},
   260  			{
   261  				`${cidrnetmask("192.168.1.0/32")}`,
   262  				"255.255.255.255",
   263  				false,
   264  			},
   265  			{
   266  				`${cidrnetmask("0.0.0.0/0")}`,
   267  				"0.0.0.0",
   268  				false,
   269  			},
   270  			{
   271  				// This doesn't really make sense for IPv6 networks
   272  				// but it ought to do something sensible anyway.
   273  				`${cidrnetmask("1::/64")}`,
   274  				"ffff:ffff:ffff:ffff::",
   275  				false,
   276  			},
   277  			{
   278  				`${cidrnetmask("not-a-cidr")}`,
   279  				nil,
   280  				true, // not a valid CIDR mask
   281  			},
   282  			{
   283  				`${cidrnetmask("10.256.0.0/8")}`,
   284  				nil,
   285  				true, // can't have an octet >255
   286  			},
   287  		},
   288  	})
   289  }
   290  
   291  func TestInterpolateFuncCidrSubnet(t *testing.T) {
   292  	testFunction(t, testFunctionConfig{
   293  		Cases: []testFunctionCase{
   294  			{
   295  				`${cidrsubnet("192.168.2.0/20", 4, 6)}`,
   296  				"192.168.6.0/24",
   297  				false,
   298  			},
   299  			{
   300  				`${cidrsubnet("fe80::/48", 16, 6)}`,
   301  				"fe80:0:0:6::/64",
   302  				false,
   303  			},
   304  			{
   305  				// IPv4 address encoded in IPv6 syntax gets normalized
   306  				`${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`,
   307  				"192.168.6.0/24",
   308  				false,
   309  			},
   310  			{
   311  				`${cidrsubnet("192.168.0.0/30", 4, 6)}`,
   312  				nil,
   313  				true, // not enough bits left
   314  			},
   315  			{
   316  				`${cidrsubnet("192.168.0.0/16", 2, 16)}`,
   317  				nil,
   318  				true, // can't encode 16 in 2 bits
   319  			},
   320  			{
   321  				`${cidrsubnet("not-a-cidr", 4, 6)}`,
   322  				nil,
   323  				true, // not a valid CIDR mask
   324  			},
   325  			{
   326  				`${cidrsubnet("10.256.0.0/8", 4, 6)}`,
   327  				nil,
   328  				true, // can't have an octet >255
   329  			},
   330  		},
   331  	})
   332  }
   333  
   334  func TestInterpolateFuncCoalesce(t *testing.T) {
   335  	testFunction(t, testFunctionConfig{
   336  		Cases: []testFunctionCase{
   337  			{
   338  				`${coalesce("first", "second", "third")}`,
   339  				"first",
   340  				false,
   341  			},
   342  			{
   343  				`${coalesce("", "second", "third")}`,
   344  				"second",
   345  				false,
   346  			},
   347  			{
   348  				`${coalesce("", "", "")}`,
   349  				"",
   350  				false,
   351  			},
   352  			{
   353  				`${coalesce("foo")}`,
   354  				nil,
   355  				true,
   356  			},
   357  		},
   358  	})
   359  }
   360  
   361  func TestInterpolateFuncConcat(t *testing.T) {
   362  	testFunction(t, testFunctionConfig{
   363  		Cases: []testFunctionCase{
   364  			// String + list
   365  			// no longer supported, now returns an error
   366  			{
   367  				`${concat("a", split(",", "b,c"))}`,
   368  				nil,
   369  				true,
   370  			},
   371  
   372  			// List + string
   373  			// no longer supported, now returns an error
   374  			{
   375  				`${concat(split(",", "a,b"), "c")}`,
   376  				nil,
   377  				true,
   378  			},
   379  
   380  			// Single list
   381  			{
   382  				`${concat(split(",", ",foo,"))}`,
   383  				[]interface{}{"", "foo", ""},
   384  				false,
   385  			},
   386  			{
   387  				`${concat(split(",", "a,b,c"))}`,
   388  				[]interface{}{"a", "b", "c"},
   389  				false,
   390  			},
   391  
   392  			// Two lists
   393  			{
   394  				`${concat(split(",", "a,b,c"), split(",", "d,e"))}`,
   395  				[]interface{}{"a", "b", "c", "d", "e"},
   396  				false,
   397  			},
   398  			// Two lists with different separators
   399  			{
   400  				`${concat(split(",", "a,b,c"), split(" ", "d e"))}`,
   401  				[]interface{}{"a", "b", "c", "d", "e"},
   402  				false,
   403  			},
   404  
   405  			// More lists
   406  			{
   407  				`${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`,
   408  				[]interface{}{"a", "b", "c", "d", "e", "f", "0", "1"},
   409  				false,
   410  			},
   411  
   412  			// list vars
   413  			{
   414  				`${concat("${var.list}", "${var.list}")}`,
   415  				[]interface{}{"a", "b", "a", "b"},
   416  				false,
   417  			},
   418  			// lists of lists
   419  			{
   420  				`${concat("${var.lists}", "${var.lists}")}`,
   421  				[]interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}},
   422  				false,
   423  			},
   424  
   425  			// lists of maps
   426  			{
   427  				`${concat("${var.maps}", "${var.maps}")}`,
   428  				[]interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}},
   429  				false,
   430  			},
   431  
   432  			// multiple strings
   433  			// no longer supported, now returns an error
   434  			{
   435  				`${concat("string1", "string2")}`,
   436  				nil,
   437  				true,
   438  			},
   439  
   440  			// mismatched types
   441  			{
   442  				`${concat("${var.lists}", "${var.maps}")}`,
   443  				nil,
   444  				true,
   445  			},
   446  		},
   447  		Vars: map[string]ast.Variable{
   448  			"var.list": {
   449  				Type: ast.TypeList,
   450  				Value: []ast.Variable{
   451  					{
   452  						Type:  ast.TypeString,
   453  						Value: "a",
   454  					},
   455  					{
   456  						Type:  ast.TypeString,
   457  						Value: "b",
   458  					},
   459  				},
   460  			},
   461  			"var.lists": {
   462  				Type: ast.TypeList,
   463  				Value: []ast.Variable{
   464  					{
   465  						Type: ast.TypeList,
   466  						Value: []ast.Variable{
   467  							{
   468  								Type:  ast.TypeString,
   469  								Value: "c",
   470  							},
   471  							{
   472  								Type:  ast.TypeString,
   473  								Value: "d",
   474  							},
   475  						},
   476  					},
   477  				},
   478  			},
   479  			"var.maps": {
   480  				Type: ast.TypeList,
   481  				Value: []ast.Variable{
   482  					{
   483  						Type: ast.TypeMap,
   484  						Value: map[string]ast.Variable{
   485  							"key1": {
   486  								Type:  ast.TypeString,
   487  								Value: "a",
   488  							},
   489  							"key2": {
   490  								Type:  ast.TypeString,
   491  								Value: "b",
   492  							},
   493  						},
   494  					},
   495  				},
   496  			},
   497  		},
   498  	})
   499  }
   500  
   501  func TestInterpolateFuncMerge(t *testing.T) {
   502  	testFunction(t, testFunctionConfig{
   503  		Cases: []testFunctionCase{
   504  			// basic merge
   505  			{
   506  				`${merge(map("a", "b"), map("c", "d"))}`,
   507  				map[string]interface{}{"a": "b", "c": "d"},
   508  				false,
   509  			},
   510  
   511  			// merge with conflicts is ok, last in wins.
   512  			{
   513  				`${merge(map("a", "b", "c", "X"), map("c", "d"))}`,
   514  				map[string]interface{}{"a": "b", "c": "d"},
   515  				false,
   516  			},
   517  
   518  			// merge variadic
   519  			{
   520  				`${merge(map("a", "b"), map("c", "d"), map("e", "f"))}`,
   521  				map[string]interface{}{"a": "b", "c": "d", "e": "f"},
   522  				false,
   523  			},
   524  
   525  			// merge with variables
   526  			{
   527  				`${merge(var.maps[0], map("c", "d"))}`,
   528  				map[string]interface{}{"key1": "a", "key2": "b", "c": "d"},
   529  				false,
   530  			},
   531  
   532  			// only accept maps
   533  			{
   534  				`${merge(map("a", "b"), list("c", "d"))}`,
   535  				nil,
   536  				true,
   537  			},
   538  
   539  			// merge maps of maps
   540  			{
   541  				`${merge(map("a", var.maps[0]), map("b", var.maps[1]))}`,
   542  				map[string]interface{}{
   543  					"b": map[string]interface{}{"key3": "d", "key4": "c"},
   544  					"a": map[string]interface{}{"key1": "a", "key2": "b"},
   545  				},
   546  				false,
   547  			},
   548  			// merge maps of lists
   549  			{
   550  				`${merge(map("a", list("b")), map("c", list("d", "e")))}`,
   551  				map[string]interface{}{"a": []interface{}{"b"}, "c": []interface{}{"d", "e"}},
   552  				false,
   553  			},
   554  			// merge map of various kinds
   555  			{
   556  				`${merge(map("a", var.maps[0]), map("b", list("c", "d")))}`,
   557  				map[string]interface{}{"a": map[string]interface{}{"key1": "a", "key2": "b"}, "b": []interface{}{"c", "d"}},
   558  				false,
   559  			},
   560  		},
   561  		Vars: map[string]ast.Variable{
   562  			"var.maps": {
   563  				Type: ast.TypeList,
   564  				Value: []ast.Variable{
   565  					{
   566  						Type: ast.TypeMap,
   567  						Value: map[string]ast.Variable{
   568  							"key1": {
   569  								Type:  ast.TypeString,
   570  								Value: "a",
   571  							},
   572  							"key2": {
   573  								Type:  ast.TypeString,
   574  								Value: "b",
   575  							},
   576  						},
   577  					},
   578  					{
   579  						Type: ast.TypeMap,
   580  						Value: map[string]ast.Variable{
   581  							"key3": {
   582  								Type:  ast.TypeString,
   583  								Value: "d",
   584  							},
   585  							"key4": {
   586  								Type:  ast.TypeString,
   587  								Value: "c",
   588  							},
   589  						},
   590  					},
   591  				},
   592  			},
   593  		},
   594  	})
   595  
   596  }
   597  
   598  func TestInterpolateFuncDistinct(t *testing.T) {
   599  	testFunction(t, testFunctionConfig{
   600  		Cases: []testFunctionCase{
   601  			// 3 duplicates
   602  			{
   603  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`,
   604  				[]interface{}{"user1", "user2", "user3"},
   605  				false,
   606  			},
   607  			// 1 duplicate
   608  			{
   609  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`,
   610  				[]interface{}{"user1", "user2", "user3", "user4"},
   611  				false,
   612  			},
   613  			// too many args
   614  			{
   615  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`,
   616  				nil,
   617  				true,
   618  			},
   619  			// non-flat list is an error
   620  			{
   621  				`${distinct(list(list("a"), list("a")))}`,
   622  				nil,
   623  				true,
   624  			},
   625  		},
   626  	})
   627  }
   628  
   629  func TestInterpolateFuncFile(t *testing.T) {
   630  	tf, err := ioutil.TempFile("", "tf")
   631  	if err != nil {
   632  		t.Fatalf("err: %s", err)
   633  	}
   634  	path := tf.Name()
   635  	tf.Write([]byte("foo"))
   636  	tf.Close()
   637  	defer os.Remove(path)
   638  
   639  	testFunction(t, testFunctionConfig{
   640  		Cases: []testFunctionCase{
   641  			{
   642  				fmt.Sprintf(`${file("%s")}`, path),
   643  				"foo",
   644  				false,
   645  			},
   646  
   647  			// Invalid path
   648  			{
   649  				`${file("/i/dont/exist")}`,
   650  				nil,
   651  				true,
   652  			},
   653  
   654  			// Too many args
   655  			{
   656  				`${file("foo", "bar")}`,
   657  				nil,
   658  				true,
   659  			},
   660  		},
   661  	})
   662  }
   663  
   664  func TestInterpolateFuncFormat(t *testing.T) {
   665  	testFunction(t, testFunctionConfig{
   666  		Cases: []testFunctionCase{
   667  			{
   668  				`${format("hello")}`,
   669  				"hello",
   670  				false,
   671  			},
   672  
   673  			{
   674  				`${format("hello %s", "world")}`,
   675  				"hello world",
   676  				false,
   677  			},
   678  
   679  			{
   680  				`${format("hello %d", 42)}`,
   681  				"hello 42",
   682  				false,
   683  			},
   684  
   685  			{
   686  				`${format("hello %05d", 42)}`,
   687  				"hello 00042",
   688  				false,
   689  			},
   690  
   691  			{
   692  				`${format("hello %05d", 12345)}`,
   693  				"hello 12345",
   694  				false,
   695  			},
   696  		},
   697  	})
   698  }
   699  
   700  func TestInterpolateFuncFormatList(t *testing.T) {
   701  	testFunction(t, testFunctionConfig{
   702  		Cases: []testFunctionCase{
   703  			// formatlist requires at least one list
   704  			{
   705  				`${formatlist("hello")}`,
   706  				nil,
   707  				true,
   708  			},
   709  			{
   710  				`${formatlist("hello %s", "world")}`,
   711  				nil,
   712  				true,
   713  			},
   714  			// formatlist applies to each list element in turn
   715  			{
   716  				`${formatlist("<%s>", split(",", "A,B"))}`,
   717  				[]interface{}{"<A>", "<B>"},
   718  				false,
   719  			},
   720  			// formatlist repeats scalar elements
   721  			{
   722  				`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
   723  				"x=A, x=B, x=C",
   724  				false,
   725  			},
   726  			// Multiple lists are walked in parallel
   727  			{
   728  				`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
   729  				"A=1, B=2, C=3",
   730  				false,
   731  			},
   732  			// Mismatched list lengths generate an error
   733  			{
   734  				`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
   735  				nil,
   736  				true,
   737  			},
   738  			// Works with lists of length 1 [GH-2240]
   739  			{
   740  				`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
   741  				[]interface{}{"demo-rest-elb.id"},
   742  				false,
   743  			},
   744  		},
   745  	})
   746  }
   747  
   748  func TestInterpolateFuncIndex(t *testing.T) {
   749  	testFunction(t, testFunctionConfig{
   750  		Vars: map[string]ast.Variable{
   751  			"var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
   752  			"var.list2": interfaceToVariableSwallowError([]string{"foo"}),
   753  			"var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}),
   754  		},
   755  		Cases: []testFunctionCase{
   756  			{
   757  				`${index("test", "")}`,
   758  				nil,
   759  				true,
   760  			},
   761  
   762  			{
   763  				`${index(var.list1, "foo")}`,
   764  				nil,
   765  				true,
   766  			},
   767  
   768  			{
   769  				`${index(var.list2, "foo")}`,
   770  				"0",
   771  				false,
   772  			},
   773  
   774  			{
   775  				`${index(var.list3, "bar")}`,
   776  				"2",
   777  				false,
   778  			},
   779  		},
   780  	})
   781  }
   782  
   783  func TestInterpolateFuncJoin(t *testing.T) {
   784  	testFunction(t, testFunctionConfig{
   785  		Vars: map[string]ast.Variable{
   786  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo"}),
   787  			"var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}),
   788  			"var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}),
   789  		},
   790  		Cases: []testFunctionCase{
   791  			{
   792  				`${join(",")}`,
   793  				nil,
   794  				true,
   795  			},
   796  
   797  			{
   798  				`${join(",", var.a_list)}`,
   799  				"foo",
   800  				false,
   801  			},
   802  
   803  			{
   804  				`${join(".", var.a_longer_list)}`,
   805  				"foo.bar.baz",
   806  				false,
   807  			},
   808  
   809  			{
   810  				`${join(".", var.list_of_lists)}`,
   811  				nil,
   812  				true,
   813  			},
   814  			{
   815  				`${join(".", list(list("nested")))}`,
   816  				nil,
   817  				true,
   818  			},
   819  		},
   820  	})
   821  }
   822  
   823  func TestInterpolateFuncJSONEncode(t *testing.T) {
   824  	testFunction(t, testFunctionConfig{
   825  		Vars: map[string]ast.Variable{
   826  			"easy": ast.Variable{
   827  				Value: "test",
   828  				Type:  ast.TypeString,
   829  			},
   830  			"hard": ast.Variable{
   831  				Value: " foo \\ \n \t \" bar ",
   832  				Type:  ast.TypeString,
   833  			},
   834  			"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
   835  			// XXX can't use InterfaceToVariable as it converts empty slice into empty
   836  			// map.
   837  			"emptylist": ast.Variable{
   838  				Value: []ast.Variable{},
   839  				Type:  ast.TypeList,
   840  			},
   841  			"map": interfaceToVariableSwallowError(map[string]string{
   842  				"foo":     "bar",
   843  				"ba \n z": "q\\x",
   844  			}),
   845  			"emptymap": interfaceToVariableSwallowError(map[string]string{}),
   846  
   847  			// Not yet supported (but it would be nice)
   848  			"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
   849  			"nestedmap":  interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
   850  		},
   851  		Cases: []testFunctionCase{
   852  			{
   853  				`${jsonencode("test")}`,
   854  				`"test"`,
   855  				false,
   856  			},
   857  			{
   858  				`${jsonencode(easy)}`,
   859  				`"test"`,
   860  				false,
   861  			},
   862  			{
   863  				`${jsonencode(hard)}`,
   864  				`" foo \\ \n \t \" bar "`,
   865  				false,
   866  			},
   867  			{
   868  				`${jsonencode("")}`,
   869  				`""`,
   870  				false,
   871  			},
   872  			{
   873  				`${jsonencode()}`,
   874  				nil,
   875  				true,
   876  			},
   877  			{
   878  				`${jsonencode(list)}`,
   879  				`["foo","bar\tbaz"]`,
   880  				false,
   881  			},
   882  			{
   883  				`${jsonencode(emptylist)}`,
   884  				`[]`,
   885  				false,
   886  			},
   887  			{
   888  				`${jsonencode(map)}`,
   889  				`{"ba \n z":"q\\x","foo":"bar"}`,
   890  				false,
   891  			},
   892  			{
   893  				`${jsonencode(emptymap)}`,
   894  				`{}`,
   895  				false,
   896  			},
   897  			{
   898  				`${jsonencode(nestedlist)}`,
   899  				nil,
   900  				true,
   901  			},
   902  			{
   903  				`${jsonencode(nestedmap)}`,
   904  				nil,
   905  				true,
   906  			},
   907  		},
   908  	})
   909  }
   910  
   911  func TestInterpolateFuncReplace(t *testing.T) {
   912  	testFunction(t, testFunctionConfig{
   913  		Cases: []testFunctionCase{
   914  			// Regular search and replace
   915  			{
   916  				`${replace("hello", "hel", "bel")}`,
   917  				"bello",
   918  				false,
   919  			},
   920  
   921  			// Search string doesn't match
   922  			{
   923  				`${replace("hello", "nope", "bel")}`,
   924  				"hello",
   925  				false,
   926  			},
   927  
   928  			// Regular expression
   929  			{
   930  				`${replace("hello", "/l/", "L")}`,
   931  				"heLLo",
   932  				false,
   933  			},
   934  
   935  			{
   936  				`${replace("helo", "/(l)/", "$1$1")}`,
   937  				"hello",
   938  				false,
   939  			},
   940  
   941  			// Bad regexp
   942  			{
   943  				`${replace("helo", "/(l/", "$1$1")}`,
   944  				nil,
   945  				true,
   946  			},
   947  		},
   948  	})
   949  }
   950  
   951  func TestInterpolateFuncLength(t *testing.T) {
   952  	testFunction(t, testFunctionConfig{
   953  		Cases: []testFunctionCase{
   954  			// Raw strings
   955  			{
   956  				`${length("")}`,
   957  				"0",
   958  				false,
   959  			},
   960  			{
   961  				`${length("a")}`,
   962  				"1",
   963  				false,
   964  			},
   965  			{
   966  				`${length(" ")}`,
   967  				"1",
   968  				false,
   969  			},
   970  			{
   971  				`${length(" a ,")}`,
   972  				"4",
   973  				false,
   974  			},
   975  			{
   976  				`${length("aaa")}`,
   977  				"3",
   978  				false,
   979  			},
   980  
   981  			// Lists
   982  			{
   983  				`${length(split(",", "a"))}`,
   984  				"1",
   985  				false,
   986  			},
   987  			{
   988  				`${length(split(",", "foo,"))}`,
   989  				"2",
   990  				false,
   991  			},
   992  			{
   993  				`${length(split(",", ",foo,"))}`,
   994  				"3",
   995  				false,
   996  			},
   997  			{
   998  				`${length(split(",", "foo,bar"))}`,
   999  				"2",
  1000  				false,
  1001  			},
  1002  			{
  1003  				`${length(split(".", "one.two.three.four.five"))}`,
  1004  				"5",
  1005  				false,
  1006  			},
  1007  			// Want length 0 if we split an empty string then compact
  1008  			{
  1009  				`${length(compact(split(",", "")))}`,
  1010  				"0",
  1011  				false,
  1012  			},
  1013  			// Works for maps
  1014  			{
  1015  				`${length(map("k", "v"))}`,
  1016  				"1",
  1017  				false,
  1018  			},
  1019  			{
  1020  				`${length(map("k1", "v1", "k2", "v2"))}`,
  1021  				"2",
  1022  				false,
  1023  			},
  1024  		},
  1025  	})
  1026  }
  1027  
  1028  func TestInterpolateFuncSignum(t *testing.T) {
  1029  	testFunction(t, testFunctionConfig{
  1030  		Cases: []testFunctionCase{
  1031  			{
  1032  				`${signum()}`,
  1033  				nil,
  1034  				true,
  1035  			},
  1036  
  1037  			{
  1038  				`${signum("")}`,
  1039  				nil,
  1040  				true,
  1041  			},
  1042  
  1043  			{
  1044  				`${signum(0)}`,
  1045  				"0",
  1046  				false,
  1047  			},
  1048  
  1049  			{
  1050  				`${signum(15)}`,
  1051  				"1",
  1052  				false,
  1053  			},
  1054  
  1055  			{
  1056  				`${signum(-29)}`,
  1057  				"-1",
  1058  				false,
  1059  			},
  1060  		},
  1061  	})
  1062  }
  1063  
  1064  func TestInterpolateFuncSort(t *testing.T) {
  1065  	testFunction(t, testFunctionConfig{
  1066  		Vars: map[string]ast.Variable{
  1067  			"var.strings": ast.Variable{
  1068  				Type: ast.TypeList,
  1069  				Value: []ast.Variable{
  1070  					{Type: ast.TypeString, Value: "c"},
  1071  					{Type: ast.TypeString, Value: "a"},
  1072  					{Type: ast.TypeString, Value: "b"},
  1073  				},
  1074  			},
  1075  			"var.notstrings": ast.Variable{
  1076  				Type: ast.TypeList,
  1077  				Value: []ast.Variable{
  1078  					{Type: ast.TypeList, Value: []ast.Variable{}},
  1079  					{Type: ast.TypeString, Value: "b"},
  1080  				},
  1081  			},
  1082  		},
  1083  		Cases: []testFunctionCase{
  1084  			{
  1085  				`${sort(var.strings)}`,
  1086  				[]interface{}{"a", "b", "c"},
  1087  				false,
  1088  			},
  1089  			{
  1090  				`${sort(var.notstrings)}`,
  1091  				nil,
  1092  				true,
  1093  			},
  1094  		},
  1095  	})
  1096  }
  1097  
  1098  func TestInterpolateFuncSplit(t *testing.T) {
  1099  	testFunction(t, testFunctionConfig{
  1100  		Cases: []testFunctionCase{
  1101  			{
  1102  				`${split(",")}`,
  1103  				nil,
  1104  				true,
  1105  			},
  1106  
  1107  			{
  1108  				`${split(",", "")}`,
  1109  				[]interface{}{""},
  1110  				false,
  1111  			},
  1112  
  1113  			{
  1114  				`${split(",", "foo")}`,
  1115  				[]interface{}{"foo"},
  1116  				false,
  1117  			},
  1118  
  1119  			{
  1120  				`${split(",", ",,,")}`,
  1121  				[]interface{}{"", "", "", ""},
  1122  				false,
  1123  			},
  1124  
  1125  			{
  1126  				`${split(",", "foo,")}`,
  1127  				[]interface{}{"foo", ""},
  1128  				false,
  1129  			},
  1130  
  1131  			{
  1132  				`${split(",", ",foo,")}`,
  1133  				[]interface{}{"", "foo", ""},
  1134  				false,
  1135  			},
  1136  
  1137  			{
  1138  				`${split(".", "foo.bar.baz")}`,
  1139  				[]interface{}{"foo", "bar", "baz"},
  1140  				false,
  1141  			},
  1142  		},
  1143  	})
  1144  }
  1145  
  1146  func TestInterpolateFuncLookup(t *testing.T) {
  1147  	testFunction(t, testFunctionConfig{
  1148  		Vars: map[string]ast.Variable{
  1149  			"var.foo": {
  1150  				Type: ast.TypeMap,
  1151  				Value: map[string]ast.Variable{
  1152  					"bar": {
  1153  						Type:  ast.TypeString,
  1154  						Value: "baz",
  1155  					},
  1156  				},
  1157  			},
  1158  			"var.map_of_lists": ast.Variable{
  1159  				Type: ast.TypeMap,
  1160  				Value: map[string]ast.Variable{
  1161  					"bar": {
  1162  						Type: ast.TypeList,
  1163  						Value: []ast.Variable{
  1164  							{
  1165  								Type:  ast.TypeString,
  1166  								Value: "baz",
  1167  							},
  1168  						},
  1169  					},
  1170  				},
  1171  			},
  1172  		},
  1173  		Cases: []testFunctionCase{
  1174  			{
  1175  				`${lookup(var.foo, "bar")}`,
  1176  				"baz",
  1177  				false,
  1178  			},
  1179  
  1180  			// Invalid key
  1181  			{
  1182  				`${lookup(var.foo, "baz")}`,
  1183  				nil,
  1184  				true,
  1185  			},
  1186  
  1187  			// Supplied default with valid key
  1188  			{
  1189  				`${lookup(var.foo, "bar", "")}`,
  1190  				"baz",
  1191  				false,
  1192  			},
  1193  
  1194  			// Supplied default with invalid key
  1195  			{
  1196  				`${lookup(var.foo, "zip", "")}`,
  1197  				"",
  1198  				false,
  1199  			},
  1200  
  1201  			// Too many args
  1202  			{
  1203  				`${lookup(var.foo, "bar", "", "abc")}`,
  1204  				nil,
  1205  				true,
  1206  			},
  1207  
  1208  			// Cannot lookup into map of lists
  1209  			{
  1210  				`${lookup(var.map_of_lists, "bar")}`,
  1211  				nil,
  1212  				true,
  1213  			},
  1214  
  1215  			// Non-empty default
  1216  			{
  1217  				`${lookup(var.foo, "zap", "xyz")}`,
  1218  				"xyz",
  1219  				false,
  1220  			},
  1221  		},
  1222  	})
  1223  }
  1224  
  1225  func TestInterpolateFuncKeys(t *testing.T) {
  1226  	testFunction(t, testFunctionConfig{
  1227  		Vars: map[string]ast.Variable{
  1228  			"var.foo": ast.Variable{
  1229  				Type: ast.TypeMap,
  1230  				Value: map[string]ast.Variable{
  1231  					"bar": ast.Variable{
  1232  						Value: "baz",
  1233  						Type:  ast.TypeString,
  1234  					},
  1235  					"qux": ast.Variable{
  1236  						Value: "quack",
  1237  						Type:  ast.TypeString,
  1238  					},
  1239  				},
  1240  			},
  1241  			"var.str": ast.Variable{
  1242  				Value: "astring",
  1243  				Type:  ast.TypeString,
  1244  			},
  1245  		},
  1246  		Cases: []testFunctionCase{
  1247  			{
  1248  				`${keys(var.foo)}`,
  1249  				[]interface{}{"bar", "qux"},
  1250  				false,
  1251  			},
  1252  
  1253  			// Invalid key
  1254  			{
  1255  				`${keys(var.not)}`,
  1256  				nil,
  1257  				true,
  1258  			},
  1259  
  1260  			// Too many args
  1261  			{
  1262  				`${keys(var.foo, "bar")}`,
  1263  				nil,
  1264  				true,
  1265  			},
  1266  
  1267  			// Not a map
  1268  			{
  1269  				`${keys(var.str)}`,
  1270  				nil,
  1271  				true,
  1272  			},
  1273  		},
  1274  	})
  1275  }
  1276  
  1277  // Confirm that keys return in sorted order, and values return in the order of
  1278  // their sorted keys.
  1279  func TestInterpolateFuncKeyValOrder(t *testing.T) {
  1280  	testFunction(t, testFunctionConfig{
  1281  		Vars: map[string]ast.Variable{
  1282  			"var.foo": ast.Variable{
  1283  				Type: ast.TypeMap,
  1284  				Value: map[string]ast.Variable{
  1285  					"D": ast.Variable{
  1286  						Value: "2",
  1287  						Type:  ast.TypeString,
  1288  					},
  1289  					"C": ast.Variable{
  1290  						Value: "Y",
  1291  						Type:  ast.TypeString,
  1292  					},
  1293  					"A": ast.Variable{
  1294  						Value: "X",
  1295  						Type:  ast.TypeString,
  1296  					},
  1297  					"10": ast.Variable{
  1298  						Value: "Z",
  1299  						Type:  ast.TypeString,
  1300  					},
  1301  					"1": ast.Variable{
  1302  						Value: "4",
  1303  						Type:  ast.TypeString,
  1304  					},
  1305  					"3": ast.Variable{
  1306  						Value: "W",
  1307  						Type:  ast.TypeString,
  1308  					},
  1309  				},
  1310  			},
  1311  		},
  1312  		Cases: []testFunctionCase{
  1313  			{
  1314  				`${keys(var.foo)}`,
  1315  				[]interface{}{"1", "10", "3", "A", "C", "D"},
  1316  				false,
  1317  			},
  1318  
  1319  			{
  1320  				`${values(var.foo)}`,
  1321  				[]interface{}{"4", "Z", "W", "X", "Y", "2"},
  1322  				false,
  1323  			},
  1324  		},
  1325  	})
  1326  }
  1327  
  1328  func TestInterpolateFuncValues(t *testing.T) {
  1329  	testFunction(t, testFunctionConfig{
  1330  		Vars: map[string]ast.Variable{
  1331  			"var.foo": ast.Variable{
  1332  				Type: ast.TypeMap,
  1333  				Value: map[string]ast.Variable{
  1334  					"bar": ast.Variable{
  1335  						Value: "quack",
  1336  						Type:  ast.TypeString,
  1337  					},
  1338  					"qux": ast.Variable{
  1339  						Value: "baz",
  1340  						Type:  ast.TypeString,
  1341  					},
  1342  				},
  1343  			},
  1344  			"var.str": ast.Variable{
  1345  				Value: "astring",
  1346  				Type:  ast.TypeString,
  1347  			},
  1348  		},
  1349  		Cases: []testFunctionCase{
  1350  			{
  1351  				`${values(var.foo)}`,
  1352  				[]interface{}{"quack", "baz"},
  1353  				false,
  1354  			},
  1355  
  1356  			// Invalid key
  1357  			{
  1358  				`${values(var.not)}`,
  1359  				nil,
  1360  				true,
  1361  			},
  1362  
  1363  			// Too many args
  1364  			{
  1365  				`${values(var.foo, "bar")}`,
  1366  				nil,
  1367  				true,
  1368  			},
  1369  
  1370  			// Not a map
  1371  			{
  1372  				`${values(var.str)}`,
  1373  				nil,
  1374  				true,
  1375  			},
  1376  
  1377  			// Map of lists
  1378  			{
  1379  				`${values(map("one", list()))}`,
  1380  				nil,
  1381  				true,
  1382  			},
  1383  		},
  1384  	})
  1385  }
  1386  
  1387  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
  1388  	variable, _ := hil.InterfaceToVariable(input)
  1389  	return variable
  1390  }
  1391  
  1392  func TestInterpolateFuncElement(t *testing.T) {
  1393  	testFunction(t, testFunctionConfig{
  1394  		Vars: map[string]ast.Variable{
  1395  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo", "baz"}),
  1396  			"var.a_short_list":  interfaceToVariableSwallowError([]string{"foo"}),
  1397  			"var.empty_list":    interfaceToVariableSwallowError([]interface{}{}),
  1398  			"var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}),
  1399  		},
  1400  		Cases: []testFunctionCase{
  1401  			{
  1402  				`${element(var.a_list, "1")}`,
  1403  				"baz",
  1404  				false,
  1405  			},
  1406  
  1407  			{
  1408  				`${element(var.a_short_list, "0")}`,
  1409  				"foo",
  1410  				false,
  1411  			},
  1412  
  1413  			// Invalid index should wrap vs. out-of-bounds
  1414  			{
  1415  				`${element(var.a_list, "2")}`,
  1416  				"foo",
  1417  				false,
  1418  			},
  1419  
  1420  			// Negative number should fail
  1421  			{
  1422  				`${element(var.a_short_list, "-1")}`,
  1423  				nil,
  1424  				true,
  1425  			},
  1426  
  1427  			// Empty list should fail
  1428  			{
  1429  				`${element(var.empty_list, 0)}`,
  1430  				nil,
  1431  				true,
  1432  			},
  1433  
  1434  			// Too many args
  1435  			{
  1436  				`${element(var.a_list, "0", "2")}`,
  1437  				nil,
  1438  				true,
  1439  			},
  1440  
  1441  			// Only works on single-level lists
  1442  			{
  1443  				`${element(var.a_nested_list, "0")}`,
  1444  				nil,
  1445  				true,
  1446  			},
  1447  		},
  1448  	})
  1449  }
  1450  
  1451  func TestInterpolateFuncBase64Encode(t *testing.T) {
  1452  	testFunction(t, testFunctionConfig{
  1453  		Cases: []testFunctionCase{
  1454  			// Regular base64 encoding
  1455  			{
  1456  				`${base64encode("abc123!?$*&()'-=@~")}`,
  1457  				"YWJjMTIzIT8kKiYoKSctPUB+",
  1458  				false,
  1459  			},
  1460  		},
  1461  	})
  1462  }
  1463  
  1464  func TestInterpolateFuncBase64Decode(t *testing.T) {
  1465  	testFunction(t, testFunctionConfig{
  1466  		Cases: []testFunctionCase{
  1467  			// Regular base64 decoding
  1468  			{
  1469  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
  1470  				"abc123!?$*&()'-=@~",
  1471  				false,
  1472  			},
  1473  
  1474  			// Invalid base64 data decoding
  1475  			{
  1476  				`${base64decode("this-is-an-invalid-base64-data")}`,
  1477  				nil,
  1478  				true,
  1479  			},
  1480  		},
  1481  	})
  1482  }
  1483  
  1484  func TestInterpolateFuncLower(t *testing.T) {
  1485  	testFunction(t, testFunctionConfig{
  1486  		Cases: []testFunctionCase{
  1487  			{
  1488  				`${lower("HELLO")}`,
  1489  				"hello",
  1490  				false,
  1491  			},
  1492  
  1493  			{
  1494  				`${lower("")}`,
  1495  				"",
  1496  				false,
  1497  			},
  1498  
  1499  			{
  1500  				`${lower()}`,
  1501  				nil,
  1502  				true,
  1503  			},
  1504  		},
  1505  	})
  1506  }
  1507  
  1508  func TestInterpolateFuncUpper(t *testing.T) {
  1509  	testFunction(t, testFunctionConfig{
  1510  		Cases: []testFunctionCase{
  1511  			{
  1512  				`${upper("hello")}`,
  1513  				"HELLO",
  1514  				false,
  1515  			},
  1516  
  1517  			{
  1518  				`${upper("")}`,
  1519  				"",
  1520  				false,
  1521  			},
  1522  
  1523  			{
  1524  				`${upper()}`,
  1525  				nil,
  1526  				true,
  1527  			},
  1528  		},
  1529  	})
  1530  }
  1531  
  1532  func TestInterpolateFuncSha1(t *testing.T) {
  1533  	testFunction(t, testFunctionConfig{
  1534  		Cases: []testFunctionCase{
  1535  			{
  1536  				`${sha1("test")}`,
  1537  				"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
  1538  				false,
  1539  			},
  1540  		},
  1541  	})
  1542  }
  1543  
  1544  func TestInterpolateFuncSha256(t *testing.T) {
  1545  	testFunction(t, testFunctionConfig{
  1546  		Cases: []testFunctionCase{
  1547  			{ // hexadecimal representation of sha256 sum
  1548  				`${sha256("test")}`,
  1549  				"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  1550  				false,
  1551  			},
  1552  		},
  1553  	})
  1554  }
  1555  
  1556  func TestInterpolateFuncTrimSpace(t *testing.T) {
  1557  	testFunction(t, testFunctionConfig{
  1558  		Cases: []testFunctionCase{
  1559  			{
  1560  				`${trimspace(" test ")}`,
  1561  				"test",
  1562  				false,
  1563  			},
  1564  		},
  1565  	})
  1566  }
  1567  
  1568  func TestInterpolateFuncBase64Sha256(t *testing.T) {
  1569  	testFunction(t, testFunctionConfig{
  1570  		Cases: []testFunctionCase{
  1571  			{
  1572  				`${base64sha256("test")}`,
  1573  				"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
  1574  				false,
  1575  			},
  1576  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  1577  				`${base64encode(sha256("test"))}`,
  1578  				"OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==",
  1579  				false,
  1580  			},
  1581  		},
  1582  	})
  1583  }
  1584  
  1585  func TestInterpolateFuncMd5(t *testing.T) {
  1586  	testFunction(t, testFunctionConfig{
  1587  		Cases: []testFunctionCase{
  1588  			{
  1589  				`${md5("tada")}`,
  1590  				"ce47d07243bb6eaf5e1322c81baf9bbf",
  1591  				false,
  1592  			},
  1593  			{ // Confirm that we're not trimming any whitespaces
  1594  				`${md5(" tada ")}`,
  1595  				"aadf191a583e53062de2d02c008141c4",
  1596  				false,
  1597  			},
  1598  			{ // We accept empty string too
  1599  				`${md5("")}`,
  1600  				"d41d8cd98f00b204e9800998ecf8427e",
  1601  				false,
  1602  			},
  1603  		},
  1604  	})
  1605  }
  1606  
  1607  func TestInterpolateFuncUUID(t *testing.T) {
  1608  	results := make(map[string]bool)
  1609  
  1610  	for i := 0; i < 100; i++ {
  1611  		ast, err := hil.Parse("${uuid()}")
  1612  		if err != nil {
  1613  			t.Fatalf("err: %s", err)
  1614  		}
  1615  
  1616  		result, err := hil.Eval(ast, langEvalConfig(nil))
  1617  		if err != nil {
  1618  			t.Fatalf("err: %s", err)
  1619  		}
  1620  
  1621  		if results[result.Value.(string)] {
  1622  			t.Fatalf("Got unexpected duplicate uuid: %s", result.Value)
  1623  		}
  1624  
  1625  		results[result.Value.(string)] = true
  1626  	}
  1627  }
  1628  
  1629  type testFunctionConfig struct {
  1630  	Cases []testFunctionCase
  1631  	Vars  map[string]ast.Variable
  1632  }
  1633  
  1634  type testFunctionCase struct {
  1635  	Input  string
  1636  	Result interface{}
  1637  	Error  bool
  1638  }
  1639  
  1640  func testFunction(t *testing.T, config testFunctionConfig) {
  1641  	for i, tc := range config.Cases {
  1642  		ast, err := hil.Parse(tc.Input)
  1643  		if err != nil {
  1644  			t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err)
  1645  		}
  1646  
  1647  		result, err := hil.Eval(ast, langEvalConfig(config.Vars))
  1648  		t.Logf("err: %v", err)
  1649  		if err != nil != tc.Error {
  1650  			t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err)
  1651  		}
  1652  
  1653  		if !reflect.DeepEqual(result.Value, tc.Result) {
  1654  			t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v",
  1655  				i, tc.Input, result.Value, tc.Result)
  1656  		}
  1657  	}
  1658  }