github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/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 TestInterpolateFuncDirname(t *testing.T) {
   856  	testFunction(t, testFunctionConfig{
   857  		Cases: []testFunctionCase{
   858  			{
   859  				`${dirname("/foo/bar/baz")}`,
   860  				"/foo/bar",
   861  				false,
   862  			},
   863  		},
   864  	})
   865  }
   866  
   867  func TestInterpolateFuncDistinct(t *testing.T) {
   868  	testFunction(t, testFunctionConfig{
   869  		Cases: []testFunctionCase{
   870  			// 3 duplicates
   871  			{
   872  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`,
   873  				[]interface{}{"user1", "user2", "user3"},
   874  				false,
   875  			},
   876  			// 1 duplicate
   877  			{
   878  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`,
   879  				[]interface{}{"user1", "user2", "user3", "user4"},
   880  				false,
   881  			},
   882  			// too many args
   883  			{
   884  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`,
   885  				nil,
   886  				true,
   887  			},
   888  			// non-flat list is an error
   889  			{
   890  				`${distinct(list(list("a"), list("a")))}`,
   891  				nil,
   892  				true,
   893  			},
   894  		},
   895  	})
   896  }
   897  
   898  func TestInterpolateFuncFile(t *testing.T) {
   899  	tf, err := ioutil.TempFile("", "tf")
   900  	if err != nil {
   901  		t.Fatalf("err: %s", err)
   902  	}
   903  	path := tf.Name()
   904  	tf.Write([]byte("foo"))
   905  	tf.Close()
   906  	defer os.Remove(path)
   907  
   908  	testFunction(t, testFunctionConfig{
   909  		Cases: []testFunctionCase{
   910  			{
   911  				fmt.Sprintf(`${file("%s")}`, path),
   912  				"foo",
   913  				false,
   914  			},
   915  
   916  			// Invalid path
   917  			{
   918  				`${file("/i/dont/exist")}`,
   919  				nil,
   920  				true,
   921  			},
   922  
   923  			// Too many args
   924  			{
   925  				`${file("foo", "bar")}`,
   926  				nil,
   927  				true,
   928  			},
   929  		},
   930  	})
   931  }
   932  
   933  func TestInterpolateFuncFormat(t *testing.T) {
   934  	testFunction(t, testFunctionConfig{
   935  		Cases: []testFunctionCase{
   936  			{
   937  				`${format("hello")}`,
   938  				"hello",
   939  				false,
   940  			},
   941  
   942  			{
   943  				`${format("hello %s", "world")}`,
   944  				"hello world",
   945  				false,
   946  			},
   947  
   948  			{
   949  				`${format("hello %d", 42)}`,
   950  				"hello 42",
   951  				false,
   952  			},
   953  
   954  			{
   955  				`${format("hello %05d", 42)}`,
   956  				"hello 00042",
   957  				false,
   958  			},
   959  
   960  			{
   961  				`${format("hello %05d", 12345)}`,
   962  				"hello 12345",
   963  				false,
   964  			},
   965  		},
   966  	})
   967  }
   968  
   969  func TestInterpolateFuncFormatList(t *testing.T) {
   970  	testFunction(t, testFunctionConfig{
   971  		Cases: []testFunctionCase{
   972  			// formatlist requires at least one list
   973  			{
   974  				`${formatlist("hello")}`,
   975  				nil,
   976  				true,
   977  			},
   978  			{
   979  				`${formatlist("hello %s", "world")}`,
   980  				nil,
   981  				true,
   982  			},
   983  			// formatlist applies to each list element in turn
   984  			{
   985  				`${formatlist("<%s>", split(",", "A,B"))}`,
   986  				[]interface{}{"<A>", "<B>"},
   987  				false,
   988  			},
   989  			// formatlist repeats scalar elements
   990  			{
   991  				`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
   992  				"x=A, x=B, x=C",
   993  				false,
   994  			},
   995  			// Multiple lists are walked in parallel
   996  			{
   997  				`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
   998  				"A=1, B=2, C=3",
   999  				false,
  1000  			},
  1001  			// Mismatched list lengths generate an error
  1002  			{
  1003  				`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
  1004  				nil,
  1005  				true,
  1006  			},
  1007  			// Works with lists of length 1 [GH-2240]
  1008  			{
  1009  				`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
  1010  				[]interface{}{"demo-rest-elb.id"},
  1011  				false,
  1012  			},
  1013  			// Works with empty lists [GH-7607]
  1014  			{
  1015  				`${formatlist("%s", var.emptylist)}`,
  1016  				[]interface{}{},
  1017  				false,
  1018  			},
  1019  		},
  1020  		Vars: map[string]ast.Variable{
  1021  			"var.emptylist": {
  1022  				Type:  ast.TypeList,
  1023  				Value: []ast.Variable{},
  1024  			},
  1025  		},
  1026  	})
  1027  }
  1028  
  1029  func TestInterpolateFuncIndex(t *testing.T) {
  1030  	testFunction(t, testFunctionConfig{
  1031  		Vars: map[string]ast.Variable{
  1032  			"var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
  1033  			"var.list2": interfaceToVariableSwallowError([]string{"foo"}),
  1034  			"var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}),
  1035  		},
  1036  		Cases: []testFunctionCase{
  1037  			{
  1038  				`${index("test", "")}`,
  1039  				nil,
  1040  				true,
  1041  			},
  1042  
  1043  			{
  1044  				`${index(var.list1, "foo")}`,
  1045  				nil,
  1046  				true,
  1047  			},
  1048  
  1049  			{
  1050  				`${index(var.list2, "foo")}`,
  1051  				"0",
  1052  				false,
  1053  			},
  1054  
  1055  			{
  1056  				`${index(var.list3, "bar")}`,
  1057  				"2",
  1058  				false,
  1059  			},
  1060  		},
  1061  	})
  1062  }
  1063  
  1064  func TestInterpolateFuncJoin(t *testing.T) {
  1065  	testFunction(t, testFunctionConfig{
  1066  		Vars: map[string]ast.Variable{
  1067  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo"}),
  1068  			"var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}),
  1069  			"var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}),
  1070  		},
  1071  		Cases: []testFunctionCase{
  1072  			{
  1073  				`${join(",")}`,
  1074  				nil,
  1075  				true,
  1076  			},
  1077  
  1078  			{
  1079  				`${join(",", var.a_list)}`,
  1080  				"foo",
  1081  				false,
  1082  			},
  1083  
  1084  			{
  1085  				`${join(".", var.a_longer_list)}`,
  1086  				"foo.bar.baz",
  1087  				false,
  1088  			},
  1089  
  1090  			{
  1091  				`${join(".", var.list_of_lists)}`,
  1092  				nil,
  1093  				true,
  1094  			},
  1095  			{
  1096  				`${join(".", list(list("nested")))}`,
  1097  				nil,
  1098  				true,
  1099  			},
  1100  		},
  1101  	})
  1102  }
  1103  
  1104  func TestInterpolateFuncJSONEncode(t *testing.T) {
  1105  	testFunction(t, testFunctionConfig{
  1106  		Vars: map[string]ast.Variable{
  1107  			"easy": ast.Variable{
  1108  				Value: "test",
  1109  				Type:  ast.TypeString,
  1110  			},
  1111  			"hard": ast.Variable{
  1112  				Value: " foo \\ \n \t \" bar ",
  1113  				Type:  ast.TypeString,
  1114  			},
  1115  			"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
  1116  			// XXX can't use InterfaceToVariable as it converts empty slice into empty
  1117  			// map.
  1118  			"emptylist": ast.Variable{
  1119  				Value: []ast.Variable{},
  1120  				Type:  ast.TypeList,
  1121  			},
  1122  			"map": interfaceToVariableSwallowError(map[string]string{
  1123  				"foo":     "bar",
  1124  				"ba \n z": "q\\x",
  1125  			}),
  1126  			"emptymap": interfaceToVariableSwallowError(map[string]string{}),
  1127  
  1128  			// Not yet supported (but it would be nice)
  1129  			"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
  1130  			"nestedmap":  interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
  1131  		},
  1132  		Cases: []testFunctionCase{
  1133  			{
  1134  				`${jsonencode("test")}`,
  1135  				`"test"`,
  1136  				false,
  1137  			},
  1138  			{
  1139  				`${jsonencode(easy)}`,
  1140  				`"test"`,
  1141  				false,
  1142  			},
  1143  			{
  1144  				`${jsonencode(hard)}`,
  1145  				`" foo \\ \n \t \" bar "`,
  1146  				false,
  1147  			},
  1148  			{
  1149  				`${jsonencode("")}`,
  1150  				`""`,
  1151  				false,
  1152  			},
  1153  			{
  1154  				`${jsonencode()}`,
  1155  				nil,
  1156  				true,
  1157  			},
  1158  			{
  1159  				`${jsonencode(list)}`,
  1160  				`["foo","bar\tbaz"]`,
  1161  				false,
  1162  			},
  1163  			{
  1164  				`${jsonencode(emptylist)}`,
  1165  				`[]`,
  1166  				false,
  1167  			},
  1168  			{
  1169  				`${jsonencode(map)}`,
  1170  				`{"ba \n z":"q\\x","foo":"bar"}`,
  1171  				false,
  1172  			},
  1173  			{
  1174  				`${jsonencode(emptymap)}`,
  1175  				`{}`,
  1176  				false,
  1177  			},
  1178  			{
  1179  				`${jsonencode(nestedlist)}`,
  1180  				nil,
  1181  				true,
  1182  			},
  1183  			{
  1184  				`${jsonencode(nestedmap)}`,
  1185  				nil,
  1186  				true,
  1187  			},
  1188  		},
  1189  	})
  1190  }
  1191  
  1192  func TestInterpolateFuncReplace(t *testing.T) {
  1193  	testFunction(t, testFunctionConfig{
  1194  		Cases: []testFunctionCase{
  1195  			// Regular search and replace
  1196  			{
  1197  				`${replace("hello", "hel", "bel")}`,
  1198  				"bello",
  1199  				false,
  1200  			},
  1201  
  1202  			// Search string doesn't match
  1203  			{
  1204  				`${replace("hello", "nope", "bel")}`,
  1205  				"hello",
  1206  				false,
  1207  			},
  1208  
  1209  			// Regular expression
  1210  			{
  1211  				`${replace("hello", "/l/", "L")}`,
  1212  				"heLLo",
  1213  				false,
  1214  			},
  1215  
  1216  			{
  1217  				`${replace("helo", "/(l)/", "$1$1")}`,
  1218  				"hello",
  1219  				false,
  1220  			},
  1221  
  1222  			// Bad regexp
  1223  			{
  1224  				`${replace("helo", "/(l/", "$1$1")}`,
  1225  				nil,
  1226  				true,
  1227  			},
  1228  		},
  1229  	})
  1230  }
  1231  
  1232  func TestInterpolateFuncLength(t *testing.T) {
  1233  	testFunction(t, testFunctionConfig{
  1234  		Cases: []testFunctionCase{
  1235  			// Raw strings
  1236  			{
  1237  				`${length("")}`,
  1238  				"0",
  1239  				false,
  1240  			},
  1241  			{
  1242  				`${length("a")}`,
  1243  				"1",
  1244  				false,
  1245  			},
  1246  			{
  1247  				`${length(" ")}`,
  1248  				"1",
  1249  				false,
  1250  			},
  1251  			{
  1252  				`${length(" a ,")}`,
  1253  				"4",
  1254  				false,
  1255  			},
  1256  			{
  1257  				`${length("aaa")}`,
  1258  				"3",
  1259  				false,
  1260  			},
  1261  
  1262  			// Lists
  1263  			{
  1264  				`${length(split(",", "a"))}`,
  1265  				"1",
  1266  				false,
  1267  			},
  1268  			{
  1269  				`${length(split(",", "foo,"))}`,
  1270  				"2",
  1271  				false,
  1272  			},
  1273  			{
  1274  				`${length(split(",", ",foo,"))}`,
  1275  				"3",
  1276  				false,
  1277  			},
  1278  			{
  1279  				`${length(split(",", "foo,bar"))}`,
  1280  				"2",
  1281  				false,
  1282  			},
  1283  			{
  1284  				`${length(split(".", "one.two.three.four.five"))}`,
  1285  				"5",
  1286  				false,
  1287  			},
  1288  			// Want length 0 if we split an empty string then compact
  1289  			{
  1290  				`${length(compact(split(",", "")))}`,
  1291  				"0",
  1292  				false,
  1293  			},
  1294  			// Works for maps
  1295  			{
  1296  				`${length(map("k", "v"))}`,
  1297  				"1",
  1298  				false,
  1299  			},
  1300  			{
  1301  				`${length(map("k1", "v1", "k2", "v2"))}`,
  1302  				"2",
  1303  				false,
  1304  			},
  1305  		},
  1306  	})
  1307  }
  1308  
  1309  func TestInterpolateFuncSignum(t *testing.T) {
  1310  	testFunction(t, testFunctionConfig{
  1311  		Cases: []testFunctionCase{
  1312  			{
  1313  				`${signum()}`,
  1314  				nil,
  1315  				true,
  1316  			},
  1317  
  1318  			{
  1319  				`${signum("")}`,
  1320  				nil,
  1321  				true,
  1322  			},
  1323  
  1324  			{
  1325  				`${signum(0)}`,
  1326  				"0",
  1327  				false,
  1328  			},
  1329  
  1330  			{
  1331  				`${signum(15)}`,
  1332  				"1",
  1333  				false,
  1334  			},
  1335  
  1336  			{
  1337  				`${signum(-29)}`,
  1338  				"-1",
  1339  				false,
  1340  			},
  1341  		},
  1342  	})
  1343  }
  1344  
  1345  func TestInterpolateFuncSlice(t *testing.T) {
  1346  	testFunction(t, testFunctionConfig{
  1347  		Cases: []testFunctionCase{
  1348  			// Negative from index
  1349  			{
  1350  				`${slice(list("a"), -1, 0)}`,
  1351  				nil,
  1352  				true,
  1353  			},
  1354  			// From index > to index
  1355  			{
  1356  				`${slice(list("a", "b", "c"), 2, 1)}`,
  1357  				nil,
  1358  				true,
  1359  			},
  1360  			// To index too large
  1361  			{
  1362  				`${slice(var.list_of_strings, 1, 4)}`,
  1363  				nil,
  1364  				true,
  1365  			},
  1366  			// Empty slice
  1367  			{
  1368  				`${slice(var.list_of_strings, 1, 1)}`,
  1369  				[]interface{}{},
  1370  				false,
  1371  			},
  1372  			{
  1373  				`${slice(var.list_of_strings, 1, 2)}`,
  1374  				[]interface{}{"b"},
  1375  				false,
  1376  			},
  1377  			{
  1378  				`${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`,
  1379  				[]interface{}{"a", "b"},
  1380  				false,
  1381  			},
  1382  		},
  1383  		Vars: map[string]ast.Variable{
  1384  			"var.list_of_strings": {
  1385  				Type: ast.TypeList,
  1386  				Value: []ast.Variable{
  1387  					{
  1388  						Type:  ast.TypeString,
  1389  						Value: "a",
  1390  					},
  1391  					{
  1392  						Type:  ast.TypeString,
  1393  						Value: "b",
  1394  					},
  1395  					{
  1396  						Type:  ast.TypeString,
  1397  						Value: "c",
  1398  					},
  1399  				},
  1400  			},
  1401  		},
  1402  	})
  1403  }
  1404  
  1405  func TestInterpolateFuncSort(t *testing.T) {
  1406  	testFunction(t, testFunctionConfig{
  1407  		Vars: map[string]ast.Variable{
  1408  			"var.strings": ast.Variable{
  1409  				Type: ast.TypeList,
  1410  				Value: []ast.Variable{
  1411  					{Type: ast.TypeString, Value: "c"},
  1412  					{Type: ast.TypeString, Value: "a"},
  1413  					{Type: ast.TypeString, Value: "b"},
  1414  				},
  1415  			},
  1416  			"var.notstrings": ast.Variable{
  1417  				Type: ast.TypeList,
  1418  				Value: []ast.Variable{
  1419  					{Type: ast.TypeList, Value: []ast.Variable{}},
  1420  					{Type: ast.TypeString, Value: "b"},
  1421  				},
  1422  			},
  1423  		},
  1424  		Cases: []testFunctionCase{
  1425  			{
  1426  				`${sort(var.strings)}`,
  1427  				[]interface{}{"a", "b", "c"},
  1428  				false,
  1429  			},
  1430  			{
  1431  				`${sort(var.notstrings)}`,
  1432  				nil,
  1433  				true,
  1434  			},
  1435  		},
  1436  	})
  1437  }
  1438  
  1439  func TestInterpolateFuncSplit(t *testing.T) {
  1440  	testFunction(t, testFunctionConfig{
  1441  		Cases: []testFunctionCase{
  1442  			{
  1443  				`${split(",")}`,
  1444  				nil,
  1445  				true,
  1446  			},
  1447  
  1448  			{
  1449  				`${split(",", "")}`,
  1450  				[]interface{}{""},
  1451  				false,
  1452  			},
  1453  
  1454  			{
  1455  				`${split(",", "foo")}`,
  1456  				[]interface{}{"foo"},
  1457  				false,
  1458  			},
  1459  
  1460  			{
  1461  				`${split(",", ",,,")}`,
  1462  				[]interface{}{"", "", "", ""},
  1463  				false,
  1464  			},
  1465  
  1466  			{
  1467  				`${split(",", "foo,")}`,
  1468  				[]interface{}{"foo", ""},
  1469  				false,
  1470  			},
  1471  
  1472  			{
  1473  				`${split(",", ",foo,")}`,
  1474  				[]interface{}{"", "foo", ""},
  1475  				false,
  1476  			},
  1477  
  1478  			{
  1479  				`${split(".", "foo.bar.baz")}`,
  1480  				[]interface{}{"foo", "bar", "baz"},
  1481  				false,
  1482  			},
  1483  		},
  1484  	})
  1485  }
  1486  
  1487  func TestInterpolateFuncLookup(t *testing.T) {
  1488  	testFunction(t, testFunctionConfig{
  1489  		Vars: map[string]ast.Variable{
  1490  			"var.foo": {
  1491  				Type: ast.TypeMap,
  1492  				Value: map[string]ast.Variable{
  1493  					"bar": {
  1494  						Type:  ast.TypeString,
  1495  						Value: "baz",
  1496  					},
  1497  				},
  1498  			},
  1499  			"var.map_of_lists": ast.Variable{
  1500  				Type: ast.TypeMap,
  1501  				Value: map[string]ast.Variable{
  1502  					"bar": {
  1503  						Type: ast.TypeList,
  1504  						Value: []ast.Variable{
  1505  							{
  1506  								Type:  ast.TypeString,
  1507  								Value: "baz",
  1508  							},
  1509  						},
  1510  					},
  1511  				},
  1512  			},
  1513  		},
  1514  		Cases: []testFunctionCase{
  1515  			{
  1516  				`${lookup(var.foo, "bar")}`,
  1517  				"baz",
  1518  				false,
  1519  			},
  1520  
  1521  			// Invalid key
  1522  			{
  1523  				`${lookup(var.foo, "baz")}`,
  1524  				nil,
  1525  				true,
  1526  			},
  1527  
  1528  			// Supplied default with valid key
  1529  			{
  1530  				`${lookup(var.foo, "bar", "")}`,
  1531  				"baz",
  1532  				false,
  1533  			},
  1534  
  1535  			// Supplied default with invalid key
  1536  			{
  1537  				`${lookup(var.foo, "zip", "")}`,
  1538  				"",
  1539  				false,
  1540  			},
  1541  
  1542  			// Too many args
  1543  			{
  1544  				`${lookup(var.foo, "bar", "", "abc")}`,
  1545  				nil,
  1546  				true,
  1547  			},
  1548  
  1549  			// Cannot lookup into map of lists
  1550  			{
  1551  				`${lookup(var.map_of_lists, "bar")}`,
  1552  				nil,
  1553  				true,
  1554  			},
  1555  
  1556  			// Non-empty default
  1557  			{
  1558  				`${lookup(var.foo, "zap", "xyz")}`,
  1559  				"xyz",
  1560  				false,
  1561  			},
  1562  		},
  1563  	})
  1564  }
  1565  
  1566  func TestInterpolateFuncKeys(t *testing.T) {
  1567  	testFunction(t, testFunctionConfig{
  1568  		Vars: map[string]ast.Variable{
  1569  			"var.foo": ast.Variable{
  1570  				Type: ast.TypeMap,
  1571  				Value: map[string]ast.Variable{
  1572  					"bar": ast.Variable{
  1573  						Value: "baz",
  1574  						Type:  ast.TypeString,
  1575  					},
  1576  					"qux": ast.Variable{
  1577  						Value: "quack",
  1578  						Type:  ast.TypeString,
  1579  					},
  1580  				},
  1581  			},
  1582  			"var.str": ast.Variable{
  1583  				Value: "astring",
  1584  				Type:  ast.TypeString,
  1585  			},
  1586  		},
  1587  		Cases: []testFunctionCase{
  1588  			{
  1589  				`${keys(var.foo)}`,
  1590  				[]interface{}{"bar", "qux"},
  1591  				false,
  1592  			},
  1593  
  1594  			// Invalid key
  1595  			{
  1596  				`${keys(var.not)}`,
  1597  				nil,
  1598  				true,
  1599  			},
  1600  
  1601  			// Too many args
  1602  			{
  1603  				`${keys(var.foo, "bar")}`,
  1604  				nil,
  1605  				true,
  1606  			},
  1607  
  1608  			// Not a map
  1609  			{
  1610  				`${keys(var.str)}`,
  1611  				nil,
  1612  				true,
  1613  			},
  1614  		},
  1615  	})
  1616  }
  1617  
  1618  // Confirm that keys return in sorted order, and values return in the order of
  1619  // their sorted keys.
  1620  func TestInterpolateFuncKeyValOrder(t *testing.T) {
  1621  	testFunction(t, testFunctionConfig{
  1622  		Vars: map[string]ast.Variable{
  1623  			"var.foo": ast.Variable{
  1624  				Type: ast.TypeMap,
  1625  				Value: map[string]ast.Variable{
  1626  					"D": ast.Variable{
  1627  						Value: "2",
  1628  						Type:  ast.TypeString,
  1629  					},
  1630  					"C": ast.Variable{
  1631  						Value: "Y",
  1632  						Type:  ast.TypeString,
  1633  					},
  1634  					"A": ast.Variable{
  1635  						Value: "X",
  1636  						Type:  ast.TypeString,
  1637  					},
  1638  					"10": ast.Variable{
  1639  						Value: "Z",
  1640  						Type:  ast.TypeString,
  1641  					},
  1642  					"1": ast.Variable{
  1643  						Value: "4",
  1644  						Type:  ast.TypeString,
  1645  					},
  1646  					"3": ast.Variable{
  1647  						Value: "W",
  1648  						Type:  ast.TypeString,
  1649  					},
  1650  				},
  1651  			},
  1652  		},
  1653  		Cases: []testFunctionCase{
  1654  			{
  1655  				`${keys(var.foo)}`,
  1656  				[]interface{}{"1", "10", "3", "A", "C", "D"},
  1657  				false,
  1658  			},
  1659  
  1660  			{
  1661  				`${values(var.foo)}`,
  1662  				[]interface{}{"4", "Z", "W", "X", "Y", "2"},
  1663  				false,
  1664  			},
  1665  		},
  1666  	})
  1667  }
  1668  
  1669  func TestInterpolateFuncValues(t *testing.T) {
  1670  	testFunction(t, testFunctionConfig{
  1671  		Vars: map[string]ast.Variable{
  1672  			"var.foo": ast.Variable{
  1673  				Type: ast.TypeMap,
  1674  				Value: map[string]ast.Variable{
  1675  					"bar": ast.Variable{
  1676  						Value: "quack",
  1677  						Type:  ast.TypeString,
  1678  					},
  1679  					"qux": ast.Variable{
  1680  						Value: "baz",
  1681  						Type:  ast.TypeString,
  1682  					},
  1683  				},
  1684  			},
  1685  			"var.str": ast.Variable{
  1686  				Value: "astring",
  1687  				Type:  ast.TypeString,
  1688  			},
  1689  		},
  1690  		Cases: []testFunctionCase{
  1691  			{
  1692  				`${values(var.foo)}`,
  1693  				[]interface{}{"quack", "baz"},
  1694  				false,
  1695  			},
  1696  
  1697  			// Invalid key
  1698  			{
  1699  				`${values(var.not)}`,
  1700  				nil,
  1701  				true,
  1702  			},
  1703  
  1704  			// Too many args
  1705  			{
  1706  				`${values(var.foo, "bar")}`,
  1707  				nil,
  1708  				true,
  1709  			},
  1710  
  1711  			// Not a map
  1712  			{
  1713  				`${values(var.str)}`,
  1714  				nil,
  1715  				true,
  1716  			},
  1717  
  1718  			// Map of lists
  1719  			{
  1720  				`${values(map("one", list()))}`,
  1721  				nil,
  1722  				true,
  1723  			},
  1724  		},
  1725  	})
  1726  }
  1727  
  1728  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
  1729  	variable, _ := hil.InterfaceToVariable(input)
  1730  	return variable
  1731  }
  1732  
  1733  func TestInterpolateFuncElement(t *testing.T) {
  1734  	testFunction(t, testFunctionConfig{
  1735  		Vars: map[string]ast.Variable{
  1736  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo", "baz"}),
  1737  			"var.a_short_list":  interfaceToVariableSwallowError([]string{"foo"}),
  1738  			"var.empty_list":    interfaceToVariableSwallowError([]interface{}{}),
  1739  			"var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}),
  1740  		},
  1741  		Cases: []testFunctionCase{
  1742  			{
  1743  				`${element(var.a_list, "1")}`,
  1744  				"baz",
  1745  				false,
  1746  			},
  1747  
  1748  			{
  1749  				`${element(var.a_short_list, "0")}`,
  1750  				"foo",
  1751  				false,
  1752  			},
  1753  
  1754  			// Invalid index should wrap vs. out-of-bounds
  1755  			{
  1756  				`${element(var.a_list, "2")}`,
  1757  				"foo",
  1758  				false,
  1759  			},
  1760  
  1761  			// Negative number should fail
  1762  			{
  1763  				`${element(var.a_short_list, "-1")}`,
  1764  				nil,
  1765  				true,
  1766  			},
  1767  
  1768  			// Empty list should fail
  1769  			{
  1770  				`${element(var.empty_list, 0)}`,
  1771  				nil,
  1772  				true,
  1773  			},
  1774  
  1775  			// Too many args
  1776  			{
  1777  				`${element(var.a_list, "0", "2")}`,
  1778  				nil,
  1779  				true,
  1780  			},
  1781  
  1782  			// Only works on single-level lists
  1783  			{
  1784  				`${element(var.a_nested_list, "0")}`,
  1785  				nil,
  1786  				true,
  1787  			},
  1788  		},
  1789  	})
  1790  }
  1791  
  1792  func TestInterpolateFuncBasename(t *testing.T) {
  1793  	testFunction(t, testFunctionConfig{
  1794  		Cases: []testFunctionCase{
  1795  			{
  1796  				`${basename("/foo/bar/baz")}`,
  1797  				"baz",
  1798  				false,
  1799  			},
  1800  		},
  1801  	})
  1802  }
  1803  
  1804  func TestInterpolateFuncBase64Encode(t *testing.T) {
  1805  	testFunction(t, testFunctionConfig{
  1806  		Cases: []testFunctionCase{
  1807  			// Regular base64 encoding
  1808  			{
  1809  				`${base64encode("abc123!?$*&()'-=@~")}`,
  1810  				"YWJjMTIzIT8kKiYoKSctPUB+",
  1811  				false,
  1812  			},
  1813  		},
  1814  	})
  1815  }
  1816  
  1817  func TestInterpolateFuncBase64Decode(t *testing.T) {
  1818  	testFunction(t, testFunctionConfig{
  1819  		Cases: []testFunctionCase{
  1820  			// Regular base64 decoding
  1821  			{
  1822  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
  1823  				"abc123!?$*&()'-=@~",
  1824  				false,
  1825  			},
  1826  
  1827  			// Invalid base64 data decoding
  1828  			{
  1829  				`${base64decode("this-is-an-invalid-base64-data")}`,
  1830  				nil,
  1831  				true,
  1832  			},
  1833  		},
  1834  	})
  1835  }
  1836  
  1837  func TestInterpolateFuncLower(t *testing.T) {
  1838  	testFunction(t, testFunctionConfig{
  1839  		Cases: []testFunctionCase{
  1840  			{
  1841  				`${lower("HELLO")}`,
  1842  				"hello",
  1843  				false,
  1844  			},
  1845  
  1846  			{
  1847  				`${lower("")}`,
  1848  				"",
  1849  				false,
  1850  			},
  1851  
  1852  			{
  1853  				`${lower()}`,
  1854  				nil,
  1855  				true,
  1856  			},
  1857  		},
  1858  	})
  1859  }
  1860  
  1861  func TestInterpolateFuncUpper(t *testing.T) {
  1862  	testFunction(t, testFunctionConfig{
  1863  		Cases: []testFunctionCase{
  1864  			{
  1865  				`${upper("hello")}`,
  1866  				"HELLO",
  1867  				false,
  1868  			},
  1869  
  1870  			{
  1871  				`${upper("")}`,
  1872  				"",
  1873  				false,
  1874  			},
  1875  
  1876  			{
  1877  				`${upper()}`,
  1878  				nil,
  1879  				true,
  1880  			},
  1881  		},
  1882  	})
  1883  }
  1884  
  1885  func TestInterpolateFuncSha1(t *testing.T) {
  1886  	testFunction(t, testFunctionConfig{
  1887  		Cases: []testFunctionCase{
  1888  			{
  1889  				`${sha1("test")}`,
  1890  				"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
  1891  				false,
  1892  			},
  1893  		},
  1894  	})
  1895  }
  1896  
  1897  func TestInterpolateFuncSha256(t *testing.T) {
  1898  	testFunction(t, testFunctionConfig{
  1899  		Cases: []testFunctionCase{
  1900  			{ // hexadecimal representation of sha256 sum
  1901  				`${sha256("test")}`,
  1902  				"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  1903  				false,
  1904  			},
  1905  		},
  1906  	})
  1907  }
  1908  
  1909  func TestInterpolateFuncTitle(t *testing.T) {
  1910  	testFunction(t, testFunctionConfig{
  1911  		Cases: []testFunctionCase{
  1912  			{
  1913  				`${title("hello")}`,
  1914  				"Hello",
  1915  				false,
  1916  			},
  1917  
  1918  			{
  1919  				`${title("hello world")}`,
  1920  				"Hello World",
  1921  				false,
  1922  			},
  1923  
  1924  			{
  1925  				`${title("")}`,
  1926  				"",
  1927  				false,
  1928  			},
  1929  
  1930  			{
  1931  				`${title()}`,
  1932  				nil,
  1933  				true,
  1934  			},
  1935  		},
  1936  	})
  1937  }
  1938  
  1939  func TestInterpolateFuncTrimSpace(t *testing.T) {
  1940  	testFunction(t, testFunctionConfig{
  1941  		Cases: []testFunctionCase{
  1942  			{
  1943  				`${trimspace(" test ")}`,
  1944  				"test",
  1945  				false,
  1946  			},
  1947  		},
  1948  	})
  1949  }
  1950  
  1951  func TestInterpolateFuncBase64Sha256(t *testing.T) {
  1952  	testFunction(t, testFunctionConfig{
  1953  		Cases: []testFunctionCase{
  1954  			{
  1955  				`${base64sha256("test")}`,
  1956  				"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
  1957  				false,
  1958  			},
  1959  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  1960  				`${base64encode(sha256("test"))}`,
  1961  				"OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==",
  1962  				false,
  1963  			},
  1964  		},
  1965  	})
  1966  }
  1967  
  1968  func TestInterpolateFuncMd5(t *testing.T) {
  1969  	testFunction(t, testFunctionConfig{
  1970  		Cases: []testFunctionCase{
  1971  			{
  1972  				`${md5("tada")}`,
  1973  				"ce47d07243bb6eaf5e1322c81baf9bbf",
  1974  				false,
  1975  			},
  1976  			{ // Confirm that we're not trimming any whitespaces
  1977  				`${md5(" tada ")}`,
  1978  				"aadf191a583e53062de2d02c008141c4",
  1979  				false,
  1980  			},
  1981  			{ // We accept empty string too
  1982  				`${md5("")}`,
  1983  				"d41d8cd98f00b204e9800998ecf8427e",
  1984  				false,
  1985  			},
  1986  		},
  1987  	})
  1988  }
  1989  
  1990  func TestInterpolateFuncUUID(t *testing.T) {
  1991  	results := make(map[string]bool)
  1992  
  1993  	for i := 0; i < 100; i++ {
  1994  		ast, err := hil.Parse("${uuid()}")
  1995  		if err != nil {
  1996  			t.Fatalf("err: %s", err)
  1997  		}
  1998  
  1999  		result, err := hil.Eval(ast, langEvalConfig(nil))
  2000  		if err != nil {
  2001  			t.Fatalf("err: %s", err)
  2002  		}
  2003  
  2004  		if results[result.Value.(string)] {
  2005  			t.Fatalf("Got unexpected duplicate uuid: %s", result.Value)
  2006  		}
  2007  
  2008  		results[result.Value.(string)] = true
  2009  	}
  2010  }
  2011  
  2012  func TestInterpolateFuncTimestamp(t *testing.T) {
  2013  	currentTime := time.Now().UTC()
  2014  	ast, err := hil.Parse("${timestamp()}")
  2015  	if err != nil {
  2016  		t.Fatalf("err: %s", err)
  2017  	}
  2018  
  2019  	result, err := hil.Eval(ast, langEvalConfig(nil))
  2020  	if err != nil {
  2021  		t.Fatalf("err: %s", err)
  2022  	}
  2023  	resultTime, err := time.Parse(time.RFC3339, result.Value.(string))
  2024  	if err != nil {
  2025  		t.Fatalf("Error parsing timestamp: %s", err)
  2026  	}
  2027  
  2028  	if resultTime.Sub(currentTime).Seconds() > 10.0 {
  2029  		t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string))
  2030  	}
  2031  }
  2032  
  2033  type testFunctionConfig struct {
  2034  	Cases []testFunctionCase
  2035  	Vars  map[string]ast.Variable
  2036  }
  2037  
  2038  type testFunctionCase struct {
  2039  	Input  string
  2040  	Result interface{}
  2041  	Error  bool
  2042  }
  2043  
  2044  func testFunction(t *testing.T, config testFunctionConfig) {
  2045  	for i, tc := range config.Cases {
  2046  		ast, err := hil.Parse(tc.Input)
  2047  		if err != nil {
  2048  			t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err)
  2049  		}
  2050  
  2051  		result, err := hil.Eval(ast, langEvalConfig(config.Vars))
  2052  		if err != nil != tc.Error {
  2053  			t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err)
  2054  		}
  2055  
  2056  		if !reflect.DeepEqual(result.Value, tc.Result) {
  2057  			t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v",
  2058  				i, tc.Input, result.Value, tc.Result)
  2059  		}
  2060  	}
  2061  }
  2062  
  2063  func TestInterpolateFuncPathExpand(t *testing.T) {
  2064  	homePath, err := homedir.Dir()
  2065  	if err != nil {
  2066  		t.Fatalf("Error getting home directory: %v", err)
  2067  	}
  2068  	testFunction(t, testFunctionConfig{
  2069  		Cases: []testFunctionCase{
  2070  			{
  2071  				`${pathexpand("~/test-file")}`,
  2072  				filepath.Join(homePath, "test-file"),
  2073  				false,
  2074  			},
  2075  			{
  2076  				`${pathexpand("~/another/test/file")}`,
  2077  				filepath.Join(homePath, "another/test/file"),
  2078  				false,
  2079  			},
  2080  			{
  2081  				`${pathexpand("/root/file")}`,
  2082  				"/root/file",
  2083  				false,
  2084  			},
  2085  			{
  2086  				`${pathexpand("/")}`,
  2087  				"/",
  2088  				false,
  2089  			},
  2090  			{
  2091  				`${pathexpand()}`,
  2092  				nil,
  2093  				true,
  2094  			},
  2095  		},
  2096  	})
  2097  }
  2098  
  2099  func TestInterpolateFuncSubstr(t *testing.T) {
  2100  	testFunction(t, testFunctionConfig{
  2101  		Cases: []testFunctionCase{
  2102  			{
  2103  				`${substr("foobar", 0, 0)}`,
  2104  				"",
  2105  				false,
  2106  			},
  2107  			{
  2108  				`${substr("foobar", 0, -1)}`,
  2109  				"foobar",
  2110  				false,
  2111  			},
  2112  			{
  2113  				`${substr("foobar", 0, 3)}`,
  2114  				"foo",
  2115  				false,
  2116  			},
  2117  			{
  2118  				`${substr("foobar", 3, 3)}`,
  2119  				"bar",
  2120  				false,
  2121  			},
  2122  			{
  2123  				`${substr("foobar", -3, 3)}`,
  2124  				"bar",
  2125  				false,
  2126  			},
  2127  
  2128  			// empty string
  2129  			{
  2130  				`${substr("", 0, 0)}`,
  2131  				"",
  2132  				false,
  2133  			},
  2134  
  2135  			// invalid offset
  2136  			{
  2137  				`${substr("", 1, 0)}`,
  2138  				nil,
  2139  				true,
  2140  			},
  2141  
  2142  			// invalid length
  2143  			{
  2144  				`${substr("", 0, 1)}`,
  2145  				nil,
  2146  				true,
  2147  			},
  2148  			{
  2149  				`${substr("", 0, -2)}`,
  2150  				nil,
  2151  				true,
  2152  			},
  2153  		},
  2154  	})
  2155  }