github.com/ojiry/terraform@v0.8.2-0.20161218223921-e50cec712c4a/config/interpolate_funcs_test.go (about)

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