github.com/archgrove/terraform@v0.9.5-0.20170502093151-adb789f0f8d2/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 TestInterpolateFuncChomp(t *testing.T) {
   374  	testFunction(t, testFunctionConfig{
   375  		Cases: []testFunctionCase{
   376  			{
   377  				`${chomp()}`,
   378  				nil,
   379  				true,
   380  			},
   381  
   382  			{
   383  				`${chomp("hello world")}`,
   384  				"hello world",
   385  				false,
   386  			},
   387  
   388  			{
   389  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld"),
   390  				"goodbye\ncruel\nworld",
   391  				false,
   392  			},
   393  
   394  			{
   395  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld"),
   396  				"goodbye\r\nwindows\r\nworld",
   397  				false,
   398  			},
   399  
   400  			{
   401  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n"),
   402  				"goodbye\ncruel\nworld",
   403  				false,
   404  			},
   405  
   406  			{
   407  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n\n\n\n"),
   408  				"goodbye\ncruel\nworld",
   409  				false,
   410  			},
   411  
   412  			{
   413  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n"),
   414  				"goodbye\r\nwindows\r\nworld",
   415  				false,
   416  			},
   417  
   418  			{
   419  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n\r\n\r\n\r\n"),
   420  				"goodbye\r\nwindows\r\nworld",
   421  				false,
   422  			},
   423  		},
   424  	})
   425  }
   426  
   427  func TestInterpolateFuncMap(t *testing.T) {
   428  	testFunction(t, testFunctionConfig{
   429  		Cases: []testFunctionCase{
   430  			// empty input returns empty map
   431  			{
   432  				`${map()}`,
   433  				map[string]interface{}{},
   434  				false,
   435  			},
   436  
   437  			// odd args is error
   438  			{
   439  				`${map("odd")}`,
   440  				nil,
   441  				true,
   442  			},
   443  
   444  			// two args returns map w/ one k/v
   445  			{
   446  				`${map("hello", "world")}`,
   447  				map[string]interface{}{"hello": "world"},
   448  				false,
   449  			},
   450  
   451  			// four args get two k/v
   452  			{
   453  				`${map("hello", "world", "what's", "up?")}`,
   454  				map[string]interface{}{"hello": "world", "what's": "up?"},
   455  				false,
   456  			},
   457  
   458  			// map of lists is okay
   459  			{
   460  				`${map("hello", list("world"), "what's", list("up?"))}`,
   461  				map[string]interface{}{
   462  					"hello":  []interface{}{"world"},
   463  					"what's": []interface{}{"up?"},
   464  				},
   465  				false,
   466  			},
   467  
   468  			// map of maps is okay
   469  			{
   470  				`${map("hello", map("there", "world"), "what's", map("really", "up?"))}`,
   471  				map[string]interface{}{
   472  					"hello":  map[string]interface{}{"there": "world"},
   473  					"what's": map[string]interface{}{"really": "up?"},
   474  				},
   475  				false,
   476  			},
   477  
   478  			// keys have to be strings
   479  			{
   480  				`${map(list("listkey"), "val")}`,
   481  				nil,
   482  				true,
   483  			},
   484  
   485  			// types have to match
   486  			{
   487  				`${map("some", "strings", "also", list("lists"))}`,
   488  				nil,
   489  				true,
   490  			},
   491  
   492  			// duplicate keys are an error
   493  			{
   494  				`${map("key", "val", "key", "again")}`,
   495  				nil,
   496  				true,
   497  			},
   498  		},
   499  	})
   500  }
   501  
   502  func TestInterpolateFuncCompact(t *testing.T) {
   503  	testFunction(t, testFunctionConfig{
   504  		Cases: []testFunctionCase{
   505  			// empty string within array
   506  			{
   507  				`${compact(split(",", "a,,b"))}`,
   508  				[]interface{}{"a", "b"},
   509  				false,
   510  			},
   511  
   512  			// empty string at the end of array
   513  			{
   514  				`${compact(split(",", "a,b,"))}`,
   515  				[]interface{}{"a", "b"},
   516  				false,
   517  			},
   518  
   519  			// single empty string
   520  			{
   521  				`${compact(split(",", ""))}`,
   522  				[]interface{}{},
   523  				false,
   524  			},
   525  
   526  			// errrors on list of lists
   527  			{
   528  				`${compact(list(list("a"), list("b")))}`,
   529  				nil,
   530  				true,
   531  			},
   532  		},
   533  	})
   534  }
   535  
   536  func TestInterpolateFuncCidrHost(t *testing.T) {
   537  	testFunction(t, testFunctionConfig{
   538  		Cases: []testFunctionCase{
   539  			{
   540  				`${cidrhost("192.168.1.0/24", 5)}`,
   541  				"192.168.1.5",
   542  				false,
   543  			},
   544  			{
   545  				`${cidrhost("192.168.1.0/24", -5)}`,
   546  				"192.168.1.251",
   547  				false,
   548  			},
   549  			{
   550  				`${cidrhost("192.168.1.0/24", -256)}`,
   551  				"192.168.1.0",
   552  				false,
   553  			},
   554  			{
   555  				`${cidrhost("192.168.1.0/30", 255)}`,
   556  				nil,
   557  				true, // 255 doesn't fit in two bits
   558  			},
   559  			{
   560  				`${cidrhost("192.168.1.0/30", -255)}`,
   561  				nil,
   562  				true, // 255 doesn't fit in two bits
   563  			},
   564  			{
   565  				`${cidrhost("not-a-cidr", 6)}`,
   566  				nil,
   567  				true, // not a valid CIDR mask
   568  			},
   569  			{
   570  				`${cidrhost("10.256.0.0/8", 6)}`,
   571  				nil,
   572  				true, // can't have an octet >255
   573  			},
   574  		},
   575  	})
   576  }
   577  
   578  func TestInterpolateFuncCidrNetmask(t *testing.T) {
   579  	testFunction(t, testFunctionConfig{
   580  		Cases: []testFunctionCase{
   581  			{
   582  				`${cidrnetmask("192.168.1.0/24")}`,
   583  				"255.255.255.0",
   584  				false,
   585  			},
   586  			{
   587  				`${cidrnetmask("192.168.1.0/32")}`,
   588  				"255.255.255.255",
   589  				false,
   590  			},
   591  			{
   592  				`${cidrnetmask("0.0.0.0/0")}`,
   593  				"0.0.0.0",
   594  				false,
   595  			},
   596  			{
   597  				// This doesn't really make sense for IPv6 networks
   598  				// but it ought to do something sensible anyway.
   599  				`${cidrnetmask("1::/64")}`,
   600  				"ffff:ffff:ffff:ffff::",
   601  				false,
   602  			},
   603  			{
   604  				`${cidrnetmask("not-a-cidr")}`,
   605  				nil,
   606  				true, // not a valid CIDR mask
   607  			},
   608  			{
   609  				`${cidrnetmask("10.256.0.0/8")}`,
   610  				nil,
   611  				true, // can't have an octet >255
   612  			},
   613  		},
   614  	})
   615  }
   616  
   617  func TestInterpolateFuncCidrSubnet(t *testing.T) {
   618  	testFunction(t, testFunctionConfig{
   619  		Cases: []testFunctionCase{
   620  			{
   621  				`${cidrsubnet("192.168.2.0/20", 4, 6)}`,
   622  				"192.168.6.0/24",
   623  				false,
   624  			},
   625  			{
   626  				`${cidrsubnet("fe80::/48", 16, 6)}`,
   627  				"fe80:0:0:6::/64",
   628  				false,
   629  			},
   630  			{
   631  				// IPv4 address encoded in IPv6 syntax gets normalized
   632  				`${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`,
   633  				"192.168.6.0/24",
   634  				false,
   635  			},
   636  			{
   637  				`${cidrsubnet("192.168.0.0/30", 4, 6)}`,
   638  				nil,
   639  				true, // not enough bits left
   640  			},
   641  			{
   642  				`${cidrsubnet("192.168.0.0/16", 2, 16)}`,
   643  				nil,
   644  				true, // can't encode 16 in 2 bits
   645  			},
   646  			{
   647  				`${cidrsubnet("not-a-cidr", 4, 6)}`,
   648  				nil,
   649  				true, // not a valid CIDR mask
   650  			},
   651  			{
   652  				`${cidrsubnet("10.256.0.0/8", 4, 6)}`,
   653  				nil,
   654  				true, // can't have an octet >255
   655  			},
   656  		},
   657  	})
   658  }
   659  
   660  func TestInterpolateFuncCoalesce(t *testing.T) {
   661  	testFunction(t, testFunctionConfig{
   662  		Cases: []testFunctionCase{
   663  			{
   664  				`${coalesce("first", "second", "third")}`,
   665  				"first",
   666  				false,
   667  			},
   668  			{
   669  				`${coalesce("", "second", "third")}`,
   670  				"second",
   671  				false,
   672  			},
   673  			{
   674  				`${coalesce("", "", "")}`,
   675  				"",
   676  				false,
   677  			},
   678  			{
   679  				`${coalesce("foo")}`,
   680  				nil,
   681  				true,
   682  			},
   683  		},
   684  	})
   685  }
   686  
   687  func TestInterpolateFuncCoalesceList(t *testing.T) {
   688  	testFunction(t, testFunctionConfig{
   689  		Cases: []testFunctionCase{
   690  			{
   691  				`${coalescelist(list("first"), list("second"), list("third"))}`,
   692  				[]interface{}{"first"},
   693  				false,
   694  			},
   695  			{
   696  				`${coalescelist(list(), list("second"), list("third"))}`,
   697  				[]interface{}{"second"},
   698  				false,
   699  			},
   700  			{
   701  				`${coalescelist(list(), list(), list())}`,
   702  				[]interface{}{},
   703  				false,
   704  			},
   705  			{
   706  				`${coalescelist(list("foo"))}`,
   707  				nil,
   708  				true,
   709  			},
   710  		},
   711  	})
   712  }
   713  
   714  func TestInterpolateFuncConcat(t *testing.T) {
   715  	testFunction(t, testFunctionConfig{
   716  		Cases: []testFunctionCase{
   717  			// String + list
   718  			// no longer supported, now returns an error
   719  			{
   720  				`${concat("a", split(",", "b,c"))}`,
   721  				nil,
   722  				true,
   723  			},
   724  
   725  			// List + string
   726  			// no longer supported, now returns an error
   727  			{
   728  				`${concat(split(",", "a,b"), "c")}`,
   729  				nil,
   730  				true,
   731  			},
   732  
   733  			// Single list
   734  			{
   735  				`${concat(split(",", ",foo,"))}`,
   736  				[]interface{}{"", "foo", ""},
   737  				false,
   738  			},
   739  			{
   740  				`${concat(split(",", "a,b,c"))}`,
   741  				[]interface{}{"a", "b", "c"},
   742  				false,
   743  			},
   744  
   745  			// Two lists
   746  			{
   747  				`${concat(split(",", "a,b,c"), split(",", "d,e"))}`,
   748  				[]interface{}{"a", "b", "c", "d", "e"},
   749  				false,
   750  			},
   751  			// Two lists with different separators
   752  			{
   753  				`${concat(split(",", "a,b,c"), split(" ", "d e"))}`,
   754  				[]interface{}{"a", "b", "c", "d", "e"},
   755  				false,
   756  			},
   757  
   758  			// More lists
   759  			{
   760  				`${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`,
   761  				[]interface{}{"a", "b", "c", "d", "e", "f", "0", "1"},
   762  				false,
   763  			},
   764  
   765  			// list vars
   766  			{
   767  				`${concat("${var.list}", "${var.list}")}`,
   768  				[]interface{}{"a", "b", "a", "b"},
   769  				false,
   770  			},
   771  			// lists of lists
   772  			{
   773  				`${concat("${var.lists}", "${var.lists}")}`,
   774  				[]interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}},
   775  				false,
   776  			},
   777  
   778  			// lists of maps
   779  			{
   780  				`${concat("${var.maps}", "${var.maps}")}`,
   781  				[]interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}},
   782  				false,
   783  			},
   784  
   785  			// multiple strings
   786  			// no longer supported, now returns an error
   787  			{
   788  				`${concat("string1", "string2")}`,
   789  				nil,
   790  				true,
   791  			},
   792  
   793  			// mismatched types
   794  			{
   795  				`${concat("${var.lists}", "${var.maps}")}`,
   796  				nil,
   797  				true,
   798  			},
   799  		},
   800  		Vars: map[string]ast.Variable{
   801  			"var.list": {
   802  				Type: ast.TypeList,
   803  				Value: []ast.Variable{
   804  					{
   805  						Type:  ast.TypeString,
   806  						Value: "a",
   807  					},
   808  					{
   809  						Type:  ast.TypeString,
   810  						Value: "b",
   811  					},
   812  				},
   813  			},
   814  			"var.lists": {
   815  				Type: ast.TypeList,
   816  				Value: []ast.Variable{
   817  					{
   818  						Type: ast.TypeList,
   819  						Value: []ast.Variable{
   820  							{
   821  								Type:  ast.TypeString,
   822  								Value: "c",
   823  							},
   824  							{
   825  								Type:  ast.TypeString,
   826  								Value: "d",
   827  							},
   828  						},
   829  					},
   830  				},
   831  			},
   832  			"var.maps": {
   833  				Type: ast.TypeList,
   834  				Value: []ast.Variable{
   835  					{
   836  						Type: ast.TypeMap,
   837  						Value: map[string]ast.Variable{
   838  							"key1": {
   839  								Type:  ast.TypeString,
   840  								Value: "a",
   841  							},
   842  							"key2": {
   843  								Type:  ast.TypeString,
   844  								Value: "b",
   845  							},
   846  						},
   847  					},
   848  				},
   849  			},
   850  		},
   851  	})
   852  }
   853  
   854  func TestInterpolateFuncMerge(t *testing.T) {
   855  	testFunction(t, testFunctionConfig{
   856  		Cases: []testFunctionCase{
   857  			// basic merge
   858  			{
   859  				`${merge(map("a", "b"), map("c", "d"))}`,
   860  				map[string]interface{}{"a": "b", "c": "d"},
   861  				false,
   862  			},
   863  
   864  			// merge with conflicts is ok, last in wins.
   865  			{
   866  				`${merge(map("a", "b", "c", "X"), map("c", "d"))}`,
   867  				map[string]interface{}{"a": "b", "c": "d"},
   868  				false,
   869  			},
   870  
   871  			// merge variadic
   872  			{
   873  				`${merge(map("a", "b"), map("c", "d"), map("e", "f"))}`,
   874  				map[string]interface{}{"a": "b", "c": "d", "e": "f"},
   875  				false,
   876  			},
   877  
   878  			// merge with variables
   879  			{
   880  				`${merge(var.maps[0], map("c", "d"))}`,
   881  				map[string]interface{}{"key1": "a", "key2": "b", "c": "d"},
   882  				false,
   883  			},
   884  
   885  			// only accept maps
   886  			{
   887  				`${merge(map("a", "b"), list("c", "d"))}`,
   888  				nil,
   889  				true,
   890  			},
   891  
   892  			// merge maps of maps
   893  			{
   894  				`${merge(map("a", var.maps[0]), map("b", var.maps[1]))}`,
   895  				map[string]interface{}{
   896  					"b": map[string]interface{}{"key3": "d", "key4": "c"},
   897  					"a": map[string]interface{}{"key1": "a", "key2": "b"},
   898  				},
   899  				false,
   900  			},
   901  			// merge maps of lists
   902  			{
   903  				`${merge(map("a", list("b")), map("c", list("d", "e")))}`,
   904  				map[string]interface{}{"a": []interface{}{"b"}, "c": []interface{}{"d", "e"}},
   905  				false,
   906  			},
   907  			// merge map of various kinds
   908  			{
   909  				`${merge(map("a", var.maps[0]), map("b", list("c", "d")))}`,
   910  				map[string]interface{}{"a": map[string]interface{}{"key1": "a", "key2": "b"}, "b": []interface{}{"c", "d"}},
   911  				false,
   912  			},
   913  		},
   914  		Vars: map[string]ast.Variable{
   915  			"var.maps": {
   916  				Type: ast.TypeList,
   917  				Value: []ast.Variable{
   918  					{
   919  						Type: ast.TypeMap,
   920  						Value: map[string]ast.Variable{
   921  							"key1": {
   922  								Type:  ast.TypeString,
   923  								Value: "a",
   924  							},
   925  							"key2": {
   926  								Type:  ast.TypeString,
   927  								Value: "b",
   928  							},
   929  						},
   930  					},
   931  					{
   932  						Type: ast.TypeMap,
   933  						Value: map[string]ast.Variable{
   934  							"key3": {
   935  								Type:  ast.TypeString,
   936  								Value: "d",
   937  							},
   938  							"key4": {
   939  								Type:  ast.TypeString,
   940  								Value: "c",
   941  							},
   942  						},
   943  					},
   944  				},
   945  			},
   946  		},
   947  	})
   948  
   949  }
   950  
   951  func TestInterpolateFuncDirname(t *testing.T) {
   952  	testFunction(t, testFunctionConfig{
   953  		Cases: []testFunctionCase{
   954  			{
   955  				`${dirname("/foo/bar/baz")}`,
   956  				"/foo/bar",
   957  				false,
   958  			},
   959  		},
   960  	})
   961  }
   962  
   963  func TestInterpolateFuncDistinct(t *testing.T) {
   964  	testFunction(t, testFunctionConfig{
   965  		Cases: []testFunctionCase{
   966  			// 3 duplicates
   967  			{
   968  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`,
   969  				[]interface{}{"user1", "user2", "user3"},
   970  				false,
   971  			},
   972  			// 1 duplicate
   973  			{
   974  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`,
   975  				[]interface{}{"user1", "user2", "user3", "user4"},
   976  				false,
   977  			},
   978  			// too many args
   979  			{
   980  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`,
   981  				nil,
   982  				true,
   983  			},
   984  			// non-flat list is an error
   985  			{
   986  				`${distinct(list(list("a"), list("a")))}`,
   987  				nil,
   988  				true,
   989  			},
   990  		},
   991  	})
   992  }
   993  
   994  func TestInterpolateFuncMatchKeys(t *testing.T) {
   995  	testFunction(t, testFunctionConfig{
   996  		Cases: []testFunctionCase{
   997  			// normal usage
   998  			{
   999  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2"))}`,
  1000  				[]interface{}{"b"},
  1001  				false,
  1002  			},
  1003  			// normal usage 2, check the order
  1004  			{
  1005  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref1"))}`,
  1006  				[]interface{}{"a", "b"},
  1007  				false,
  1008  			},
  1009  			// duplicate item in searchset
  1010  			{
  1011  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref2"))}`,
  1012  				[]interface{}{"b"},
  1013  				false,
  1014  			},
  1015  			// no matches
  1016  			{
  1017  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref4"))}`,
  1018  				[]interface{}{},
  1019  				false,
  1020  			},
  1021  			// no matches 2
  1022  			{
  1023  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list())}`,
  1024  				[]interface{}{},
  1025  				false,
  1026  			},
  1027  			// zero case
  1028  			{
  1029  				`${matchkeys(list(), list(), list("nope"))}`,
  1030  				[]interface{}{},
  1031  				false,
  1032  			},
  1033  			// complex values
  1034  			{
  1035  				`${matchkeys(list(list("a", "a")), list("a"), list("a"))}`,
  1036  				[]interface{}{[]interface{}{"a", "a"}},
  1037  				false,
  1038  			},
  1039  			// errors
  1040  			// different types
  1041  			{
  1042  				`${matchkeys(list("a"), list(1), list("a"))}`,
  1043  				nil,
  1044  				true,
  1045  			},
  1046  			// different types
  1047  			{
  1048  				`${matchkeys(list("a"), list(list("a"), list("a")), list("a"))}`,
  1049  				nil,
  1050  				true,
  1051  			},
  1052  			// lists of different length is an error
  1053  			{
  1054  				`${matchkeys(list("a"), list("a", "b"), list("a"))}`,
  1055  				nil,
  1056  				true,
  1057  			},
  1058  		},
  1059  	})
  1060  }
  1061  
  1062  func TestInterpolateFuncFile(t *testing.T) {
  1063  	tf, err := ioutil.TempFile("", "tf")
  1064  	if err != nil {
  1065  		t.Fatalf("err: %s", err)
  1066  	}
  1067  	path := tf.Name()
  1068  	tf.Write([]byte("foo"))
  1069  	tf.Close()
  1070  	defer os.Remove(path)
  1071  
  1072  	testFunction(t, testFunctionConfig{
  1073  		Cases: []testFunctionCase{
  1074  			{
  1075  				fmt.Sprintf(`${file("%s")}`, path),
  1076  				"foo",
  1077  				false,
  1078  			},
  1079  
  1080  			// Invalid path
  1081  			{
  1082  				`${file("/i/dont/exist")}`,
  1083  				nil,
  1084  				true,
  1085  			},
  1086  
  1087  			// Too many args
  1088  			{
  1089  				`${file("foo", "bar")}`,
  1090  				nil,
  1091  				true,
  1092  			},
  1093  		},
  1094  	})
  1095  }
  1096  
  1097  func TestInterpolateFuncFormat(t *testing.T) {
  1098  	testFunction(t, testFunctionConfig{
  1099  		Cases: []testFunctionCase{
  1100  			{
  1101  				`${format("hello")}`,
  1102  				"hello",
  1103  				false,
  1104  			},
  1105  
  1106  			{
  1107  				`${format("hello %s", "world")}`,
  1108  				"hello world",
  1109  				false,
  1110  			},
  1111  
  1112  			{
  1113  				`${format("hello %d", 42)}`,
  1114  				"hello 42",
  1115  				false,
  1116  			},
  1117  
  1118  			{
  1119  				`${format("hello %05d", 42)}`,
  1120  				"hello 00042",
  1121  				false,
  1122  			},
  1123  
  1124  			{
  1125  				`${format("hello %05d", 12345)}`,
  1126  				"hello 12345",
  1127  				false,
  1128  			},
  1129  		},
  1130  	})
  1131  }
  1132  
  1133  func TestInterpolateFuncFormatList(t *testing.T) {
  1134  	testFunction(t, testFunctionConfig{
  1135  		Cases: []testFunctionCase{
  1136  			// formatlist requires at least one list
  1137  			{
  1138  				`${formatlist("hello")}`,
  1139  				nil,
  1140  				true,
  1141  			},
  1142  			{
  1143  				`${formatlist("hello %s", "world")}`,
  1144  				nil,
  1145  				true,
  1146  			},
  1147  			// formatlist applies to each list element in turn
  1148  			{
  1149  				`${formatlist("<%s>", split(",", "A,B"))}`,
  1150  				[]interface{}{"<A>", "<B>"},
  1151  				false,
  1152  			},
  1153  			// formatlist repeats scalar elements
  1154  			{
  1155  				`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
  1156  				"x=A, x=B, x=C",
  1157  				false,
  1158  			},
  1159  			// Multiple lists are walked in parallel
  1160  			{
  1161  				`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
  1162  				"A=1, B=2, C=3",
  1163  				false,
  1164  			},
  1165  			// Mismatched list lengths generate an error
  1166  			{
  1167  				`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
  1168  				nil,
  1169  				true,
  1170  			},
  1171  			// Works with lists of length 1 [GH-2240]
  1172  			{
  1173  				`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
  1174  				[]interface{}{"demo-rest-elb.id"},
  1175  				false,
  1176  			},
  1177  			// Works with empty lists [GH-7607]
  1178  			{
  1179  				`${formatlist("%s", var.emptylist)}`,
  1180  				[]interface{}{},
  1181  				false,
  1182  			},
  1183  		},
  1184  		Vars: map[string]ast.Variable{
  1185  			"var.emptylist": {
  1186  				Type:  ast.TypeList,
  1187  				Value: []ast.Variable{},
  1188  			},
  1189  		},
  1190  	})
  1191  }
  1192  
  1193  func TestInterpolateFuncIndex(t *testing.T) {
  1194  	testFunction(t, testFunctionConfig{
  1195  		Vars: map[string]ast.Variable{
  1196  			"var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
  1197  			"var.list2": interfaceToVariableSwallowError([]string{"foo"}),
  1198  			"var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}),
  1199  		},
  1200  		Cases: []testFunctionCase{
  1201  			{
  1202  				`${index("test", "")}`,
  1203  				nil,
  1204  				true,
  1205  			},
  1206  
  1207  			{
  1208  				`${index(var.list1, "foo")}`,
  1209  				nil,
  1210  				true,
  1211  			},
  1212  
  1213  			{
  1214  				`${index(var.list2, "foo")}`,
  1215  				"0",
  1216  				false,
  1217  			},
  1218  
  1219  			{
  1220  				`${index(var.list3, "bar")}`,
  1221  				"2",
  1222  				false,
  1223  			},
  1224  		},
  1225  	})
  1226  }
  1227  
  1228  func TestInterpolateFuncJoin(t *testing.T) {
  1229  	testFunction(t, testFunctionConfig{
  1230  		Vars: map[string]ast.Variable{
  1231  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo"}),
  1232  			"var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}),
  1233  			"var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}),
  1234  		},
  1235  		Cases: []testFunctionCase{
  1236  			{
  1237  				`${join(",")}`,
  1238  				nil,
  1239  				true,
  1240  			},
  1241  
  1242  			{
  1243  				`${join(",", var.a_list)}`,
  1244  				"foo",
  1245  				false,
  1246  			},
  1247  
  1248  			{
  1249  				`${join(".", var.a_longer_list)}`,
  1250  				"foo.bar.baz",
  1251  				false,
  1252  			},
  1253  
  1254  			{
  1255  				`${join(".", var.list_of_lists)}`,
  1256  				nil,
  1257  				true,
  1258  			},
  1259  			{
  1260  				`${join(".", list(list("nested")))}`,
  1261  				nil,
  1262  				true,
  1263  			},
  1264  		},
  1265  	})
  1266  }
  1267  
  1268  func TestInterpolateFuncJSONEncode(t *testing.T) {
  1269  	testFunction(t, testFunctionConfig{
  1270  		Vars: map[string]ast.Variable{
  1271  			"easy": ast.Variable{
  1272  				Value: "test",
  1273  				Type:  ast.TypeString,
  1274  			},
  1275  			"hard": ast.Variable{
  1276  				Value: " foo \\ \n \t \" bar ",
  1277  				Type:  ast.TypeString,
  1278  			},
  1279  			"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
  1280  			// XXX can't use InterfaceToVariable as it converts empty slice into empty
  1281  			// map.
  1282  			"emptylist": ast.Variable{
  1283  				Value: []ast.Variable{},
  1284  				Type:  ast.TypeList,
  1285  			},
  1286  			"map": interfaceToVariableSwallowError(map[string]string{
  1287  				"foo":     "bar",
  1288  				"ba \n z": "q\\x",
  1289  			}),
  1290  			"emptymap": interfaceToVariableSwallowError(map[string]string{}),
  1291  
  1292  			// Not yet supported (but it would be nice)
  1293  			"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
  1294  			"nestedmap":  interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
  1295  		},
  1296  		Cases: []testFunctionCase{
  1297  			{
  1298  				`${jsonencode("test")}`,
  1299  				`"test"`,
  1300  				false,
  1301  			},
  1302  			{
  1303  				`${jsonencode(easy)}`,
  1304  				`"test"`,
  1305  				false,
  1306  			},
  1307  			{
  1308  				`${jsonencode(hard)}`,
  1309  				`" foo \\ \n \t \" bar "`,
  1310  				false,
  1311  			},
  1312  			{
  1313  				`${jsonencode("")}`,
  1314  				`""`,
  1315  				false,
  1316  			},
  1317  			{
  1318  				`${jsonencode()}`,
  1319  				nil,
  1320  				true,
  1321  			},
  1322  			{
  1323  				`${jsonencode(list)}`,
  1324  				`["foo","bar\tbaz"]`,
  1325  				false,
  1326  			},
  1327  			{
  1328  				`${jsonencode(emptylist)}`,
  1329  				`[]`,
  1330  				false,
  1331  			},
  1332  			{
  1333  				`${jsonencode(map)}`,
  1334  				`{"ba \n z":"q\\x","foo":"bar"}`,
  1335  				false,
  1336  			},
  1337  			{
  1338  				`${jsonencode(emptymap)}`,
  1339  				`{}`,
  1340  				false,
  1341  			},
  1342  			{
  1343  				`${jsonencode(nestedlist)}`,
  1344  				nil,
  1345  				true,
  1346  			},
  1347  			{
  1348  				`${jsonencode(nestedmap)}`,
  1349  				nil,
  1350  				true,
  1351  			},
  1352  		},
  1353  	})
  1354  }
  1355  
  1356  func TestInterpolateFuncReplace(t *testing.T) {
  1357  	testFunction(t, testFunctionConfig{
  1358  		Cases: []testFunctionCase{
  1359  			// Regular search and replace
  1360  			{
  1361  				`${replace("hello", "hel", "bel")}`,
  1362  				"bello",
  1363  				false,
  1364  			},
  1365  
  1366  			// Search string doesn't match
  1367  			{
  1368  				`${replace("hello", "nope", "bel")}`,
  1369  				"hello",
  1370  				false,
  1371  			},
  1372  
  1373  			// Regular expression
  1374  			{
  1375  				`${replace("hello", "/l/", "L")}`,
  1376  				"heLLo",
  1377  				false,
  1378  			},
  1379  
  1380  			{
  1381  				`${replace("helo", "/(l)/", "$1$1")}`,
  1382  				"hello",
  1383  				false,
  1384  			},
  1385  
  1386  			// Bad regexp
  1387  			{
  1388  				`${replace("helo", "/(l/", "$1$1")}`,
  1389  				nil,
  1390  				true,
  1391  			},
  1392  		},
  1393  	})
  1394  }
  1395  
  1396  func TestInterpolateFuncLength(t *testing.T) {
  1397  	testFunction(t, testFunctionConfig{
  1398  		Cases: []testFunctionCase{
  1399  			// Raw strings
  1400  			{
  1401  				`${length("")}`,
  1402  				"0",
  1403  				false,
  1404  			},
  1405  			{
  1406  				`${length("a")}`,
  1407  				"1",
  1408  				false,
  1409  			},
  1410  			{
  1411  				`${length(" ")}`,
  1412  				"1",
  1413  				false,
  1414  			},
  1415  			{
  1416  				`${length(" a ,")}`,
  1417  				"4",
  1418  				false,
  1419  			},
  1420  			{
  1421  				`${length("aaa")}`,
  1422  				"3",
  1423  				false,
  1424  			},
  1425  
  1426  			// Lists
  1427  			{
  1428  				`${length(split(",", "a"))}`,
  1429  				"1",
  1430  				false,
  1431  			},
  1432  			{
  1433  				`${length(split(",", "foo,"))}`,
  1434  				"2",
  1435  				false,
  1436  			},
  1437  			{
  1438  				`${length(split(",", ",foo,"))}`,
  1439  				"3",
  1440  				false,
  1441  			},
  1442  			{
  1443  				`${length(split(",", "foo,bar"))}`,
  1444  				"2",
  1445  				false,
  1446  			},
  1447  			{
  1448  				`${length(split(".", "one.two.three.four.five"))}`,
  1449  				"5",
  1450  				false,
  1451  			},
  1452  			// Want length 0 if we split an empty string then compact
  1453  			{
  1454  				`${length(compact(split(",", "")))}`,
  1455  				"0",
  1456  				false,
  1457  			},
  1458  			// Works for maps
  1459  			{
  1460  				`${length(map("k", "v"))}`,
  1461  				"1",
  1462  				false,
  1463  			},
  1464  			{
  1465  				`${length(map("k1", "v1", "k2", "v2"))}`,
  1466  				"2",
  1467  				false,
  1468  			},
  1469  		},
  1470  	})
  1471  }
  1472  
  1473  func TestInterpolateFuncSignum(t *testing.T) {
  1474  	testFunction(t, testFunctionConfig{
  1475  		Cases: []testFunctionCase{
  1476  			{
  1477  				`${signum()}`,
  1478  				nil,
  1479  				true,
  1480  			},
  1481  
  1482  			{
  1483  				`${signum("")}`,
  1484  				nil,
  1485  				true,
  1486  			},
  1487  
  1488  			{
  1489  				`${signum(0)}`,
  1490  				"0",
  1491  				false,
  1492  			},
  1493  
  1494  			{
  1495  				`${signum(15)}`,
  1496  				"1",
  1497  				false,
  1498  			},
  1499  
  1500  			{
  1501  				`${signum(-29)}`,
  1502  				"-1",
  1503  				false,
  1504  			},
  1505  		},
  1506  	})
  1507  }
  1508  
  1509  func TestInterpolateFuncSlice(t *testing.T) {
  1510  	testFunction(t, testFunctionConfig{
  1511  		Cases: []testFunctionCase{
  1512  			// Negative from index
  1513  			{
  1514  				`${slice(list("a"), -1, 0)}`,
  1515  				nil,
  1516  				true,
  1517  			},
  1518  			// From index > to index
  1519  			{
  1520  				`${slice(list("a", "b", "c"), 2, 1)}`,
  1521  				nil,
  1522  				true,
  1523  			},
  1524  			// To index too large
  1525  			{
  1526  				`${slice(var.list_of_strings, 1, 4)}`,
  1527  				nil,
  1528  				true,
  1529  			},
  1530  			// Empty slice
  1531  			{
  1532  				`${slice(var.list_of_strings, 1, 1)}`,
  1533  				[]interface{}{},
  1534  				false,
  1535  			},
  1536  			{
  1537  				`${slice(var.list_of_strings, 1, 2)}`,
  1538  				[]interface{}{"b"},
  1539  				false,
  1540  			},
  1541  			{
  1542  				`${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`,
  1543  				[]interface{}{"a", "b"},
  1544  				false,
  1545  			},
  1546  		},
  1547  		Vars: map[string]ast.Variable{
  1548  			"var.list_of_strings": {
  1549  				Type: ast.TypeList,
  1550  				Value: []ast.Variable{
  1551  					{
  1552  						Type:  ast.TypeString,
  1553  						Value: "a",
  1554  					},
  1555  					{
  1556  						Type:  ast.TypeString,
  1557  						Value: "b",
  1558  					},
  1559  					{
  1560  						Type:  ast.TypeString,
  1561  						Value: "c",
  1562  					},
  1563  				},
  1564  			},
  1565  		},
  1566  	})
  1567  }
  1568  
  1569  func TestInterpolateFuncSort(t *testing.T) {
  1570  	testFunction(t, testFunctionConfig{
  1571  		Vars: map[string]ast.Variable{
  1572  			"var.strings": ast.Variable{
  1573  				Type: ast.TypeList,
  1574  				Value: []ast.Variable{
  1575  					{Type: ast.TypeString, Value: "c"},
  1576  					{Type: ast.TypeString, Value: "a"},
  1577  					{Type: ast.TypeString, Value: "b"},
  1578  				},
  1579  			},
  1580  			"var.notstrings": ast.Variable{
  1581  				Type: ast.TypeList,
  1582  				Value: []ast.Variable{
  1583  					{Type: ast.TypeList, Value: []ast.Variable{}},
  1584  					{Type: ast.TypeString, Value: "b"},
  1585  				},
  1586  			},
  1587  		},
  1588  		Cases: []testFunctionCase{
  1589  			{
  1590  				`${sort(var.strings)}`,
  1591  				[]interface{}{"a", "b", "c"},
  1592  				false,
  1593  			},
  1594  			{
  1595  				`${sort(var.notstrings)}`,
  1596  				nil,
  1597  				true,
  1598  			},
  1599  		},
  1600  	})
  1601  }
  1602  
  1603  func TestInterpolateFuncSplit(t *testing.T) {
  1604  	testFunction(t, testFunctionConfig{
  1605  		Cases: []testFunctionCase{
  1606  			{
  1607  				`${split(",")}`,
  1608  				nil,
  1609  				true,
  1610  			},
  1611  
  1612  			{
  1613  				`${split(",", "")}`,
  1614  				[]interface{}{""},
  1615  				false,
  1616  			},
  1617  
  1618  			{
  1619  				`${split(",", "foo")}`,
  1620  				[]interface{}{"foo"},
  1621  				false,
  1622  			},
  1623  
  1624  			{
  1625  				`${split(",", ",,,")}`,
  1626  				[]interface{}{"", "", "", ""},
  1627  				false,
  1628  			},
  1629  
  1630  			{
  1631  				`${split(",", "foo,")}`,
  1632  				[]interface{}{"foo", ""},
  1633  				false,
  1634  			},
  1635  
  1636  			{
  1637  				`${split(",", ",foo,")}`,
  1638  				[]interface{}{"", "foo", ""},
  1639  				false,
  1640  			},
  1641  
  1642  			{
  1643  				`${split(".", "foo.bar.baz")}`,
  1644  				[]interface{}{"foo", "bar", "baz"},
  1645  				false,
  1646  			},
  1647  		},
  1648  	})
  1649  }
  1650  
  1651  func TestInterpolateFuncLookup(t *testing.T) {
  1652  	testFunction(t, testFunctionConfig{
  1653  		Vars: map[string]ast.Variable{
  1654  			"var.foo": {
  1655  				Type: ast.TypeMap,
  1656  				Value: map[string]ast.Variable{
  1657  					"bar": {
  1658  						Type:  ast.TypeString,
  1659  						Value: "baz",
  1660  					},
  1661  				},
  1662  			},
  1663  			"var.map_of_lists": ast.Variable{
  1664  				Type: ast.TypeMap,
  1665  				Value: map[string]ast.Variable{
  1666  					"bar": {
  1667  						Type: ast.TypeList,
  1668  						Value: []ast.Variable{
  1669  							{
  1670  								Type:  ast.TypeString,
  1671  								Value: "baz",
  1672  							},
  1673  						},
  1674  					},
  1675  				},
  1676  			},
  1677  		},
  1678  		Cases: []testFunctionCase{
  1679  			{
  1680  				`${lookup(var.foo, "bar")}`,
  1681  				"baz",
  1682  				false,
  1683  			},
  1684  
  1685  			// Invalid key
  1686  			{
  1687  				`${lookup(var.foo, "baz")}`,
  1688  				nil,
  1689  				true,
  1690  			},
  1691  
  1692  			// Supplied default with valid key
  1693  			{
  1694  				`${lookup(var.foo, "bar", "")}`,
  1695  				"baz",
  1696  				false,
  1697  			},
  1698  
  1699  			// Supplied default with invalid key
  1700  			{
  1701  				`${lookup(var.foo, "zip", "")}`,
  1702  				"",
  1703  				false,
  1704  			},
  1705  
  1706  			// Too many args
  1707  			{
  1708  				`${lookup(var.foo, "bar", "", "abc")}`,
  1709  				nil,
  1710  				true,
  1711  			},
  1712  
  1713  			// Cannot lookup into map of lists
  1714  			{
  1715  				`${lookup(var.map_of_lists, "bar")}`,
  1716  				nil,
  1717  				true,
  1718  			},
  1719  
  1720  			// Non-empty default
  1721  			{
  1722  				`${lookup(var.foo, "zap", "xyz")}`,
  1723  				"xyz",
  1724  				false,
  1725  			},
  1726  		},
  1727  	})
  1728  }
  1729  
  1730  func TestInterpolateFuncKeys(t *testing.T) {
  1731  	testFunction(t, testFunctionConfig{
  1732  		Vars: map[string]ast.Variable{
  1733  			"var.foo": ast.Variable{
  1734  				Type: ast.TypeMap,
  1735  				Value: map[string]ast.Variable{
  1736  					"bar": ast.Variable{
  1737  						Value: "baz",
  1738  						Type:  ast.TypeString,
  1739  					},
  1740  					"qux": ast.Variable{
  1741  						Value: "quack",
  1742  						Type:  ast.TypeString,
  1743  					},
  1744  				},
  1745  			},
  1746  			"var.str": ast.Variable{
  1747  				Value: "astring",
  1748  				Type:  ast.TypeString,
  1749  			},
  1750  		},
  1751  		Cases: []testFunctionCase{
  1752  			{
  1753  				`${keys(var.foo)}`,
  1754  				[]interface{}{"bar", "qux"},
  1755  				false,
  1756  			},
  1757  
  1758  			// Invalid key
  1759  			{
  1760  				`${keys(var.not)}`,
  1761  				nil,
  1762  				true,
  1763  			},
  1764  
  1765  			// Too many args
  1766  			{
  1767  				`${keys(var.foo, "bar")}`,
  1768  				nil,
  1769  				true,
  1770  			},
  1771  
  1772  			// Not a map
  1773  			{
  1774  				`${keys(var.str)}`,
  1775  				nil,
  1776  				true,
  1777  			},
  1778  		},
  1779  	})
  1780  }
  1781  
  1782  // Confirm that keys return in sorted order, and values return in the order of
  1783  // their sorted keys.
  1784  func TestInterpolateFuncKeyValOrder(t *testing.T) {
  1785  	testFunction(t, testFunctionConfig{
  1786  		Vars: map[string]ast.Variable{
  1787  			"var.foo": ast.Variable{
  1788  				Type: ast.TypeMap,
  1789  				Value: map[string]ast.Variable{
  1790  					"D": ast.Variable{
  1791  						Value: "2",
  1792  						Type:  ast.TypeString,
  1793  					},
  1794  					"C": ast.Variable{
  1795  						Value: "Y",
  1796  						Type:  ast.TypeString,
  1797  					},
  1798  					"A": ast.Variable{
  1799  						Value: "X",
  1800  						Type:  ast.TypeString,
  1801  					},
  1802  					"10": ast.Variable{
  1803  						Value: "Z",
  1804  						Type:  ast.TypeString,
  1805  					},
  1806  					"1": ast.Variable{
  1807  						Value: "4",
  1808  						Type:  ast.TypeString,
  1809  					},
  1810  					"3": ast.Variable{
  1811  						Value: "W",
  1812  						Type:  ast.TypeString,
  1813  					},
  1814  				},
  1815  			},
  1816  		},
  1817  		Cases: []testFunctionCase{
  1818  			{
  1819  				`${keys(var.foo)}`,
  1820  				[]interface{}{"1", "10", "3", "A", "C", "D"},
  1821  				false,
  1822  			},
  1823  
  1824  			{
  1825  				`${values(var.foo)}`,
  1826  				[]interface{}{"4", "Z", "W", "X", "Y", "2"},
  1827  				false,
  1828  			},
  1829  		},
  1830  	})
  1831  }
  1832  
  1833  func TestInterpolateFuncValues(t *testing.T) {
  1834  	testFunction(t, testFunctionConfig{
  1835  		Vars: map[string]ast.Variable{
  1836  			"var.foo": ast.Variable{
  1837  				Type: ast.TypeMap,
  1838  				Value: map[string]ast.Variable{
  1839  					"bar": ast.Variable{
  1840  						Value: "quack",
  1841  						Type:  ast.TypeString,
  1842  					},
  1843  					"qux": ast.Variable{
  1844  						Value: "baz",
  1845  						Type:  ast.TypeString,
  1846  					},
  1847  				},
  1848  			},
  1849  			"var.str": ast.Variable{
  1850  				Value: "astring",
  1851  				Type:  ast.TypeString,
  1852  			},
  1853  		},
  1854  		Cases: []testFunctionCase{
  1855  			{
  1856  				`${values(var.foo)}`,
  1857  				[]interface{}{"quack", "baz"},
  1858  				false,
  1859  			},
  1860  
  1861  			// Invalid key
  1862  			{
  1863  				`${values(var.not)}`,
  1864  				nil,
  1865  				true,
  1866  			},
  1867  
  1868  			// Too many args
  1869  			{
  1870  				`${values(var.foo, "bar")}`,
  1871  				nil,
  1872  				true,
  1873  			},
  1874  
  1875  			// Not a map
  1876  			{
  1877  				`${values(var.str)}`,
  1878  				nil,
  1879  				true,
  1880  			},
  1881  
  1882  			// Map of lists
  1883  			{
  1884  				`${values(map("one", list()))}`,
  1885  				nil,
  1886  				true,
  1887  			},
  1888  		},
  1889  	})
  1890  }
  1891  
  1892  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
  1893  	variable, _ := hil.InterfaceToVariable(input)
  1894  	return variable
  1895  }
  1896  
  1897  func TestInterpolateFuncElement(t *testing.T) {
  1898  	testFunction(t, testFunctionConfig{
  1899  		Vars: map[string]ast.Variable{
  1900  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo", "baz"}),
  1901  			"var.a_short_list":  interfaceToVariableSwallowError([]string{"foo"}),
  1902  			"var.empty_list":    interfaceToVariableSwallowError([]interface{}{}),
  1903  			"var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}),
  1904  		},
  1905  		Cases: []testFunctionCase{
  1906  			{
  1907  				`${element(var.a_list, "1")}`,
  1908  				"baz",
  1909  				false,
  1910  			},
  1911  
  1912  			{
  1913  				`${element(var.a_short_list, "0")}`,
  1914  				"foo",
  1915  				false,
  1916  			},
  1917  
  1918  			// Invalid index should wrap vs. out-of-bounds
  1919  			{
  1920  				`${element(var.a_list, "2")}`,
  1921  				"foo",
  1922  				false,
  1923  			},
  1924  
  1925  			// Negative number should fail
  1926  			{
  1927  				`${element(var.a_short_list, "-1")}`,
  1928  				nil,
  1929  				true,
  1930  			},
  1931  
  1932  			// Empty list should fail
  1933  			{
  1934  				`${element(var.empty_list, 0)}`,
  1935  				nil,
  1936  				true,
  1937  			},
  1938  
  1939  			// Too many args
  1940  			{
  1941  				`${element(var.a_list, "0", "2")}`,
  1942  				nil,
  1943  				true,
  1944  			},
  1945  
  1946  			// Only works on single-level lists
  1947  			{
  1948  				`${element(var.a_nested_list, "0")}`,
  1949  				nil,
  1950  				true,
  1951  			},
  1952  		},
  1953  	})
  1954  }
  1955  
  1956  func TestInterpolateFuncBasename(t *testing.T) {
  1957  	testFunction(t, testFunctionConfig{
  1958  		Cases: []testFunctionCase{
  1959  			{
  1960  				`${basename("/foo/bar/baz")}`,
  1961  				"baz",
  1962  				false,
  1963  			},
  1964  		},
  1965  	})
  1966  }
  1967  
  1968  func TestInterpolateFuncBase64Encode(t *testing.T) {
  1969  	testFunction(t, testFunctionConfig{
  1970  		Cases: []testFunctionCase{
  1971  			// Regular base64 encoding
  1972  			{
  1973  				`${base64encode("abc123!?$*&()'-=@~")}`,
  1974  				"YWJjMTIzIT8kKiYoKSctPUB+",
  1975  				false,
  1976  			},
  1977  		},
  1978  	})
  1979  }
  1980  
  1981  func TestInterpolateFuncBase64Decode(t *testing.T) {
  1982  	testFunction(t, testFunctionConfig{
  1983  		Cases: []testFunctionCase{
  1984  			// Regular base64 decoding
  1985  			{
  1986  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
  1987  				"abc123!?$*&()'-=@~",
  1988  				false,
  1989  			},
  1990  
  1991  			// Invalid base64 data decoding
  1992  			{
  1993  				`${base64decode("this-is-an-invalid-base64-data")}`,
  1994  				nil,
  1995  				true,
  1996  			},
  1997  		},
  1998  	})
  1999  }
  2000  
  2001  func TestInterpolateFuncLower(t *testing.T) {
  2002  	testFunction(t, testFunctionConfig{
  2003  		Cases: []testFunctionCase{
  2004  			{
  2005  				`${lower("HELLO")}`,
  2006  				"hello",
  2007  				false,
  2008  			},
  2009  
  2010  			{
  2011  				`${lower("")}`,
  2012  				"",
  2013  				false,
  2014  			},
  2015  
  2016  			{
  2017  				`${lower()}`,
  2018  				nil,
  2019  				true,
  2020  			},
  2021  		},
  2022  	})
  2023  }
  2024  
  2025  func TestInterpolateFuncUpper(t *testing.T) {
  2026  	testFunction(t, testFunctionConfig{
  2027  		Cases: []testFunctionCase{
  2028  			{
  2029  				`${upper("hello")}`,
  2030  				"HELLO",
  2031  				false,
  2032  			},
  2033  
  2034  			{
  2035  				`${upper("")}`,
  2036  				"",
  2037  				false,
  2038  			},
  2039  
  2040  			{
  2041  				`${upper()}`,
  2042  				nil,
  2043  				true,
  2044  			},
  2045  		},
  2046  	})
  2047  }
  2048  
  2049  func TestInterpolateFuncSha1(t *testing.T) {
  2050  	testFunction(t, testFunctionConfig{
  2051  		Cases: []testFunctionCase{
  2052  			{
  2053  				`${sha1("test")}`,
  2054  				"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
  2055  				false,
  2056  			},
  2057  		},
  2058  	})
  2059  }
  2060  
  2061  func TestInterpolateFuncSha256(t *testing.T) {
  2062  	testFunction(t, testFunctionConfig{
  2063  		Cases: []testFunctionCase{
  2064  			{ // hexadecimal representation of sha256 sum
  2065  				`${sha256("test")}`,
  2066  				"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  2067  				false,
  2068  			},
  2069  		},
  2070  	})
  2071  }
  2072  
  2073  func TestInterpolateFuncTitle(t *testing.T) {
  2074  	testFunction(t, testFunctionConfig{
  2075  		Cases: []testFunctionCase{
  2076  			{
  2077  				`${title("hello")}`,
  2078  				"Hello",
  2079  				false,
  2080  			},
  2081  
  2082  			{
  2083  				`${title("hello world")}`,
  2084  				"Hello World",
  2085  				false,
  2086  			},
  2087  
  2088  			{
  2089  				`${title("")}`,
  2090  				"",
  2091  				false,
  2092  			},
  2093  
  2094  			{
  2095  				`${title()}`,
  2096  				nil,
  2097  				true,
  2098  			},
  2099  		},
  2100  	})
  2101  }
  2102  
  2103  func TestInterpolateFuncTrimSpace(t *testing.T) {
  2104  	testFunction(t, testFunctionConfig{
  2105  		Cases: []testFunctionCase{
  2106  			{
  2107  				`${trimspace(" test ")}`,
  2108  				"test",
  2109  				false,
  2110  			},
  2111  		},
  2112  	})
  2113  }
  2114  
  2115  func TestInterpolateFuncBase64Sha256(t *testing.T) {
  2116  	testFunction(t, testFunctionConfig{
  2117  		Cases: []testFunctionCase{
  2118  			{
  2119  				`${base64sha256("test")}`,
  2120  				"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
  2121  				false,
  2122  			},
  2123  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  2124  				`${base64encode(sha256("test"))}`,
  2125  				"OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==",
  2126  				false,
  2127  			},
  2128  		},
  2129  	})
  2130  }
  2131  
  2132  func TestInterpolateFuncMd5(t *testing.T) {
  2133  	testFunction(t, testFunctionConfig{
  2134  		Cases: []testFunctionCase{
  2135  			{
  2136  				`${md5("tada")}`,
  2137  				"ce47d07243bb6eaf5e1322c81baf9bbf",
  2138  				false,
  2139  			},
  2140  			{ // Confirm that we're not trimming any whitespaces
  2141  				`${md5(" tada ")}`,
  2142  				"aadf191a583e53062de2d02c008141c4",
  2143  				false,
  2144  			},
  2145  			{ // We accept empty string too
  2146  				`${md5("")}`,
  2147  				"d41d8cd98f00b204e9800998ecf8427e",
  2148  				false,
  2149  			},
  2150  		},
  2151  	})
  2152  }
  2153  
  2154  func TestInterpolateFuncUUID(t *testing.T) {
  2155  	results := make(map[string]bool)
  2156  
  2157  	for i := 0; i < 100; i++ {
  2158  		ast, err := hil.Parse("${uuid()}")
  2159  		if err != nil {
  2160  			t.Fatalf("err: %s", err)
  2161  		}
  2162  
  2163  		result, err := hil.Eval(ast, langEvalConfig(nil))
  2164  		if err != nil {
  2165  			t.Fatalf("err: %s", err)
  2166  		}
  2167  
  2168  		if results[result.Value.(string)] {
  2169  			t.Fatalf("Got unexpected duplicate uuid: %s", result.Value)
  2170  		}
  2171  
  2172  		results[result.Value.(string)] = true
  2173  	}
  2174  }
  2175  
  2176  func TestInterpolateFuncTimestamp(t *testing.T) {
  2177  	currentTime := time.Now().UTC()
  2178  	ast, err := hil.Parse("${timestamp()}")
  2179  	if err != nil {
  2180  		t.Fatalf("err: %s", err)
  2181  	}
  2182  
  2183  	result, err := hil.Eval(ast, langEvalConfig(nil))
  2184  	if err != nil {
  2185  		t.Fatalf("err: %s", err)
  2186  	}
  2187  	resultTime, err := time.Parse(time.RFC3339, result.Value.(string))
  2188  	if err != nil {
  2189  		t.Fatalf("Error parsing timestamp: %s", err)
  2190  	}
  2191  
  2192  	if resultTime.Sub(currentTime).Seconds() > 10.0 {
  2193  		t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string))
  2194  	}
  2195  }
  2196  
  2197  type testFunctionConfig struct {
  2198  	Cases []testFunctionCase
  2199  	Vars  map[string]ast.Variable
  2200  }
  2201  
  2202  type testFunctionCase struct {
  2203  	Input  string
  2204  	Result interface{}
  2205  	Error  bool
  2206  }
  2207  
  2208  func testFunction(t *testing.T, config testFunctionConfig) {
  2209  	for i, tc := range config.Cases {
  2210  		ast, err := hil.Parse(tc.Input)
  2211  		if err != nil {
  2212  			t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err)
  2213  		}
  2214  
  2215  		result, err := hil.Eval(ast, langEvalConfig(config.Vars))
  2216  		if err != nil != tc.Error {
  2217  			t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err)
  2218  		}
  2219  
  2220  		if !reflect.DeepEqual(result.Value, tc.Result) {
  2221  			t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v",
  2222  				i, tc.Input, result.Value, tc.Result)
  2223  		}
  2224  	}
  2225  }
  2226  
  2227  func TestInterpolateFuncPathExpand(t *testing.T) {
  2228  	homePath, err := homedir.Dir()
  2229  	if err != nil {
  2230  		t.Fatalf("Error getting home directory: %v", err)
  2231  	}
  2232  	testFunction(t, testFunctionConfig{
  2233  		Cases: []testFunctionCase{
  2234  			{
  2235  				`${pathexpand("~/test-file")}`,
  2236  				filepath.Join(homePath, "test-file"),
  2237  				false,
  2238  			},
  2239  			{
  2240  				`${pathexpand("~/another/test/file")}`,
  2241  				filepath.Join(homePath, "another/test/file"),
  2242  				false,
  2243  			},
  2244  			{
  2245  				`${pathexpand("/root/file")}`,
  2246  				"/root/file",
  2247  				false,
  2248  			},
  2249  			{
  2250  				`${pathexpand("/")}`,
  2251  				"/",
  2252  				false,
  2253  			},
  2254  			{
  2255  				`${pathexpand()}`,
  2256  				nil,
  2257  				true,
  2258  			},
  2259  		},
  2260  	})
  2261  }
  2262  
  2263  func TestInterpolateFuncSubstr(t *testing.T) {
  2264  	testFunction(t, testFunctionConfig{
  2265  		Cases: []testFunctionCase{
  2266  			{
  2267  				`${substr("foobar", 0, 0)}`,
  2268  				"",
  2269  				false,
  2270  			},
  2271  			{
  2272  				`${substr("foobar", 0, -1)}`,
  2273  				"foobar",
  2274  				false,
  2275  			},
  2276  			{
  2277  				`${substr("foobar", 0, 3)}`,
  2278  				"foo",
  2279  				false,
  2280  			},
  2281  			{
  2282  				`${substr("foobar", 3, 3)}`,
  2283  				"bar",
  2284  				false,
  2285  			},
  2286  			{
  2287  				`${substr("foobar", -3, 3)}`,
  2288  				"bar",
  2289  				false,
  2290  			},
  2291  
  2292  			// empty string
  2293  			{
  2294  				`${substr("", 0, 0)}`,
  2295  				"",
  2296  				false,
  2297  			},
  2298  
  2299  			// invalid offset
  2300  			{
  2301  				`${substr("", 1, 0)}`,
  2302  				nil,
  2303  				true,
  2304  			},
  2305  
  2306  			// invalid length
  2307  			{
  2308  				`${substr("", 0, 1)}`,
  2309  				nil,
  2310  				true,
  2311  			},
  2312  			{
  2313  				`${substr("", 0, -2)}`,
  2314  				nil,
  2315  				true,
  2316  			},
  2317  		},
  2318  	})
  2319  }