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