github.com/grange74/terraform@v0.7.0-rc3.0.20160722171430-8c8803864753/config/interpolate_funcs_test.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/hil"
    11  	"github.com/hashicorp/hil/ast"
    12  )
    13  
    14  func TestInterpolateFuncList(t *testing.T) {
    15  	testFunction(t, testFunctionConfig{
    16  		Cases: []testFunctionCase{
    17  			// empty input returns empty list
    18  			{
    19  				`${list()}`,
    20  				[]interface{}{},
    21  				false,
    22  			},
    23  
    24  			// single input returns list of length 1
    25  			{
    26  				`${list("hello")}`,
    27  				[]interface{}{"hello"},
    28  				false,
    29  			},
    30  
    31  			// two inputs returns list of length 2
    32  			{
    33  				`${list("hello", "world")}`,
    34  				[]interface{}{"hello", "world"},
    35  				false,
    36  			},
    37  
    38  			// not a string input gives error
    39  			{
    40  				`${list("hello", 42)}`,
    41  				nil,
    42  				true,
    43  			},
    44  
    45  			// list of lists
    46  			{
    47  				`${list("${var.list}", "${var.list2}")}`,
    48  				[]interface{}{[]interface{}{"Hello", "World"}, []interface{}{"bar", "baz"}},
    49  				false,
    50  			},
    51  
    52  			// list of maps
    53  			{
    54  				`${list("${var.map}", "${var.map2}")}`,
    55  				[]interface{}{map[string]interface{}{"key": "bar"}, map[string]interface{}{"key2": "baz"}},
    56  				false,
    57  			},
    58  
    59  			// error on a heterogeneous list
    60  			{
    61  				`${list("first", "${var.list}")}`,
    62  				nil,
    63  				true,
    64  			},
    65  		},
    66  		Vars: map[string]ast.Variable{
    67  			"var.list": {
    68  				Type: ast.TypeList,
    69  				Value: []ast.Variable{
    70  					{
    71  						Type:  ast.TypeString,
    72  						Value: "Hello",
    73  					},
    74  					{
    75  						Type:  ast.TypeString,
    76  						Value: "World",
    77  					},
    78  				},
    79  			},
    80  			"var.list2": {
    81  				Type: ast.TypeList,
    82  				Value: []ast.Variable{
    83  					{
    84  						Type:  ast.TypeString,
    85  						Value: "bar",
    86  					},
    87  					{
    88  						Type:  ast.TypeString,
    89  						Value: "baz",
    90  					},
    91  				},
    92  			},
    93  
    94  			"var.map": {
    95  				Type: ast.TypeMap,
    96  				Value: map[string]ast.Variable{
    97  					"key": {
    98  						Type:  ast.TypeString,
    99  						Value: "bar",
   100  					},
   101  				},
   102  			},
   103  			"var.map2": {
   104  				Type: ast.TypeMap,
   105  				Value: map[string]ast.Variable{
   106  					"key2": {
   107  						Type:  ast.TypeString,
   108  						Value: "baz",
   109  					},
   110  				},
   111  			},
   112  		},
   113  	})
   114  }
   115  
   116  func TestInterpolateFuncCompact(t *testing.T) {
   117  	testFunction(t, testFunctionConfig{
   118  		Cases: []testFunctionCase{
   119  			// empty string within array
   120  			{
   121  				`${compact(split(",", "a,,b"))}`,
   122  				[]interface{}{"a", "b"},
   123  				false,
   124  			},
   125  
   126  			// empty string at the end of array
   127  			{
   128  				`${compact(split(",", "a,b,"))}`,
   129  				[]interface{}{"a", "b"},
   130  				false,
   131  			},
   132  
   133  			// single empty string
   134  			{
   135  				`${compact(split(",", ""))}`,
   136  				[]interface{}{},
   137  				false,
   138  			},
   139  		},
   140  	})
   141  }
   142  
   143  func TestInterpolateFuncCidrHost(t *testing.T) {
   144  	testFunction(t, testFunctionConfig{
   145  		Cases: []testFunctionCase{
   146  			{
   147  				`${cidrhost("192.168.1.0/24", 5)}`,
   148  				"192.168.1.5",
   149  				false,
   150  			},
   151  			{
   152  				`${cidrhost("192.168.1.0/30", 255)}`,
   153  				nil,
   154  				true, // 255 doesn't fit in two bits
   155  			},
   156  			{
   157  				`${cidrhost("not-a-cidr", 6)}`,
   158  				nil,
   159  				true, // not a valid CIDR mask
   160  			},
   161  			{
   162  				`${cidrhost("10.256.0.0/8", 6)}`,
   163  				nil,
   164  				true, // can't have an octet >255
   165  			},
   166  		},
   167  	})
   168  }
   169  
   170  func TestInterpolateFuncCidrNetmask(t *testing.T) {
   171  	testFunction(t, testFunctionConfig{
   172  		Cases: []testFunctionCase{
   173  			{
   174  				`${cidrnetmask("192.168.1.0/24")}`,
   175  				"255.255.255.0",
   176  				false,
   177  			},
   178  			{
   179  				`${cidrnetmask("192.168.1.0/32")}`,
   180  				"255.255.255.255",
   181  				false,
   182  			},
   183  			{
   184  				`${cidrnetmask("0.0.0.0/0")}`,
   185  				"0.0.0.0",
   186  				false,
   187  			},
   188  			{
   189  				// This doesn't really make sense for IPv6 networks
   190  				// but it ought to do something sensible anyway.
   191  				`${cidrnetmask("1::/64")}`,
   192  				"ffff:ffff:ffff:ffff::",
   193  				false,
   194  			},
   195  			{
   196  				`${cidrnetmask("not-a-cidr")}`,
   197  				nil,
   198  				true, // not a valid CIDR mask
   199  			},
   200  			{
   201  				`${cidrnetmask("10.256.0.0/8")}`,
   202  				nil,
   203  				true, // can't have an octet >255
   204  			},
   205  		},
   206  	})
   207  }
   208  
   209  func TestInterpolateFuncCidrSubnet(t *testing.T) {
   210  	testFunction(t, testFunctionConfig{
   211  		Cases: []testFunctionCase{
   212  			{
   213  				`${cidrsubnet("192.168.2.0/20", 4, 6)}`,
   214  				"192.168.6.0/24",
   215  				false,
   216  			},
   217  			{
   218  				`${cidrsubnet("fe80::/48", 16, 6)}`,
   219  				"fe80:0:0:6::/64",
   220  				false,
   221  			},
   222  			{
   223  				// IPv4 address encoded in IPv6 syntax gets normalized
   224  				`${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`,
   225  				"192.168.6.0/24",
   226  				false,
   227  			},
   228  			{
   229  				`${cidrsubnet("192.168.0.0/30", 4, 6)}`,
   230  				nil,
   231  				true, // not enough bits left
   232  			},
   233  			{
   234  				`${cidrsubnet("192.168.0.0/16", 2, 16)}`,
   235  				nil,
   236  				true, // can't encode 16 in 2 bits
   237  			},
   238  			{
   239  				`${cidrsubnet("not-a-cidr", 4, 6)}`,
   240  				nil,
   241  				true, // not a valid CIDR mask
   242  			},
   243  			{
   244  				`${cidrsubnet("10.256.0.0/8", 4, 6)}`,
   245  				nil,
   246  				true, // can't have an octet >255
   247  			},
   248  		},
   249  	})
   250  }
   251  
   252  func TestInterpolateFuncCoalesce(t *testing.T) {
   253  	testFunction(t, testFunctionConfig{
   254  		Cases: []testFunctionCase{
   255  			{
   256  				`${coalesce("first", "second", "third")}`,
   257  				"first",
   258  				false,
   259  			},
   260  			{
   261  				`${coalesce("", "second", "third")}`,
   262  				"second",
   263  				false,
   264  			},
   265  			{
   266  				`${coalesce("", "", "")}`,
   267  				"",
   268  				false,
   269  			},
   270  			{
   271  				`${coalesce("foo")}`,
   272  				nil,
   273  				true,
   274  			},
   275  		},
   276  	})
   277  }
   278  
   279  func TestInterpolateFuncConcat(t *testing.T) {
   280  	testFunction(t, testFunctionConfig{
   281  		Cases: []testFunctionCase{
   282  			// String + list
   283  			{
   284  				`${concat("a", split(",", "b,c"))}`,
   285  				[]interface{}{"a", "b", "c"},
   286  				false,
   287  			},
   288  
   289  			// List + string
   290  			{
   291  				`${concat(split(",", "a,b"), "c")}`,
   292  				[]interface{}{"a", "b", "c"},
   293  				false,
   294  			},
   295  
   296  			// Single list
   297  			{
   298  				`${concat(split(",", ",foo,"))}`,
   299  				[]interface{}{"", "foo", ""},
   300  				false,
   301  			},
   302  			{
   303  				`${concat(split(",", "a,b,c"))}`,
   304  				[]interface{}{"a", "b", "c"},
   305  				false,
   306  			},
   307  
   308  			// Two lists
   309  			{
   310  				`${concat(split(",", "a,b,c"), split(",", "d,e"))}`,
   311  				[]interface{}{"a", "b", "c", "d", "e"},
   312  				false,
   313  			},
   314  			// Two lists with different separators
   315  			{
   316  				`${concat(split(",", "a,b,c"), split(" ", "d e"))}`,
   317  				[]interface{}{"a", "b", "c", "d", "e"},
   318  				false,
   319  			},
   320  
   321  			// More lists
   322  			{
   323  				`${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`,
   324  				[]interface{}{"a", "b", "c", "d", "e", "f", "0", "1"},
   325  				false,
   326  			},
   327  
   328  			// list vars
   329  			{
   330  				`${concat("${var.list}", "${var.list}")}`,
   331  				[]interface{}{"a", "b", "a", "b"},
   332  				false,
   333  			},
   334  			// lists of lists
   335  			{
   336  				`${concat("${var.lists}", "${var.lists}")}`,
   337  				[]interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}},
   338  				false,
   339  			},
   340  
   341  			// lists of maps
   342  			{
   343  				`${concat("${var.maps}", "${var.maps}")}`,
   344  				[]interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}},
   345  				false,
   346  			},
   347  
   348  			// mismatched types
   349  			{
   350  				`${concat("${var.lists}", "${var.maps}")}`,
   351  				nil,
   352  				true,
   353  			},
   354  		},
   355  		Vars: map[string]ast.Variable{
   356  			"var.list": {
   357  				Type: ast.TypeList,
   358  				Value: []ast.Variable{
   359  					{
   360  						Type:  ast.TypeString,
   361  						Value: "a",
   362  					},
   363  					{
   364  						Type:  ast.TypeString,
   365  						Value: "b",
   366  					},
   367  				},
   368  			},
   369  			"var.lists": {
   370  				Type: ast.TypeList,
   371  				Value: []ast.Variable{
   372  					{
   373  						Type: ast.TypeList,
   374  						Value: []ast.Variable{
   375  							{
   376  								Type:  ast.TypeString,
   377  								Value: "c",
   378  							},
   379  							{
   380  								Type:  ast.TypeString,
   381  								Value: "d",
   382  							},
   383  						},
   384  					},
   385  				},
   386  			},
   387  			"var.maps": {
   388  				Type: ast.TypeList,
   389  				Value: []ast.Variable{
   390  					{
   391  						Type: ast.TypeMap,
   392  						Value: map[string]ast.Variable{
   393  							"key1": {
   394  								Type:  ast.TypeString,
   395  								Value: "a",
   396  							},
   397  							"key2": {
   398  								Type:  ast.TypeString,
   399  								Value: "b",
   400  							},
   401  						},
   402  					},
   403  				},
   404  			},
   405  		},
   406  	})
   407  }
   408  
   409  func TestInterpolateFuncDistinct(t *testing.T) {
   410  	testFunction(t, testFunctionConfig{
   411  		Cases: []testFunctionCase{
   412  			// 3 duplicates
   413  			{
   414  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`,
   415  				[]interface{}{"user1", "user2", "user3"},
   416  				false,
   417  			},
   418  			// 1 duplicate
   419  			{
   420  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`,
   421  				[]interface{}{"user1", "user2", "user3", "user4"},
   422  				false,
   423  			},
   424  			// too many args
   425  			{
   426  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`,
   427  				nil,
   428  				true,
   429  			},
   430  		},
   431  	})
   432  }
   433  
   434  func TestInterpolateFuncFile(t *testing.T) {
   435  	tf, err := ioutil.TempFile("", "tf")
   436  	if err != nil {
   437  		t.Fatalf("err: %s", err)
   438  	}
   439  	path := tf.Name()
   440  	tf.Write([]byte("foo"))
   441  	tf.Close()
   442  	defer os.Remove(path)
   443  
   444  	testFunction(t, testFunctionConfig{
   445  		Cases: []testFunctionCase{
   446  			{
   447  				fmt.Sprintf(`${file("%s")}`, path),
   448  				"foo",
   449  				false,
   450  			},
   451  
   452  			// Invalid path
   453  			{
   454  				`${file("/i/dont/exist")}`,
   455  				nil,
   456  				true,
   457  			},
   458  
   459  			// Too many args
   460  			{
   461  				`${file("foo", "bar")}`,
   462  				nil,
   463  				true,
   464  			},
   465  		},
   466  	})
   467  }
   468  
   469  func TestInterpolateFuncFormat(t *testing.T) {
   470  	testFunction(t, testFunctionConfig{
   471  		Cases: []testFunctionCase{
   472  			{
   473  				`${format("hello")}`,
   474  				"hello",
   475  				false,
   476  			},
   477  
   478  			{
   479  				`${format("hello %s", "world")}`,
   480  				"hello world",
   481  				false,
   482  			},
   483  
   484  			{
   485  				`${format("hello %d", 42)}`,
   486  				"hello 42",
   487  				false,
   488  			},
   489  
   490  			{
   491  				`${format("hello %05d", 42)}`,
   492  				"hello 00042",
   493  				false,
   494  			},
   495  
   496  			{
   497  				`${format("hello %05d", 12345)}`,
   498  				"hello 12345",
   499  				false,
   500  			},
   501  		},
   502  	})
   503  }
   504  
   505  func TestInterpolateFuncFormatList(t *testing.T) {
   506  	testFunction(t, testFunctionConfig{
   507  		Cases: []testFunctionCase{
   508  			// formatlist requires at least one list
   509  			{
   510  				`${formatlist("hello")}`,
   511  				nil,
   512  				true,
   513  			},
   514  			{
   515  				`${formatlist("hello %s", "world")}`,
   516  				nil,
   517  				true,
   518  			},
   519  			// formatlist applies to each list element in turn
   520  			{
   521  				`${formatlist("<%s>", split(",", "A,B"))}`,
   522  				[]interface{}{"<A>", "<B>"},
   523  				false,
   524  			},
   525  			// formatlist repeats scalar elements
   526  			{
   527  				`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
   528  				"x=A, x=B, x=C",
   529  				false,
   530  			},
   531  			// Multiple lists are walked in parallel
   532  			{
   533  				`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
   534  				"A=1, B=2, C=3",
   535  				false,
   536  			},
   537  			// Mismatched list lengths generate an error
   538  			{
   539  				`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
   540  				nil,
   541  				true,
   542  			},
   543  			// Works with lists of length 1 [GH-2240]
   544  			{
   545  				`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
   546  				[]interface{}{"demo-rest-elb.id"},
   547  				false,
   548  			},
   549  		},
   550  	})
   551  }
   552  
   553  func TestInterpolateFuncIndex(t *testing.T) {
   554  	testFunction(t, testFunctionConfig{
   555  		Vars: map[string]ast.Variable{
   556  			"var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
   557  			"var.list2": interfaceToVariableSwallowError([]string{"foo"}),
   558  			"var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}),
   559  		},
   560  		Cases: []testFunctionCase{
   561  			{
   562  				`${index("test", "")}`,
   563  				nil,
   564  				true,
   565  			},
   566  
   567  			{
   568  				`${index(var.list1, "foo")}`,
   569  				nil,
   570  				true,
   571  			},
   572  
   573  			{
   574  				`${index(var.list2, "foo")}`,
   575  				"0",
   576  				false,
   577  			},
   578  
   579  			{
   580  				`${index(var.list3, "bar")}`,
   581  				"2",
   582  				false,
   583  			},
   584  		},
   585  	})
   586  }
   587  
   588  func TestInterpolateFuncJoin(t *testing.T) {
   589  	testFunction(t, testFunctionConfig{
   590  		Vars: map[string]ast.Variable{
   591  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo"}),
   592  			"var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}),
   593  		},
   594  		Cases: []testFunctionCase{
   595  			{
   596  				`${join(",")}`,
   597  				nil,
   598  				true,
   599  			},
   600  
   601  			{
   602  				`${join(",", var.a_list)}`,
   603  				"foo",
   604  				false,
   605  			},
   606  
   607  			{
   608  				`${join(".", var.a_longer_list)}`,
   609  				"foo.bar.baz",
   610  				false,
   611  			},
   612  		},
   613  	})
   614  }
   615  
   616  func TestInterpolateFuncJSONEncode(t *testing.T) {
   617  	testFunction(t, testFunctionConfig{
   618  		Vars: map[string]ast.Variable{
   619  			"easy": ast.Variable{
   620  				Value: "test",
   621  				Type:  ast.TypeString,
   622  			},
   623  			"hard": ast.Variable{
   624  				Value: " foo \\ \n \t \" bar ",
   625  				Type:  ast.TypeString,
   626  			},
   627  			"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
   628  			// XXX can't use InterfaceToVariable as it converts empty slice into empty
   629  			// map.
   630  			"emptylist": ast.Variable{
   631  				Value: []ast.Variable{},
   632  				Type:  ast.TypeList,
   633  			},
   634  			"map": interfaceToVariableSwallowError(map[string]string{
   635  				"foo":     "bar",
   636  				"ba \n z": "q\\x",
   637  			}),
   638  			"emptymap": interfaceToVariableSwallowError(map[string]string{}),
   639  
   640  			// Not yet supported (but it would be nice)
   641  			"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
   642  			"nestedmap":  interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
   643  		},
   644  		Cases: []testFunctionCase{
   645  			{
   646  				`${jsonencode("test")}`,
   647  				`"test"`,
   648  				false,
   649  			},
   650  			{
   651  				`${jsonencode(easy)}`,
   652  				`"test"`,
   653  				false,
   654  			},
   655  			{
   656  				`${jsonencode(hard)}`,
   657  				`" foo \\ \n \t \" bar "`,
   658  				false,
   659  			},
   660  			{
   661  				`${jsonencode("")}`,
   662  				`""`,
   663  				false,
   664  			},
   665  			{
   666  				`${jsonencode()}`,
   667  				nil,
   668  				true,
   669  			},
   670  			{
   671  				`${jsonencode(list)}`,
   672  				`["foo","bar\tbaz"]`,
   673  				false,
   674  			},
   675  			{
   676  				`${jsonencode(emptylist)}`,
   677  				`[]`,
   678  				false,
   679  			},
   680  			{
   681  				`${jsonencode(map)}`,
   682  				`{"ba \n z":"q\\x","foo":"bar"}`,
   683  				false,
   684  			},
   685  			{
   686  				`${jsonencode(emptymap)}`,
   687  				`{}`,
   688  				false,
   689  			},
   690  			{
   691  				`${jsonencode(nestedlist)}`,
   692  				nil,
   693  				true,
   694  			},
   695  			{
   696  				`${jsonencode(nestedmap)}`,
   697  				nil,
   698  				true,
   699  			},
   700  		},
   701  	})
   702  }
   703  
   704  func TestInterpolateFuncReplace(t *testing.T) {
   705  	testFunction(t, testFunctionConfig{
   706  		Cases: []testFunctionCase{
   707  			// Regular search and replace
   708  			{
   709  				`${replace("hello", "hel", "bel")}`,
   710  				"bello",
   711  				false,
   712  			},
   713  
   714  			// Search string doesn't match
   715  			{
   716  				`${replace("hello", "nope", "bel")}`,
   717  				"hello",
   718  				false,
   719  			},
   720  
   721  			// Regular expression
   722  			{
   723  				`${replace("hello", "/l/", "L")}`,
   724  				"heLLo",
   725  				false,
   726  			},
   727  
   728  			{
   729  				`${replace("helo", "/(l)/", "$1$1")}`,
   730  				"hello",
   731  				false,
   732  			},
   733  
   734  			// Bad regexp
   735  			{
   736  				`${replace("helo", "/(l/", "$1$1")}`,
   737  				nil,
   738  				true,
   739  			},
   740  		},
   741  	})
   742  }
   743  
   744  func TestInterpolateFuncLength(t *testing.T) {
   745  	testFunction(t, testFunctionConfig{
   746  		Cases: []testFunctionCase{
   747  			// Raw strings
   748  			{
   749  				`${length("")}`,
   750  				"0",
   751  				false,
   752  			},
   753  			{
   754  				`${length("a")}`,
   755  				"1",
   756  				false,
   757  			},
   758  			{
   759  				`${length(" ")}`,
   760  				"1",
   761  				false,
   762  			},
   763  			{
   764  				`${length(" a ,")}`,
   765  				"4",
   766  				false,
   767  			},
   768  			{
   769  				`${length("aaa")}`,
   770  				"3",
   771  				false,
   772  			},
   773  
   774  			// Lists
   775  			{
   776  				`${length(split(",", "a"))}`,
   777  				"1",
   778  				false,
   779  			},
   780  			{
   781  				`${length(split(",", "foo,"))}`,
   782  				"2",
   783  				false,
   784  			},
   785  			{
   786  				`${length(split(",", ",foo,"))}`,
   787  				"3",
   788  				false,
   789  			},
   790  			{
   791  				`${length(split(",", "foo,bar"))}`,
   792  				"2",
   793  				false,
   794  			},
   795  			{
   796  				`${length(split(".", "one.two.three.four.five"))}`,
   797  				"5",
   798  				false,
   799  			},
   800  			// Want length 0 if we split an empty string then compact
   801  			{
   802  				`${length(compact(split(",", "")))}`,
   803  				"0",
   804  				false,
   805  			},
   806  		},
   807  	})
   808  }
   809  
   810  func TestInterpolateFuncSignum(t *testing.T) {
   811  	testFunction(t, testFunctionConfig{
   812  		Cases: []testFunctionCase{
   813  			{
   814  				`${signum()}`,
   815  				nil,
   816  				true,
   817  			},
   818  
   819  			{
   820  				`${signum("")}`,
   821  				nil,
   822  				true,
   823  			},
   824  
   825  			{
   826  				`${signum(0)}`,
   827  				"0",
   828  				false,
   829  			},
   830  
   831  			{
   832  				`${signum(15)}`,
   833  				"1",
   834  				false,
   835  			},
   836  
   837  			{
   838  				`${signum(-29)}`,
   839  				"-1",
   840  				false,
   841  			},
   842  		},
   843  	})
   844  }
   845  
   846  func TestInterpolateFuncSort(t *testing.T) {
   847  	testFunction(t, testFunctionConfig{
   848  		Vars: map[string]ast.Variable{
   849  			"var.strings": ast.Variable{
   850  				Type: ast.TypeList,
   851  				Value: []ast.Variable{
   852  					{Type: ast.TypeString, Value: "c"},
   853  					{Type: ast.TypeString, Value: "a"},
   854  					{Type: ast.TypeString, Value: "b"},
   855  				},
   856  			},
   857  			"var.notstrings": ast.Variable{
   858  				Type: ast.TypeList,
   859  				Value: []ast.Variable{
   860  					{Type: ast.TypeList, Value: []ast.Variable{}},
   861  					{Type: ast.TypeString, Value: "b"},
   862  				},
   863  			},
   864  		},
   865  		Cases: []testFunctionCase{
   866  			{
   867  				`${sort(var.strings)}`,
   868  				[]interface{}{"a", "b", "c"},
   869  				false,
   870  			},
   871  			{
   872  				`${sort(var.notstrings)}`,
   873  				nil,
   874  				true,
   875  			},
   876  		},
   877  	})
   878  }
   879  
   880  func TestInterpolateFuncSplit(t *testing.T) {
   881  	testFunction(t, testFunctionConfig{
   882  		Cases: []testFunctionCase{
   883  			{
   884  				`${split(",")}`,
   885  				nil,
   886  				true,
   887  			},
   888  
   889  			{
   890  				`${split(",", "")}`,
   891  				[]interface{}{""},
   892  				false,
   893  			},
   894  
   895  			{
   896  				`${split(",", "foo")}`,
   897  				[]interface{}{"foo"},
   898  				false,
   899  			},
   900  
   901  			{
   902  				`${split(",", ",,,")}`,
   903  				[]interface{}{"", "", "", ""},
   904  				false,
   905  			},
   906  
   907  			{
   908  				`${split(",", "foo,")}`,
   909  				[]interface{}{"foo", ""},
   910  				false,
   911  			},
   912  
   913  			{
   914  				`${split(",", ",foo,")}`,
   915  				[]interface{}{"", "foo", ""},
   916  				false,
   917  			},
   918  
   919  			{
   920  				`${split(".", "foo.bar.baz")}`,
   921  				[]interface{}{"foo", "bar", "baz"},
   922  				false,
   923  			},
   924  		},
   925  	})
   926  }
   927  
   928  func TestInterpolateFuncLookup(t *testing.T) {
   929  	testFunction(t, testFunctionConfig{
   930  		Vars: map[string]ast.Variable{
   931  			"var.foo": ast.Variable{
   932  				Type: ast.TypeMap,
   933  				Value: map[string]ast.Variable{
   934  					"bar": ast.Variable{
   935  						Type:  ast.TypeString,
   936  						Value: "baz",
   937  					},
   938  				},
   939  			},
   940  		},
   941  		Cases: []testFunctionCase{
   942  			{
   943  				`${lookup(var.foo, "bar")}`,
   944  				"baz",
   945  				false,
   946  			},
   947  
   948  			// Invalid key
   949  			{
   950  				`${lookup(var.foo, "baz")}`,
   951  				nil,
   952  				true,
   953  			},
   954  
   955  			// Supplied default with valid key
   956  			{
   957  				`${lookup(var.foo, "bar", "")}`,
   958  				"baz",
   959  				false,
   960  			},
   961  
   962  			// Supplied default with invalid key
   963  			{
   964  				`${lookup(var.foo, "zip", "")}`,
   965  				"",
   966  				false,
   967  			},
   968  
   969  			// Too many args
   970  			{
   971  				`${lookup(var.foo, "bar", "", "abc")}`,
   972  				nil,
   973  				true,
   974  			},
   975  
   976  			// Non-empty default
   977  			{
   978  				`${lookup(var.foo, "zap", "xyz")}`,
   979  				"xyz",
   980  				false,
   981  			},
   982  		},
   983  	})
   984  }
   985  
   986  func TestInterpolateFuncKeys(t *testing.T) {
   987  	testFunction(t, testFunctionConfig{
   988  		Vars: map[string]ast.Variable{
   989  			"var.foo": ast.Variable{
   990  				Type: ast.TypeMap,
   991  				Value: map[string]ast.Variable{
   992  					"bar": ast.Variable{
   993  						Value: "baz",
   994  						Type:  ast.TypeString,
   995  					},
   996  					"qux": ast.Variable{
   997  						Value: "quack",
   998  						Type:  ast.TypeString,
   999  					},
  1000  				},
  1001  			},
  1002  			"var.str": ast.Variable{
  1003  				Value: "astring",
  1004  				Type:  ast.TypeString,
  1005  			},
  1006  		},
  1007  		Cases: []testFunctionCase{
  1008  			{
  1009  				`${keys(var.foo)}`,
  1010  				[]interface{}{"bar", "qux"},
  1011  				false,
  1012  			},
  1013  
  1014  			// Invalid key
  1015  			{
  1016  				`${keys(var.not)}`,
  1017  				nil,
  1018  				true,
  1019  			},
  1020  
  1021  			// Too many args
  1022  			{
  1023  				`${keys(var.foo, "bar")}`,
  1024  				nil,
  1025  				true,
  1026  			},
  1027  
  1028  			// Not a map
  1029  			{
  1030  				`${keys(var.str)}`,
  1031  				nil,
  1032  				true,
  1033  			},
  1034  		},
  1035  	})
  1036  }
  1037  
  1038  // Confirm that keys return in sorted order, and values return in the order of
  1039  // their sorted keys.
  1040  func TestInterpolateFuncKeyValOrder(t *testing.T) {
  1041  	testFunction(t, testFunctionConfig{
  1042  		Vars: map[string]ast.Variable{
  1043  			"var.foo": ast.Variable{
  1044  				Type: ast.TypeMap,
  1045  				Value: map[string]ast.Variable{
  1046  					"D": ast.Variable{
  1047  						Value: "2",
  1048  						Type:  ast.TypeString,
  1049  					},
  1050  					"C": ast.Variable{
  1051  						Value: "Y",
  1052  						Type:  ast.TypeString,
  1053  					},
  1054  					"A": ast.Variable{
  1055  						Value: "X",
  1056  						Type:  ast.TypeString,
  1057  					},
  1058  					"10": ast.Variable{
  1059  						Value: "Z",
  1060  						Type:  ast.TypeString,
  1061  					},
  1062  					"1": ast.Variable{
  1063  						Value: "4",
  1064  						Type:  ast.TypeString,
  1065  					},
  1066  					"3": ast.Variable{
  1067  						Value: "W",
  1068  						Type:  ast.TypeString,
  1069  					},
  1070  				},
  1071  			},
  1072  		},
  1073  		Cases: []testFunctionCase{
  1074  			{
  1075  				`${keys(var.foo)}`,
  1076  				[]interface{}{"1", "10", "3", "A", "C", "D"},
  1077  				false,
  1078  			},
  1079  
  1080  			{
  1081  				`${values(var.foo)}`,
  1082  				[]interface{}{"4", "Z", "W", "X", "Y", "2"},
  1083  				false,
  1084  			},
  1085  		},
  1086  	})
  1087  }
  1088  
  1089  func TestInterpolateFuncValues(t *testing.T) {
  1090  	testFunction(t, testFunctionConfig{
  1091  		Vars: map[string]ast.Variable{
  1092  			"var.foo": ast.Variable{
  1093  				Type: ast.TypeMap,
  1094  				Value: map[string]ast.Variable{
  1095  					"bar": ast.Variable{
  1096  						Value: "quack",
  1097  						Type:  ast.TypeString,
  1098  					},
  1099  					"qux": ast.Variable{
  1100  						Value: "baz",
  1101  						Type:  ast.TypeString,
  1102  					},
  1103  				},
  1104  			},
  1105  			"var.str": ast.Variable{
  1106  				Value: "astring",
  1107  				Type:  ast.TypeString,
  1108  			},
  1109  		},
  1110  		Cases: []testFunctionCase{
  1111  			{
  1112  				`${values(var.foo)}`,
  1113  				[]interface{}{"quack", "baz"},
  1114  				false,
  1115  			},
  1116  
  1117  			// Invalid key
  1118  			{
  1119  				`${values(var.not)}`,
  1120  				nil,
  1121  				true,
  1122  			},
  1123  
  1124  			// Too many args
  1125  			{
  1126  				`${values(var.foo, "bar")}`,
  1127  				nil,
  1128  				true,
  1129  			},
  1130  
  1131  			// Not a map
  1132  			{
  1133  				`${values(var.str)}`,
  1134  				nil,
  1135  				true,
  1136  			},
  1137  		},
  1138  	})
  1139  }
  1140  
  1141  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
  1142  	variable, _ := hil.InterfaceToVariable(input)
  1143  	return variable
  1144  }
  1145  
  1146  func TestInterpolateFuncElement(t *testing.T) {
  1147  	testFunction(t, testFunctionConfig{
  1148  		Vars: map[string]ast.Variable{
  1149  			"var.a_list":       interfaceToVariableSwallowError([]string{"foo", "baz"}),
  1150  			"var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}),
  1151  			"var.empty_list":   interfaceToVariableSwallowError([]interface{}{}),
  1152  		},
  1153  		Cases: []testFunctionCase{
  1154  			{
  1155  				`${element(var.a_list, "1")}`,
  1156  				"baz",
  1157  				false,
  1158  			},
  1159  
  1160  			{
  1161  				`${element(var.a_short_list, "0")}`,
  1162  				"foo",
  1163  				false,
  1164  			},
  1165  
  1166  			// Invalid index should wrap vs. out-of-bounds
  1167  			{
  1168  				`${element(var.a_list, "2")}`,
  1169  				"foo",
  1170  				false,
  1171  			},
  1172  
  1173  			// Negative number should fail
  1174  			{
  1175  				`${element(var.a_short_list, "-1")}`,
  1176  				nil,
  1177  				true,
  1178  			},
  1179  
  1180  			// Empty list should fail
  1181  			{
  1182  				`${element(var.empty_list, 0)}`,
  1183  				nil,
  1184  				true,
  1185  			},
  1186  
  1187  			// Too many args
  1188  			{
  1189  				`${element(var.a_list, "0", "2")}`,
  1190  				nil,
  1191  				true,
  1192  			},
  1193  		},
  1194  	})
  1195  }
  1196  
  1197  func TestInterpolateFuncBase64Encode(t *testing.T) {
  1198  	testFunction(t, testFunctionConfig{
  1199  		Cases: []testFunctionCase{
  1200  			// Regular base64 encoding
  1201  			{
  1202  				`${base64encode("abc123!?$*&()'-=@~")}`,
  1203  				"YWJjMTIzIT8kKiYoKSctPUB+",
  1204  				false,
  1205  			},
  1206  		},
  1207  	})
  1208  }
  1209  
  1210  func TestInterpolateFuncBase64Decode(t *testing.T) {
  1211  	testFunction(t, testFunctionConfig{
  1212  		Cases: []testFunctionCase{
  1213  			// Regular base64 decoding
  1214  			{
  1215  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
  1216  				"abc123!?$*&()'-=@~",
  1217  				false,
  1218  			},
  1219  
  1220  			// Invalid base64 data decoding
  1221  			{
  1222  				`${base64decode("this-is-an-invalid-base64-data")}`,
  1223  				nil,
  1224  				true,
  1225  			},
  1226  		},
  1227  	})
  1228  }
  1229  
  1230  func TestInterpolateFuncLower(t *testing.T) {
  1231  	testFunction(t, testFunctionConfig{
  1232  		Cases: []testFunctionCase{
  1233  			{
  1234  				`${lower("HELLO")}`,
  1235  				"hello",
  1236  				false,
  1237  			},
  1238  
  1239  			{
  1240  				`${lower("")}`,
  1241  				"",
  1242  				false,
  1243  			},
  1244  
  1245  			{
  1246  				`${lower()}`,
  1247  				nil,
  1248  				true,
  1249  			},
  1250  		},
  1251  	})
  1252  }
  1253  
  1254  func TestInterpolateFuncUpper(t *testing.T) {
  1255  	testFunction(t, testFunctionConfig{
  1256  		Cases: []testFunctionCase{
  1257  			{
  1258  				`${upper("hello")}`,
  1259  				"HELLO",
  1260  				false,
  1261  			},
  1262  
  1263  			{
  1264  				`${upper("")}`,
  1265  				"",
  1266  				false,
  1267  			},
  1268  
  1269  			{
  1270  				`${upper()}`,
  1271  				nil,
  1272  				true,
  1273  			},
  1274  		},
  1275  	})
  1276  }
  1277  
  1278  func TestInterpolateFuncSha1(t *testing.T) {
  1279  	testFunction(t, testFunctionConfig{
  1280  		Cases: []testFunctionCase{
  1281  			{
  1282  				`${sha1("test")}`,
  1283  				"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
  1284  				false,
  1285  			},
  1286  		},
  1287  	})
  1288  }
  1289  
  1290  func TestInterpolateFuncSha256(t *testing.T) {
  1291  	testFunction(t, testFunctionConfig{
  1292  		Cases: []testFunctionCase{
  1293  			{ // hexadecimal representation of sha256 sum
  1294  				`${sha256("test")}`,
  1295  				"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  1296  				false,
  1297  			},
  1298  		},
  1299  	})
  1300  }
  1301  
  1302  func TestInterpolateFuncTrimSpace(t *testing.T) {
  1303  	testFunction(t, testFunctionConfig{
  1304  		Cases: []testFunctionCase{
  1305  			{
  1306  				`${trimspace(" test ")}`,
  1307  				"test",
  1308  				false,
  1309  			},
  1310  		},
  1311  	})
  1312  }
  1313  
  1314  func TestInterpolateFuncBase64Sha256(t *testing.T) {
  1315  	testFunction(t, testFunctionConfig{
  1316  		Cases: []testFunctionCase{
  1317  			{
  1318  				`${base64sha256("test")}`,
  1319  				"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
  1320  				false,
  1321  			},
  1322  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  1323  				`${base64encode(sha256("test"))}`,
  1324  				"OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==",
  1325  				false,
  1326  			},
  1327  		},
  1328  	})
  1329  }
  1330  
  1331  func TestInterpolateFuncMd5(t *testing.T) {
  1332  	testFunction(t, testFunctionConfig{
  1333  		Cases: []testFunctionCase{
  1334  			{
  1335  				`${md5("tada")}`,
  1336  				"ce47d07243bb6eaf5e1322c81baf9bbf",
  1337  				false,
  1338  			},
  1339  			{ // Confirm that we're not trimming any whitespaces
  1340  				`${md5(" tada ")}`,
  1341  				"aadf191a583e53062de2d02c008141c4",
  1342  				false,
  1343  			},
  1344  			{ // We accept empty string too
  1345  				`${md5("")}`,
  1346  				"d41d8cd98f00b204e9800998ecf8427e",
  1347  				false,
  1348  			},
  1349  		},
  1350  	})
  1351  }
  1352  
  1353  func TestInterpolateFuncUUID(t *testing.T) {
  1354  	results := make(map[string]bool)
  1355  
  1356  	for i := 0; i < 100; i++ {
  1357  		ast, err := hil.Parse("${uuid()}")
  1358  		if err != nil {
  1359  			t.Fatalf("err: %s", err)
  1360  		}
  1361  
  1362  		result, err := hil.Eval(ast, langEvalConfig(nil))
  1363  		if err != nil {
  1364  			t.Fatalf("err: %s", err)
  1365  		}
  1366  
  1367  		if results[result.Value.(string)] {
  1368  			t.Fatalf("Got unexpected duplicate uuid: %s", result.Value)
  1369  		}
  1370  
  1371  		results[result.Value.(string)] = true
  1372  	}
  1373  }
  1374  
  1375  type testFunctionConfig struct {
  1376  	Cases []testFunctionCase
  1377  	Vars  map[string]ast.Variable
  1378  }
  1379  
  1380  type testFunctionCase struct {
  1381  	Input  string
  1382  	Result interface{}
  1383  	Error  bool
  1384  }
  1385  
  1386  func testFunction(t *testing.T, config testFunctionConfig) {
  1387  	for i, tc := range config.Cases {
  1388  		ast, err := hil.Parse(tc.Input)
  1389  		if err != nil {
  1390  			t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err)
  1391  		}
  1392  
  1393  		result, err := hil.Eval(ast, langEvalConfig(config.Vars))
  1394  		if err != nil != tc.Error {
  1395  			t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err)
  1396  		}
  1397  
  1398  		if !reflect.DeepEqual(result.Value, tc.Result) {
  1399  			t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v",
  1400  				i, tc.Input, result.Value, tc.Result)
  1401  		}
  1402  	}
  1403  }