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