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