github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/config/interpolate_funcs_test.go (about)

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