github.com/sfdevops1/terrra4orm@v0.11.12-beta1/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  	"golang.org/x/crypto/bcrypt"
    17  )
    18  
    19  func TestInterpolateFuncZipMap(t *testing.T) {
    20  	testFunction(t, testFunctionConfig{
    21  		Cases: []testFunctionCase{
    22  			{
    23  				`${zipmap(var.list, var.list2)}`,
    24  				map[string]interface{}{
    25  					"Hello": "bar",
    26  					"World": "baz",
    27  				},
    28  				false,
    29  			},
    30  			{
    31  				`${zipmap(var.list, var.nonstrings)}`,
    32  				map[string]interface{}{
    33  					"Hello": []interface{}{"bar", "baz"},
    34  					"World": []interface{}{"boo", "foo"},
    35  				},
    36  				false,
    37  			},
    38  			{
    39  				`${zipmap(var.nonstrings, var.list2)}`,
    40  				nil,
    41  				true,
    42  			},
    43  			{
    44  				`${zipmap(var.list, var.differentlengthlist)}`,
    45  				nil,
    46  				true,
    47  			},
    48  		},
    49  		Vars: map[string]ast.Variable{
    50  			"var.list": {
    51  				Type: ast.TypeList,
    52  				Value: []ast.Variable{
    53  					{
    54  						Type:  ast.TypeString,
    55  						Value: "Hello",
    56  					},
    57  					{
    58  						Type:  ast.TypeString,
    59  						Value: "World",
    60  					},
    61  				},
    62  			},
    63  			"var.list2": {
    64  				Type: ast.TypeList,
    65  				Value: []ast.Variable{
    66  					{
    67  						Type:  ast.TypeString,
    68  						Value: "bar",
    69  					},
    70  					{
    71  						Type:  ast.TypeString,
    72  						Value: "baz",
    73  					},
    74  				},
    75  			},
    76  			"var.differentlengthlist": {
    77  				Type: ast.TypeList,
    78  				Value: []ast.Variable{
    79  					{
    80  						Type:  ast.TypeString,
    81  						Value: "bar",
    82  					},
    83  					{
    84  						Type:  ast.TypeString,
    85  						Value: "baz",
    86  					},
    87  					{
    88  						Type:  ast.TypeString,
    89  						Value: "boo",
    90  					},
    91  				},
    92  			},
    93  			"var.nonstrings": {
    94  				Type: ast.TypeList,
    95  				Value: []ast.Variable{
    96  					{
    97  						Type: ast.TypeList,
    98  						Value: []ast.Variable{
    99  							{
   100  								Type:  ast.TypeString,
   101  								Value: "bar",
   102  							},
   103  							{
   104  								Type:  ast.TypeString,
   105  								Value: "baz",
   106  							},
   107  						},
   108  					},
   109  					{
   110  						Type: ast.TypeList,
   111  						Value: []ast.Variable{
   112  							{
   113  								Type:  ast.TypeString,
   114  								Value: "boo",
   115  							},
   116  							{
   117  								Type:  ast.TypeString,
   118  								Value: "foo",
   119  							},
   120  						},
   121  					},
   122  				},
   123  			},
   124  		},
   125  	})
   126  }
   127  
   128  func TestInterpolateFuncList(t *testing.T) {
   129  	testFunction(t, testFunctionConfig{
   130  		Cases: []testFunctionCase{
   131  			// empty input returns empty list
   132  			{
   133  				`${list()}`,
   134  				[]interface{}{},
   135  				false,
   136  			},
   137  
   138  			// single input returns list of length 1
   139  			{
   140  				`${list("hello")}`,
   141  				[]interface{}{"hello"},
   142  				false,
   143  			},
   144  
   145  			// two inputs returns list of length 2
   146  			{
   147  				`${list("hello", "world")}`,
   148  				[]interface{}{"hello", "world"},
   149  				false,
   150  			},
   151  
   152  			// not a string input gives error
   153  			{
   154  				`${list("hello", 42)}`,
   155  				nil,
   156  				true,
   157  			},
   158  
   159  			// list of lists
   160  			{
   161  				`${list("${var.list}", "${var.list2}")}`,
   162  				[]interface{}{[]interface{}{"Hello", "World"}, []interface{}{"bar", "baz"}},
   163  				false,
   164  			},
   165  
   166  			// list of maps
   167  			{
   168  				`${list("${var.map}", "${var.map2}")}`,
   169  				[]interface{}{map[string]interface{}{"key": "bar"}, map[string]interface{}{"key2": "baz"}},
   170  				false,
   171  			},
   172  
   173  			// error on a heterogeneous list
   174  			{
   175  				`${list("first", "${var.list}")}`,
   176  				nil,
   177  				true,
   178  			},
   179  		},
   180  		Vars: map[string]ast.Variable{
   181  			"var.list": {
   182  				Type: ast.TypeList,
   183  				Value: []ast.Variable{
   184  					{
   185  						Type:  ast.TypeString,
   186  						Value: "Hello",
   187  					},
   188  					{
   189  						Type:  ast.TypeString,
   190  						Value: "World",
   191  					},
   192  				},
   193  			},
   194  			"var.list2": {
   195  				Type: ast.TypeList,
   196  				Value: []ast.Variable{
   197  					{
   198  						Type:  ast.TypeString,
   199  						Value: "bar",
   200  					},
   201  					{
   202  						Type:  ast.TypeString,
   203  						Value: "baz",
   204  					},
   205  				},
   206  			},
   207  
   208  			"var.map": {
   209  				Type: ast.TypeMap,
   210  				Value: map[string]ast.Variable{
   211  					"key": {
   212  						Type:  ast.TypeString,
   213  						Value: "bar",
   214  					},
   215  				},
   216  			},
   217  			"var.map2": {
   218  				Type: ast.TypeMap,
   219  				Value: map[string]ast.Variable{
   220  					"key2": {
   221  						Type:  ast.TypeString,
   222  						Value: "baz",
   223  					},
   224  				},
   225  			},
   226  		},
   227  	})
   228  }
   229  
   230  func TestInterpolateFuncMax(t *testing.T) {
   231  	testFunction(t, testFunctionConfig{
   232  		Cases: []testFunctionCase{
   233  			{
   234  				`${max()}`,
   235  				nil,
   236  				true,
   237  			},
   238  
   239  			{
   240  				`${max("")}`,
   241  				nil,
   242  				true,
   243  			},
   244  
   245  			{
   246  				`${max(-1, 0, 1)}`,
   247  				"1",
   248  				false,
   249  			},
   250  
   251  			{
   252  				`${max(1, 0, -1)}`,
   253  				"1",
   254  				false,
   255  			},
   256  
   257  			{
   258  				`${max(-1, -2)}`,
   259  				"-1",
   260  				false,
   261  			},
   262  
   263  			{
   264  				`${max(-1)}`,
   265  				"-1",
   266  				false,
   267  			},
   268  		},
   269  	})
   270  }
   271  
   272  func TestInterpolateFuncMin(t *testing.T) {
   273  	testFunction(t, testFunctionConfig{
   274  		Cases: []testFunctionCase{
   275  			{
   276  				`${min()}`,
   277  				nil,
   278  				true,
   279  			},
   280  
   281  			{
   282  				`${min("")}`,
   283  				nil,
   284  				true,
   285  			},
   286  
   287  			{
   288  				`${min(-1, 0, 1)}`,
   289  				"-1",
   290  				false,
   291  			},
   292  
   293  			{
   294  				`${min(1, 0, -1)}`,
   295  				"-1",
   296  				false,
   297  			},
   298  
   299  			{
   300  				`${min(-1, -2)}`,
   301  				"-2",
   302  				false,
   303  			},
   304  
   305  			{
   306  				`${min(-1)}`,
   307  				"-1",
   308  				false,
   309  			},
   310  		},
   311  	})
   312  }
   313  
   314  func TestInterpolateFuncPow(t *testing.T) {
   315  	testFunction(t, testFunctionConfig{
   316  		Cases: []testFunctionCase{
   317  			{
   318  				`${pow(1, 0)}`,
   319  				"1",
   320  				false,
   321  			},
   322  			{
   323  				`${pow(1, 1)}`,
   324  				"1",
   325  				false,
   326  			},
   327  
   328  			{
   329  				`${pow(2, 0)}`,
   330  				"1",
   331  				false,
   332  			},
   333  			{
   334  				`${pow(2, 1)}`,
   335  				"2",
   336  				false,
   337  			},
   338  			{
   339  				`${pow(3, 2)}`,
   340  				"9",
   341  				false,
   342  			},
   343  			{
   344  				`${pow(-3, 2)}`,
   345  				"9",
   346  				false,
   347  			},
   348  			{
   349  				`${pow(2, -2)}`,
   350  				"0.25",
   351  				false,
   352  			},
   353  			{
   354  				`${pow(0, 2)}`,
   355  				"0",
   356  				false,
   357  			},
   358  			{
   359  				`${pow("invalid-input", 2)}`,
   360  				nil,
   361  				true,
   362  			},
   363  			{
   364  				`${pow(2, "invalid-input")}`,
   365  				nil,
   366  				true,
   367  			},
   368  			{
   369  				`${pow(2)}`,
   370  				nil,
   371  				true,
   372  			},
   373  		},
   374  	})
   375  }
   376  
   377  func TestInterpolateFuncFloor(t *testing.T) {
   378  	testFunction(t, testFunctionConfig{
   379  		Cases: []testFunctionCase{
   380  			{
   381  				`${floor()}`,
   382  				nil,
   383  				true,
   384  			},
   385  
   386  			{
   387  				`${floor("")}`,
   388  				nil,
   389  				true,
   390  			},
   391  
   392  			{
   393  				`${floor("-1.3")}`, // there appears to be a AST bug where the parsed argument ends up being -1 without the "s
   394  				"-2",
   395  				false,
   396  			},
   397  
   398  			{
   399  				`${floor(1.7)}`,
   400  				"1",
   401  				false,
   402  			},
   403  		},
   404  	})
   405  }
   406  
   407  func TestInterpolateFuncCeil(t *testing.T) {
   408  	testFunction(t, testFunctionConfig{
   409  		Cases: []testFunctionCase{
   410  			{
   411  				`${ceil()}`,
   412  				nil,
   413  				true,
   414  			},
   415  
   416  			{
   417  				`${ceil("")}`,
   418  				nil,
   419  				true,
   420  			},
   421  
   422  			{
   423  				`${ceil(-1.8)}`,
   424  				"-1",
   425  				false,
   426  			},
   427  
   428  			{
   429  				`${ceil(1.2)}`,
   430  				"2",
   431  				false,
   432  			},
   433  		},
   434  	})
   435  }
   436  
   437  func TestInterpolateFuncLog(t *testing.T) {
   438  	testFunction(t, testFunctionConfig{
   439  		Cases: []testFunctionCase{
   440  			{
   441  				`${log(1, 10)}`,
   442  				"0",
   443  				false,
   444  			},
   445  			{
   446  				`${log(10, 10)}`,
   447  				"1",
   448  				false,
   449  			},
   450  
   451  			{
   452  				`${log(0, 10)}`,
   453  				"-Inf",
   454  				false,
   455  			},
   456  			{
   457  				`${log(10, 0)}`,
   458  				"-0",
   459  				false,
   460  			},
   461  		},
   462  	})
   463  }
   464  
   465  func TestInterpolateFuncChomp(t *testing.T) {
   466  	testFunction(t, testFunctionConfig{
   467  		Cases: []testFunctionCase{
   468  			{
   469  				`${chomp()}`,
   470  				nil,
   471  				true,
   472  			},
   473  
   474  			{
   475  				`${chomp("hello world")}`,
   476  				"hello world",
   477  				false,
   478  			},
   479  
   480  			{
   481  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld"),
   482  				"goodbye\ncruel\nworld",
   483  				false,
   484  			},
   485  
   486  			{
   487  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld"),
   488  				"goodbye\r\nwindows\r\nworld",
   489  				false,
   490  			},
   491  
   492  			{
   493  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n"),
   494  				"goodbye\ncruel\nworld",
   495  				false,
   496  			},
   497  
   498  			{
   499  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n\n\n\n"),
   500  				"goodbye\ncruel\nworld",
   501  				false,
   502  			},
   503  
   504  			{
   505  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n"),
   506  				"goodbye\r\nwindows\r\nworld",
   507  				false,
   508  			},
   509  
   510  			{
   511  				fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n\r\n\r\n\r\n"),
   512  				"goodbye\r\nwindows\r\nworld",
   513  				false,
   514  			},
   515  		},
   516  	})
   517  }
   518  
   519  func TestInterpolateFuncMap(t *testing.T) {
   520  	testFunction(t, testFunctionConfig{
   521  		Cases: []testFunctionCase{
   522  			// empty input returns empty map
   523  			{
   524  				`${map()}`,
   525  				map[string]interface{}{},
   526  				false,
   527  			},
   528  
   529  			// odd args is error
   530  			{
   531  				`${map("odd")}`,
   532  				nil,
   533  				true,
   534  			},
   535  
   536  			// two args returns map w/ one k/v
   537  			{
   538  				`${map("hello", "world")}`,
   539  				map[string]interface{}{"hello": "world"},
   540  				false,
   541  			},
   542  
   543  			// four args get two k/v
   544  			{
   545  				`${map("hello", "world", "what's", "up?")}`,
   546  				map[string]interface{}{"hello": "world", "what's": "up?"},
   547  				false,
   548  			},
   549  
   550  			// map of lists is okay
   551  			{
   552  				`${map("hello", list("world"), "what's", list("up?"))}`,
   553  				map[string]interface{}{
   554  					"hello":  []interface{}{"world"},
   555  					"what's": []interface{}{"up?"},
   556  				},
   557  				false,
   558  			},
   559  
   560  			// map of maps is okay
   561  			{
   562  				`${map("hello", map("there", "world"), "what's", map("really", "up?"))}`,
   563  				map[string]interface{}{
   564  					"hello":  map[string]interface{}{"there": "world"},
   565  					"what's": map[string]interface{}{"really": "up?"},
   566  				},
   567  				false,
   568  			},
   569  
   570  			// keys have to be strings
   571  			{
   572  				`${map(list("listkey"), "val")}`,
   573  				nil,
   574  				true,
   575  			},
   576  
   577  			// types have to match
   578  			{
   579  				`${map("some", "strings", "also", list("lists"))}`,
   580  				nil,
   581  				true,
   582  			},
   583  
   584  			// duplicate keys are an error
   585  			{
   586  				`${map("key", "val", "key", "again")}`,
   587  				nil,
   588  				true,
   589  			},
   590  		},
   591  	})
   592  }
   593  
   594  func TestInterpolateFuncCompact(t *testing.T) {
   595  	testFunction(t, testFunctionConfig{
   596  		Cases: []testFunctionCase{
   597  			// empty string within array
   598  			{
   599  				`${compact(split(",", "a,,b"))}`,
   600  				[]interface{}{"a", "b"},
   601  				false,
   602  			},
   603  
   604  			// empty string at the end of array
   605  			{
   606  				`${compact(split(",", "a,b,"))}`,
   607  				[]interface{}{"a", "b"},
   608  				false,
   609  			},
   610  
   611  			// single empty string
   612  			{
   613  				`${compact(split(",", ""))}`,
   614  				[]interface{}{},
   615  				false,
   616  			},
   617  
   618  			// errrors on list of lists
   619  			{
   620  				`${compact(list(list("a"), list("b")))}`,
   621  				nil,
   622  				true,
   623  			},
   624  		},
   625  	})
   626  }
   627  
   628  func TestInterpolateFuncCidrHost(t *testing.T) {
   629  	testFunction(t, testFunctionConfig{
   630  		Cases: []testFunctionCase{
   631  			{
   632  				`${cidrhost("192.168.1.0/24", 5)}`,
   633  				"192.168.1.5",
   634  				false,
   635  			},
   636  			{
   637  				`${cidrhost("192.168.1.0/24", -5)}`,
   638  				"192.168.1.251",
   639  				false,
   640  			},
   641  			{
   642  				`${cidrhost("192.168.1.0/24", -256)}`,
   643  				"192.168.1.0",
   644  				false,
   645  			},
   646  			{
   647  				`${cidrhost("192.168.1.0/30", 255)}`,
   648  				nil,
   649  				true, // 255 doesn't fit in two bits
   650  			},
   651  			{
   652  				`${cidrhost("192.168.1.0/30", -255)}`,
   653  				nil,
   654  				true, // 255 doesn't fit in two bits
   655  			},
   656  			{
   657  				`${cidrhost("not-a-cidr", 6)}`,
   658  				nil,
   659  				true, // not a valid CIDR mask
   660  			},
   661  			{
   662  				`${cidrhost("10.256.0.0/8", 6)}`,
   663  				nil,
   664  				true, // can't have an octet >255
   665  			},
   666  		},
   667  	})
   668  }
   669  
   670  func TestInterpolateFuncCidrNetmask(t *testing.T) {
   671  	testFunction(t, testFunctionConfig{
   672  		Cases: []testFunctionCase{
   673  			{
   674  				`${cidrnetmask("192.168.1.0/24")}`,
   675  				"255.255.255.0",
   676  				false,
   677  			},
   678  			{
   679  				`${cidrnetmask("192.168.1.0/32")}`,
   680  				"255.255.255.255",
   681  				false,
   682  			},
   683  			{
   684  				`${cidrnetmask("0.0.0.0/0")}`,
   685  				"0.0.0.0",
   686  				false,
   687  			},
   688  			{
   689  				// This doesn't really make sense for IPv6 networks
   690  				// but it ought to do something sensible anyway.
   691  				`${cidrnetmask("1::/64")}`,
   692  				"ffff:ffff:ffff:ffff::",
   693  				false,
   694  			},
   695  			{
   696  				`${cidrnetmask("not-a-cidr")}`,
   697  				nil,
   698  				true, // not a valid CIDR mask
   699  			},
   700  			{
   701  				`${cidrnetmask("10.256.0.0/8")}`,
   702  				nil,
   703  				true, // can't have an octet >255
   704  			},
   705  		},
   706  	})
   707  }
   708  
   709  func TestInterpolateFuncCidrSubnet(t *testing.T) {
   710  	testFunction(t, testFunctionConfig{
   711  		Cases: []testFunctionCase{
   712  			{
   713  				`${cidrsubnet("192.168.2.0/20", 4, 6)}`,
   714  				"192.168.6.0/24",
   715  				false,
   716  			},
   717  			{
   718  				`${cidrsubnet("fe80::/48", 16, 6)}`,
   719  				"fe80:0:0:6::/64",
   720  				false,
   721  			},
   722  			{
   723  				// IPv4 address encoded in IPv6 syntax gets normalized
   724  				`${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`,
   725  				"192.168.6.0/24",
   726  				false,
   727  			},
   728  			{
   729  				`${cidrsubnet("192.168.0.0/30", 4, 6)}`,
   730  				nil,
   731  				true, // not enough bits left
   732  			},
   733  			{
   734  				`${cidrsubnet("192.168.0.0/16", 2, 16)}`,
   735  				nil,
   736  				true, // can't encode 16 in 2 bits
   737  			},
   738  			{
   739  				`${cidrsubnet("not-a-cidr", 4, 6)}`,
   740  				nil,
   741  				true, // not a valid CIDR mask
   742  			},
   743  			{
   744  				`${cidrsubnet("10.256.0.0/8", 4, 6)}`,
   745  				nil,
   746  				true, // can't have an octet >255
   747  			},
   748  		},
   749  	})
   750  }
   751  
   752  func TestInterpolateFuncCoalesce(t *testing.T) {
   753  	testFunction(t, testFunctionConfig{
   754  		Cases: []testFunctionCase{
   755  			{
   756  				`${coalesce("first", "second", "third")}`,
   757  				"first",
   758  				false,
   759  			},
   760  			{
   761  				`${coalesce("", "second", "third")}`,
   762  				"second",
   763  				false,
   764  			},
   765  			{
   766  				`${coalesce("", "", "")}`,
   767  				"",
   768  				false,
   769  			},
   770  			{
   771  				`${coalesce("foo")}`,
   772  				nil,
   773  				true,
   774  			},
   775  		},
   776  	})
   777  }
   778  
   779  func TestInterpolateFuncCoalesceList(t *testing.T) {
   780  	testFunction(t, testFunctionConfig{
   781  		Cases: []testFunctionCase{
   782  			{
   783  				`${coalescelist(list("first"), list("second"), list("third"))}`,
   784  				[]interface{}{"first"},
   785  				false,
   786  			},
   787  			{
   788  				`${coalescelist(list(), list("second"), list("third"))}`,
   789  				[]interface{}{"second"},
   790  				false,
   791  			},
   792  			{
   793  				`${coalescelist(list(), list(), list())}`,
   794  				[]interface{}{},
   795  				false,
   796  			},
   797  			{
   798  				`${coalescelist(list("foo"))}`,
   799  				nil,
   800  				true,
   801  			},
   802  		},
   803  	})
   804  }
   805  
   806  func TestInterpolateFuncConcat(t *testing.T) {
   807  	testFunction(t, testFunctionConfig{
   808  		Cases: []testFunctionCase{
   809  			// String + list
   810  			// no longer supported, now returns an error
   811  			{
   812  				`${concat("a", split(",", "b,c"))}`,
   813  				nil,
   814  				true,
   815  			},
   816  
   817  			// List + string
   818  			// no longer supported, now returns an error
   819  			{
   820  				`${concat(split(",", "a,b"), "c")}`,
   821  				nil,
   822  				true,
   823  			},
   824  
   825  			// Single list
   826  			{
   827  				`${concat(split(",", ",foo,"))}`,
   828  				[]interface{}{"", "foo", ""},
   829  				false,
   830  			},
   831  			{
   832  				`${concat(split(",", "a,b,c"))}`,
   833  				[]interface{}{"a", "b", "c"},
   834  				false,
   835  			},
   836  
   837  			// Two lists
   838  			{
   839  				`${concat(split(",", "a,b,c"), split(",", "d,e"))}`,
   840  				[]interface{}{"a", "b", "c", "d", "e"},
   841  				false,
   842  			},
   843  			// Two lists with different separators
   844  			{
   845  				`${concat(split(",", "a,b,c"), split(" ", "d e"))}`,
   846  				[]interface{}{"a", "b", "c", "d", "e"},
   847  				false,
   848  			},
   849  
   850  			// More lists
   851  			{
   852  				`${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`,
   853  				[]interface{}{"a", "b", "c", "d", "e", "f", "0", "1"},
   854  				false,
   855  			},
   856  
   857  			// list vars
   858  			{
   859  				`${concat("${var.list}", "${var.list}")}`,
   860  				[]interface{}{"a", "b", "a", "b"},
   861  				false,
   862  			},
   863  			// lists of lists
   864  			{
   865  				`${concat("${var.lists}", "${var.lists}")}`,
   866  				[]interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}},
   867  				false,
   868  			},
   869  
   870  			// lists of maps
   871  			{
   872  				`${concat("${var.maps}", "${var.maps}")}`,
   873  				[]interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}},
   874  				false,
   875  			},
   876  
   877  			// multiple strings
   878  			// no longer supported, now returns an error
   879  			{
   880  				`${concat("string1", "string2")}`,
   881  				nil,
   882  				true,
   883  			},
   884  
   885  			// mismatched types
   886  			{
   887  				`${concat("${var.lists}", "${var.maps}")}`,
   888  				nil,
   889  				true,
   890  			},
   891  		},
   892  		Vars: map[string]ast.Variable{
   893  			"var.list": {
   894  				Type: ast.TypeList,
   895  				Value: []ast.Variable{
   896  					{
   897  						Type:  ast.TypeString,
   898  						Value: "a",
   899  					},
   900  					{
   901  						Type:  ast.TypeString,
   902  						Value: "b",
   903  					},
   904  				},
   905  			},
   906  			"var.lists": {
   907  				Type: ast.TypeList,
   908  				Value: []ast.Variable{
   909  					{
   910  						Type: ast.TypeList,
   911  						Value: []ast.Variable{
   912  							{
   913  								Type:  ast.TypeString,
   914  								Value: "c",
   915  							},
   916  							{
   917  								Type:  ast.TypeString,
   918  								Value: "d",
   919  							},
   920  						},
   921  					},
   922  				},
   923  			},
   924  			"var.maps": {
   925  				Type: ast.TypeList,
   926  				Value: []ast.Variable{
   927  					{
   928  						Type: ast.TypeMap,
   929  						Value: map[string]ast.Variable{
   930  							"key1": {
   931  								Type:  ast.TypeString,
   932  								Value: "a",
   933  							},
   934  							"key2": {
   935  								Type:  ast.TypeString,
   936  								Value: "b",
   937  							},
   938  						},
   939  					},
   940  				},
   941  			},
   942  		},
   943  	})
   944  }
   945  
   946  func TestInterpolateFuncContains(t *testing.T) {
   947  	testFunction(t, testFunctionConfig{
   948  		Vars: map[string]ast.Variable{
   949  			"var.listOfStrings": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
   950  			"var.listOfInts":    interfaceToVariableSwallowError([]int{1, 2, 3}),
   951  		},
   952  		Cases: []testFunctionCase{
   953  			{
   954  				`${contains(var.listOfStrings, "bar")}`,
   955  				"true",
   956  				false,
   957  			},
   958  
   959  			{
   960  				`${contains(var.listOfStrings, "foo")}`,
   961  				"false",
   962  				false,
   963  			},
   964  			{
   965  				`${contains(var.listOfInts, 1)}`,
   966  				"true",
   967  				false,
   968  			},
   969  			{
   970  				`${contains(var.listOfInts, 10)}`,
   971  				"false",
   972  				false,
   973  			},
   974  			{
   975  				`${contains(var.listOfInts, "2")}`,
   976  				"true",
   977  				false,
   978  			},
   979  		},
   980  	})
   981  }
   982  
   983  func TestInterpolateFuncMerge(t *testing.T) {
   984  	testFunction(t, testFunctionConfig{
   985  		Cases: []testFunctionCase{
   986  			// basic merge
   987  			{
   988  				`${merge(map("a", "b"), map("c", "d"))}`,
   989  				map[string]interface{}{"a": "b", "c": "d"},
   990  				false,
   991  			},
   992  
   993  			// merge with conflicts is ok, last in wins.
   994  			{
   995  				`${merge(map("a", "b", "c", "X"), map("c", "d"))}`,
   996  				map[string]interface{}{"a": "b", "c": "d"},
   997  				false,
   998  			},
   999  
  1000  			// merge variadic
  1001  			{
  1002  				`${merge(map("a", "b"), map("c", "d"), map("e", "f"))}`,
  1003  				map[string]interface{}{"a": "b", "c": "d", "e": "f"},
  1004  				false,
  1005  			},
  1006  
  1007  			// merge with variables
  1008  			{
  1009  				`${merge(var.maps[0], map("c", "d"))}`,
  1010  				map[string]interface{}{"key1": "a", "key2": "b", "c": "d"},
  1011  				false,
  1012  			},
  1013  
  1014  			// only accept maps
  1015  			{
  1016  				`${merge(map("a", "b"), list("c", "d"))}`,
  1017  				nil,
  1018  				true,
  1019  			},
  1020  
  1021  			// merge maps of maps
  1022  			{
  1023  				`${merge(map("a", var.maps[0]), map("b", var.maps[1]))}`,
  1024  				map[string]interface{}{
  1025  					"b": map[string]interface{}{"key3": "d", "key4": "c"},
  1026  					"a": map[string]interface{}{"key1": "a", "key2": "b"},
  1027  				},
  1028  				false,
  1029  			},
  1030  			// merge maps of lists
  1031  			{
  1032  				`${merge(map("a", list("b")), map("c", list("d", "e")))}`,
  1033  				map[string]interface{}{"a": []interface{}{"b"}, "c": []interface{}{"d", "e"}},
  1034  				false,
  1035  			},
  1036  			// merge map of various kinds
  1037  			{
  1038  				`${merge(map("a", var.maps[0]), map("b", list("c", "d")))}`,
  1039  				map[string]interface{}{"a": map[string]interface{}{"key1": "a", "key2": "b"}, "b": []interface{}{"c", "d"}},
  1040  				false,
  1041  			},
  1042  		},
  1043  		Vars: map[string]ast.Variable{
  1044  			"var.maps": {
  1045  				Type: ast.TypeList,
  1046  				Value: []ast.Variable{
  1047  					{
  1048  						Type: ast.TypeMap,
  1049  						Value: map[string]ast.Variable{
  1050  							"key1": {
  1051  								Type:  ast.TypeString,
  1052  								Value: "a",
  1053  							},
  1054  							"key2": {
  1055  								Type:  ast.TypeString,
  1056  								Value: "b",
  1057  							},
  1058  						},
  1059  					},
  1060  					{
  1061  						Type: ast.TypeMap,
  1062  						Value: map[string]ast.Variable{
  1063  							"key3": {
  1064  								Type:  ast.TypeString,
  1065  								Value: "d",
  1066  							},
  1067  							"key4": {
  1068  								Type:  ast.TypeString,
  1069  								Value: "c",
  1070  							},
  1071  						},
  1072  					},
  1073  				},
  1074  			},
  1075  		},
  1076  	})
  1077  
  1078  }
  1079  
  1080  func TestInterpolateFuncDirname(t *testing.T) {
  1081  	testFunction(t, testFunctionConfig{
  1082  		Cases: []testFunctionCase{
  1083  			{
  1084  				`${dirname("/foo/bar/baz")}`,
  1085  				"/foo/bar",
  1086  				false,
  1087  			},
  1088  		},
  1089  	})
  1090  }
  1091  
  1092  func TestInterpolateFuncDistinct(t *testing.T) {
  1093  	testFunction(t, testFunctionConfig{
  1094  		Cases: []testFunctionCase{
  1095  			// 3 duplicates
  1096  			{
  1097  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`,
  1098  				[]interface{}{"user1", "user2", "user3"},
  1099  				false,
  1100  			},
  1101  			// 1 duplicate
  1102  			{
  1103  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`,
  1104  				[]interface{}{"user1", "user2", "user3", "user4"},
  1105  				false,
  1106  			},
  1107  			// too many args
  1108  			{
  1109  				`${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`,
  1110  				nil,
  1111  				true,
  1112  			},
  1113  			// non-flat list is an error
  1114  			{
  1115  				`${distinct(list(list("a"), list("a")))}`,
  1116  				nil,
  1117  				true,
  1118  			},
  1119  		},
  1120  	})
  1121  }
  1122  
  1123  func TestInterpolateFuncMatchKeys(t *testing.T) {
  1124  	testFunction(t, testFunctionConfig{
  1125  		Cases: []testFunctionCase{
  1126  			// normal usage
  1127  			{
  1128  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2"))}`,
  1129  				[]interface{}{"b"},
  1130  				false,
  1131  			},
  1132  			// normal usage 2, check the order
  1133  			{
  1134  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref1"))}`,
  1135  				[]interface{}{"a", "b"},
  1136  				false,
  1137  			},
  1138  			// duplicate item in searchset
  1139  			{
  1140  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref2"))}`,
  1141  				[]interface{}{"b"},
  1142  				false,
  1143  			},
  1144  			// no matches
  1145  			{
  1146  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref4"))}`,
  1147  				[]interface{}{},
  1148  				false,
  1149  			},
  1150  			// no matches 2
  1151  			{
  1152  				`${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list())}`,
  1153  				[]interface{}{},
  1154  				false,
  1155  			},
  1156  			// zero case
  1157  			{
  1158  				`${matchkeys(list(), list(), list("nope"))}`,
  1159  				[]interface{}{},
  1160  				false,
  1161  			},
  1162  			// complex values
  1163  			{
  1164  				`${matchkeys(list(list("a", "a")), list("a"), list("a"))}`,
  1165  				[]interface{}{[]interface{}{"a", "a"}},
  1166  				false,
  1167  			},
  1168  			// errors
  1169  			// different types
  1170  			{
  1171  				`${matchkeys(list("a"), list(1), list("a"))}`,
  1172  				nil,
  1173  				true,
  1174  			},
  1175  			// different types
  1176  			{
  1177  				`${matchkeys(list("a"), list(list("a"), list("a")), list("a"))}`,
  1178  				nil,
  1179  				true,
  1180  			},
  1181  			// lists of different length is an error
  1182  			{
  1183  				`${matchkeys(list("a"), list("a", "b"), list("a"))}`,
  1184  				nil,
  1185  				true,
  1186  			},
  1187  		},
  1188  	})
  1189  }
  1190  
  1191  func TestInterpolateFuncFile(t *testing.T) {
  1192  	tf, err := ioutil.TempFile("", "tf")
  1193  	if err != nil {
  1194  		t.Fatalf("err: %s", err)
  1195  	}
  1196  	path := tf.Name()
  1197  	tf.Write([]byte("foo"))
  1198  	tf.Close()
  1199  	defer os.Remove(path)
  1200  
  1201  	testFunction(t, testFunctionConfig{
  1202  		Cases: []testFunctionCase{
  1203  			{
  1204  				fmt.Sprintf(`${file("%s")}`, path),
  1205  				"foo",
  1206  				false,
  1207  			},
  1208  
  1209  			// Invalid path
  1210  			{
  1211  				`${file("/i/dont/exist")}`,
  1212  				nil,
  1213  				true,
  1214  			},
  1215  
  1216  			// Too many args
  1217  			{
  1218  				`${file("foo", "bar")}`,
  1219  				nil,
  1220  				true,
  1221  			},
  1222  		},
  1223  	})
  1224  }
  1225  
  1226  func TestInterpolateFuncFormat(t *testing.T) {
  1227  	testFunction(t, testFunctionConfig{
  1228  		Cases: []testFunctionCase{
  1229  			{
  1230  				`${format("hello")}`,
  1231  				"hello",
  1232  				false,
  1233  			},
  1234  
  1235  			{
  1236  				`${format("hello %s", "world")}`,
  1237  				"hello world",
  1238  				false,
  1239  			},
  1240  
  1241  			{
  1242  				`${format("hello %d", 42)}`,
  1243  				"hello 42",
  1244  				false,
  1245  			},
  1246  
  1247  			{
  1248  				`${format("hello %05d", 42)}`,
  1249  				"hello 00042",
  1250  				false,
  1251  			},
  1252  
  1253  			{
  1254  				`${format("hello %05d", 12345)}`,
  1255  				"hello 12345",
  1256  				false,
  1257  			},
  1258  		},
  1259  	})
  1260  }
  1261  
  1262  func TestInterpolateFuncFormatList(t *testing.T) {
  1263  	testFunction(t, testFunctionConfig{
  1264  		Cases: []testFunctionCase{
  1265  			// formatlist requires at least one list
  1266  			{
  1267  				`${formatlist("hello")}`,
  1268  				nil,
  1269  				true,
  1270  			},
  1271  			{
  1272  				`${formatlist("hello %s", "world")}`,
  1273  				nil,
  1274  				true,
  1275  			},
  1276  			// formatlist applies to each list element in turn
  1277  			{
  1278  				`${formatlist("<%s>", split(",", "A,B"))}`,
  1279  				[]interface{}{"<A>", "<B>"},
  1280  				false,
  1281  			},
  1282  			// formatlist repeats scalar elements
  1283  			{
  1284  				`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
  1285  				"x=A, x=B, x=C",
  1286  				false,
  1287  			},
  1288  			// Multiple lists are walked in parallel
  1289  			{
  1290  				`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
  1291  				"A=1, B=2, C=3",
  1292  				false,
  1293  			},
  1294  			// Mismatched list lengths generate an error
  1295  			{
  1296  				`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
  1297  				nil,
  1298  				true,
  1299  			},
  1300  			// Works with lists of length 1 [GH-2240]
  1301  			{
  1302  				`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
  1303  				[]interface{}{"demo-rest-elb.id"},
  1304  				false,
  1305  			},
  1306  			// Works with empty lists [GH-7607]
  1307  			{
  1308  				`${formatlist("%s", var.emptylist)}`,
  1309  				[]interface{}{},
  1310  				false,
  1311  			},
  1312  		},
  1313  		Vars: map[string]ast.Variable{
  1314  			"var.emptylist": {
  1315  				Type:  ast.TypeList,
  1316  				Value: []ast.Variable{},
  1317  			},
  1318  		},
  1319  	})
  1320  }
  1321  
  1322  func TestInterpolateFuncIndex(t *testing.T) {
  1323  	testFunction(t, testFunctionConfig{
  1324  		Vars: map[string]ast.Variable{
  1325  			"var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}),
  1326  			"var.list2": interfaceToVariableSwallowError([]string{"foo"}),
  1327  			"var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}),
  1328  		},
  1329  		Cases: []testFunctionCase{
  1330  			{
  1331  				`${index("test", "")}`,
  1332  				nil,
  1333  				true,
  1334  			},
  1335  
  1336  			{
  1337  				`${index(var.list1, "foo")}`,
  1338  				nil,
  1339  				true,
  1340  			},
  1341  
  1342  			{
  1343  				`${index(var.list2, "foo")}`,
  1344  				"0",
  1345  				false,
  1346  			},
  1347  
  1348  			{
  1349  				`${index(var.list3, "bar")}`,
  1350  				"2",
  1351  				false,
  1352  			},
  1353  		},
  1354  	})
  1355  }
  1356  
  1357  func TestInterpolateFuncIndent(t *testing.T) {
  1358  	testFunction(t, testFunctionConfig{
  1359  		Cases: []testFunctionCase{
  1360  			{
  1361  				`${indent(4, "Fleas:
  1362  Adam
  1363  Had'em
  1364  
  1365  E.E. Cummings")}`,
  1366  				"Fleas:\n    Adam\n    Had'em\n    \n    E.E. Cummings",
  1367  				false,
  1368  			},
  1369  			{
  1370  				`${indent(4, "oneliner")}`,
  1371  				"oneliner",
  1372  				false,
  1373  			},
  1374  			{
  1375  				`${indent(4, "#!/usr/bin/env bash
  1376  date
  1377  pwd")}`,
  1378  				"#!/usr/bin/env bash\n    date\n    pwd",
  1379  				false,
  1380  			},
  1381  		},
  1382  	})
  1383  }
  1384  
  1385  func TestInterpolateFuncJoin(t *testing.T) {
  1386  	testFunction(t, testFunctionConfig{
  1387  		Vars: map[string]ast.Variable{
  1388  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo"}),
  1389  			"var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}),
  1390  			"var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}),
  1391  		},
  1392  		Cases: []testFunctionCase{
  1393  			{
  1394  				`${join(",")}`,
  1395  				nil,
  1396  				true,
  1397  			},
  1398  
  1399  			{
  1400  				`${join(",", var.a_list)}`,
  1401  				"foo",
  1402  				false,
  1403  			},
  1404  
  1405  			{
  1406  				`${join(".", var.a_longer_list)}`,
  1407  				"foo.bar.baz",
  1408  				false,
  1409  			},
  1410  
  1411  			{
  1412  				`${join(".", var.list_of_lists)}`,
  1413  				nil,
  1414  				true,
  1415  			},
  1416  			{
  1417  				`${join(".", list(list("nested")))}`,
  1418  				nil,
  1419  				true,
  1420  			},
  1421  		},
  1422  	})
  1423  }
  1424  
  1425  func TestInterpolateFuncJSONEncode(t *testing.T) {
  1426  	testFunction(t, testFunctionConfig{
  1427  		Vars: map[string]ast.Variable{
  1428  			"easy": ast.Variable{
  1429  				Value: "test",
  1430  				Type:  ast.TypeString,
  1431  			},
  1432  			"hard": ast.Variable{
  1433  				Value: " foo \\ \n \t \" bar ",
  1434  				Type:  ast.TypeString,
  1435  			},
  1436  			"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
  1437  			"emptylist": ast.Variable{
  1438  				Value: []ast.Variable{},
  1439  				Type:  ast.TypeList,
  1440  			},
  1441  			"map": interfaceToVariableSwallowError(map[string]string{
  1442  				"foo":     "bar",
  1443  				"ba \n z": "q\\x",
  1444  			}),
  1445  			"emptymap":   interfaceToVariableSwallowError(map[string]string{}),
  1446  			"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
  1447  			"nestedmap":  interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
  1448  		},
  1449  		Cases: []testFunctionCase{
  1450  			{
  1451  				`${jsonencode("test")}`,
  1452  				`"test"`,
  1453  				false,
  1454  			},
  1455  			{
  1456  				`${jsonencode(easy)}`,
  1457  				`"test"`,
  1458  				false,
  1459  			},
  1460  			{
  1461  				`${jsonencode(hard)}`,
  1462  				`" foo \\ \n \t \" bar "`,
  1463  				false,
  1464  			},
  1465  			{
  1466  				`${jsonencode("")}`,
  1467  				`""`,
  1468  				false,
  1469  			},
  1470  			{
  1471  				`${jsonencode()}`,
  1472  				nil,
  1473  				true,
  1474  			},
  1475  			{
  1476  				`${jsonencode(list)}`,
  1477  				`["foo","bar\tbaz"]`,
  1478  				false,
  1479  			},
  1480  			{
  1481  				`${jsonencode(emptylist)}`,
  1482  				`[]`,
  1483  				false,
  1484  			},
  1485  			{
  1486  				`${jsonencode(map)}`,
  1487  				`{"ba \n z":"q\\x","foo":"bar"}`,
  1488  				false,
  1489  			},
  1490  			{
  1491  				`${jsonencode(emptymap)}`,
  1492  				`{}`,
  1493  				false,
  1494  			},
  1495  			{
  1496  				`${jsonencode(nestedlist)}`,
  1497  				`[["foo"]]`,
  1498  				false,
  1499  			},
  1500  			{
  1501  				`${jsonencode(nestedmap)}`,
  1502  				`{"foo":["bar"]}`,
  1503  				false,
  1504  			},
  1505  		},
  1506  	})
  1507  }
  1508  
  1509  func TestInterpolateFuncReplace(t *testing.T) {
  1510  	testFunction(t, testFunctionConfig{
  1511  		Cases: []testFunctionCase{
  1512  			// Regular search and replace
  1513  			{
  1514  				`${replace("hello", "hel", "bel")}`,
  1515  				"bello",
  1516  				false,
  1517  			},
  1518  
  1519  			// Search string doesn't match
  1520  			{
  1521  				`${replace("hello", "nope", "bel")}`,
  1522  				"hello",
  1523  				false,
  1524  			},
  1525  
  1526  			// Regular expression
  1527  			{
  1528  				`${replace("hello", "/l/", "L")}`,
  1529  				"heLLo",
  1530  				false,
  1531  			},
  1532  
  1533  			{
  1534  				`${replace("helo", "/(l)/", "$1$1")}`,
  1535  				"hello",
  1536  				false,
  1537  			},
  1538  
  1539  			// Bad regexp
  1540  			{
  1541  				`${replace("helo", "/(l/", "$1$1")}`,
  1542  				nil,
  1543  				true,
  1544  			},
  1545  		},
  1546  	})
  1547  }
  1548  
  1549  func TestInterpolateFuncLength(t *testing.T) {
  1550  	testFunction(t, testFunctionConfig{
  1551  		Cases: []testFunctionCase{
  1552  			// Raw strings
  1553  			{
  1554  				`${length("")}`,
  1555  				"0",
  1556  				false,
  1557  			},
  1558  			{
  1559  				`${length("a")}`,
  1560  				"1",
  1561  				false,
  1562  			},
  1563  			{
  1564  				`${length(" ")}`,
  1565  				"1",
  1566  				false,
  1567  			},
  1568  			{
  1569  				`${length(" a ,")}`,
  1570  				"4",
  1571  				false,
  1572  			},
  1573  			{
  1574  				`${length("aaa")}`,
  1575  				"3",
  1576  				false,
  1577  			},
  1578  
  1579  			// Lists
  1580  			{
  1581  				`${length(split(",", "a"))}`,
  1582  				"1",
  1583  				false,
  1584  			},
  1585  			{
  1586  				`${length(split(",", "foo,"))}`,
  1587  				"2",
  1588  				false,
  1589  			},
  1590  			{
  1591  				`${length(split(",", ",foo,"))}`,
  1592  				"3",
  1593  				false,
  1594  			},
  1595  			{
  1596  				`${length(split(",", "foo,bar"))}`,
  1597  				"2",
  1598  				false,
  1599  			},
  1600  			{
  1601  				`${length(split(".", "one.two.three.four.five"))}`,
  1602  				"5",
  1603  				false,
  1604  			},
  1605  			// Want length 0 if we split an empty string then compact
  1606  			{
  1607  				`${length(compact(split(",", "")))}`,
  1608  				"0",
  1609  				false,
  1610  			},
  1611  			// Works for maps
  1612  			{
  1613  				`${length(map("k", "v"))}`,
  1614  				"1",
  1615  				false,
  1616  			},
  1617  			{
  1618  				`${length(map("k1", "v1", "k2", "v2"))}`,
  1619  				"2",
  1620  				false,
  1621  			},
  1622  		},
  1623  	})
  1624  }
  1625  
  1626  func TestInterpolateFuncSignum(t *testing.T) {
  1627  	testFunction(t, testFunctionConfig{
  1628  		Cases: []testFunctionCase{
  1629  			{
  1630  				`${signum()}`,
  1631  				nil,
  1632  				true,
  1633  			},
  1634  
  1635  			{
  1636  				`${signum("")}`,
  1637  				nil,
  1638  				true,
  1639  			},
  1640  
  1641  			{
  1642  				`${signum(0)}`,
  1643  				"0",
  1644  				false,
  1645  			},
  1646  
  1647  			{
  1648  				`${signum(15)}`,
  1649  				"1",
  1650  				false,
  1651  			},
  1652  
  1653  			{
  1654  				`${signum(-29)}`,
  1655  				"-1",
  1656  				false,
  1657  			},
  1658  		},
  1659  	})
  1660  }
  1661  
  1662  func TestInterpolateFuncSlice(t *testing.T) {
  1663  	testFunction(t, testFunctionConfig{
  1664  		Cases: []testFunctionCase{
  1665  			// Negative from index
  1666  			{
  1667  				`${slice(list("a"), -1, 0)}`,
  1668  				nil,
  1669  				true,
  1670  			},
  1671  			// From index > to index
  1672  			{
  1673  				`${slice(list("a", "b", "c"), 2, 1)}`,
  1674  				nil,
  1675  				true,
  1676  			},
  1677  			// To index too large
  1678  			{
  1679  				`${slice(var.list_of_strings, 1, 4)}`,
  1680  				nil,
  1681  				true,
  1682  			},
  1683  			// Empty slice
  1684  			{
  1685  				`${slice(var.list_of_strings, 1, 1)}`,
  1686  				[]interface{}{},
  1687  				false,
  1688  			},
  1689  			{
  1690  				`${slice(var.list_of_strings, 1, 2)}`,
  1691  				[]interface{}{"b"},
  1692  				false,
  1693  			},
  1694  			{
  1695  				`${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`,
  1696  				[]interface{}{"a", "b"},
  1697  				false,
  1698  			},
  1699  		},
  1700  		Vars: map[string]ast.Variable{
  1701  			"var.list_of_strings": {
  1702  				Type: ast.TypeList,
  1703  				Value: []ast.Variable{
  1704  					{
  1705  						Type:  ast.TypeString,
  1706  						Value: "a",
  1707  					},
  1708  					{
  1709  						Type:  ast.TypeString,
  1710  						Value: "b",
  1711  					},
  1712  					{
  1713  						Type:  ast.TypeString,
  1714  						Value: "c",
  1715  					},
  1716  				},
  1717  			},
  1718  		},
  1719  	})
  1720  }
  1721  
  1722  func TestInterpolateFuncSort(t *testing.T) {
  1723  	testFunction(t, testFunctionConfig{
  1724  		Vars: map[string]ast.Variable{
  1725  			"var.strings": ast.Variable{
  1726  				Type: ast.TypeList,
  1727  				Value: []ast.Variable{
  1728  					{Type: ast.TypeString, Value: "c"},
  1729  					{Type: ast.TypeString, Value: "a"},
  1730  					{Type: ast.TypeString, Value: "b"},
  1731  				},
  1732  			},
  1733  			"var.notstrings": ast.Variable{
  1734  				Type: ast.TypeList,
  1735  				Value: []ast.Variable{
  1736  					{Type: ast.TypeList, Value: []ast.Variable{}},
  1737  					{Type: ast.TypeString, Value: "b"},
  1738  				},
  1739  			},
  1740  		},
  1741  		Cases: []testFunctionCase{
  1742  			{
  1743  				`${sort(var.strings)}`,
  1744  				[]interface{}{"a", "b", "c"},
  1745  				false,
  1746  			},
  1747  			{
  1748  				`${sort(var.notstrings)}`,
  1749  				nil,
  1750  				true,
  1751  			},
  1752  		},
  1753  	})
  1754  }
  1755  
  1756  func TestInterpolateFuncSplit(t *testing.T) {
  1757  	testFunction(t, testFunctionConfig{
  1758  		Cases: []testFunctionCase{
  1759  			{
  1760  				`${split(",")}`,
  1761  				nil,
  1762  				true,
  1763  			},
  1764  
  1765  			{
  1766  				`${split(",", "")}`,
  1767  				[]interface{}{""},
  1768  				false,
  1769  			},
  1770  
  1771  			{
  1772  				`${split(",", "foo")}`,
  1773  				[]interface{}{"foo"},
  1774  				false,
  1775  			},
  1776  
  1777  			{
  1778  				`${split(",", ",,,")}`,
  1779  				[]interface{}{"", "", "", ""},
  1780  				false,
  1781  			},
  1782  
  1783  			{
  1784  				`${split(",", "foo,")}`,
  1785  				[]interface{}{"foo", ""},
  1786  				false,
  1787  			},
  1788  
  1789  			{
  1790  				`${split(",", ",foo,")}`,
  1791  				[]interface{}{"", "foo", ""},
  1792  				false,
  1793  			},
  1794  
  1795  			{
  1796  				`${split(".", "foo.bar.baz")}`,
  1797  				[]interface{}{"foo", "bar", "baz"},
  1798  				false,
  1799  			},
  1800  		},
  1801  	})
  1802  }
  1803  
  1804  func TestInterpolateFuncLookup(t *testing.T) {
  1805  	testFunction(t, testFunctionConfig{
  1806  		Vars: map[string]ast.Variable{
  1807  			"var.foo": {
  1808  				Type: ast.TypeMap,
  1809  				Value: map[string]ast.Variable{
  1810  					"bar": {
  1811  						Type:  ast.TypeString,
  1812  						Value: "baz",
  1813  					},
  1814  				},
  1815  			},
  1816  			"var.map_of_lists": ast.Variable{
  1817  				Type: ast.TypeMap,
  1818  				Value: map[string]ast.Variable{
  1819  					"bar": {
  1820  						Type: ast.TypeList,
  1821  						Value: []ast.Variable{
  1822  							{
  1823  								Type:  ast.TypeString,
  1824  								Value: "baz",
  1825  							},
  1826  						},
  1827  					},
  1828  				},
  1829  			},
  1830  		},
  1831  		Cases: []testFunctionCase{
  1832  			{
  1833  				`${lookup(var.foo, "bar")}`,
  1834  				"baz",
  1835  				false,
  1836  			},
  1837  
  1838  			// Invalid key
  1839  			{
  1840  				`${lookup(var.foo, "baz")}`,
  1841  				nil,
  1842  				true,
  1843  			},
  1844  
  1845  			// Supplied default with valid key
  1846  			{
  1847  				`${lookup(var.foo, "bar", "")}`,
  1848  				"baz",
  1849  				false,
  1850  			},
  1851  
  1852  			// Supplied default with invalid key
  1853  			{
  1854  				`${lookup(var.foo, "zip", "")}`,
  1855  				"",
  1856  				false,
  1857  			},
  1858  
  1859  			// Too many args
  1860  			{
  1861  				`${lookup(var.foo, "bar", "", "abc")}`,
  1862  				nil,
  1863  				true,
  1864  			},
  1865  
  1866  			// Cannot lookup into map of lists
  1867  			{
  1868  				`${lookup(var.map_of_lists, "bar")}`,
  1869  				nil,
  1870  				true,
  1871  			},
  1872  
  1873  			// Non-empty default
  1874  			{
  1875  				`${lookup(var.foo, "zap", "xyz")}`,
  1876  				"xyz",
  1877  				false,
  1878  			},
  1879  		},
  1880  	})
  1881  }
  1882  
  1883  func TestInterpolateFuncKeys(t *testing.T) {
  1884  	testFunction(t, testFunctionConfig{
  1885  		Vars: map[string]ast.Variable{
  1886  			"var.foo": ast.Variable{
  1887  				Type: ast.TypeMap,
  1888  				Value: map[string]ast.Variable{
  1889  					"bar": ast.Variable{
  1890  						Value: "baz",
  1891  						Type:  ast.TypeString,
  1892  					},
  1893  					"qux": ast.Variable{
  1894  						Value: "quack",
  1895  						Type:  ast.TypeString,
  1896  					},
  1897  				},
  1898  			},
  1899  			"var.str": ast.Variable{
  1900  				Value: "astring",
  1901  				Type:  ast.TypeString,
  1902  			},
  1903  		},
  1904  		Cases: []testFunctionCase{
  1905  			{
  1906  				`${keys(var.foo)}`,
  1907  				[]interface{}{"bar", "qux"},
  1908  				false,
  1909  			},
  1910  
  1911  			// Invalid key
  1912  			{
  1913  				`${keys(var.not)}`,
  1914  				nil,
  1915  				true,
  1916  			},
  1917  
  1918  			// Too many args
  1919  			{
  1920  				`${keys(var.foo, "bar")}`,
  1921  				nil,
  1922  				true,
  1923  			},
  1924  
  1925  			// Not a map
  1926  			{
  1927  				`${keys(var.str)}`,
  1928  				nil,
  1929  				true,
  1930  			},
  1931  		},
  1932  	})
  1933  }
  1934  
  1935  // Confirm that keys return in sorted order, and values return in the order of
  1936  // their sorted keys.
  1937  func TestInterpolateFuncKeyValOrder(t *testing.T) {
  1938  	testFunction(t, testFunctionConfig{
  1939  		Vars: map[string]ast.Variable{
  1940  			"var.foo": ast.Variable{
  1941  				Type: ast.TypeMap,
  1942  				Value: map[string]ast.Variable{
  1943  					"D": ast.Variable{
  1944  						Value: "2",
  1945  						Type:  ast.TypeString,
  1946  					},
  1947  					"C": ast.Variable{
  1948  						Value: "Y",
  1949  						Type:  ast.TypeString,
  1950  					},
  1951  					"A": ast.Variable{
  1952  						Value: "X",
  1953  						Type:  ast.TypeString,
  1954  					},
  1955  					"10": ast.Variable{
  1956  						Value: "Z",
  1957  						Type:  ast.TypeString,
  1958  					},
  1959  					"1": ast.Variable{
  1960  						Value: "4",
  1961  						Type:  ast.TypeString,
  1962  					},
  1963  					"3": ast.Variable{
  1964  						Value: "W",
  1965  						Type:  ast.TypeString,
  1966  					},
  1967  				},
  1968  			},
  1969  		},
  1970  		Cases: []testFunctionCase{
  1971  			{
  1972  				`${keys(var.foo)}`,
  1973  				[]interface{}{"1", "10", "3", "A", "C", "D"},
  1974  				false,
  1975  			},
  1976  
  1977  			{
  1978  				`${values(var.foo)}`,
  1979  				[]interface{}{"4", "Z", "W", "X", "Y", "2"},
  1980  				false,
  1981  			},
  1982  		},
  1983  	})
  1984  }
  1985  
  1986  func TestInterpolateFuncValues(t *testing.T) {
  1987  	testFunction(t, testFunctionConfig{
  1988  		Vars: map[string]ast.Variable{
  1989  			"var.foo": ast.Variable{
  1990  				Type: ast.TypeMap,
  1991  				Value: map[string]ast.Variable{
  1992  					"bar": ast.Variable{
  1993  						Value: "quack",
  1994  						Type:  ast.TypeString,
  1995  					},
  1996  					"qux": ast.Variable{
  1997  						Value: "baz",
  1998  						Type:  ast.TypeString,
  1999  					},
  2000  				},
  2001  			},
  2002  			"var.str": ast.Variable{
  2003  				Value: "astring",
  2004  				Type:  ast.TypeString,
  2005  			},
  2006  		},
  2007  		Cases: []testFunctionCase{
  2008  			{
  2009  				`${values(var.foo)}`,
  2010  				[]interface{}{"quack", "baz"},
  2011  				false,
  2012  			},
  2013  
  2014  			// Invalid key
  2015  			{
  2016  				`${values(var.not)}`,
  2017  				nil,
  2018  				true,
  2019  			},
  2020  
  2021  			// Too many args
  2022  			{
  2023  				`${values(var.foo, "bar")}`,
  2024  				nil,
  2025  				true,
  2026  			},
  2027  
  2028  			// Not a map
  2029  			{
  2030  				`${values(var.str)}`,
  2031  				nil,
  2032  				true,
  2033  			},
  2034  
  2035  			// Map of lists
  2036  			{
  2037  				`${values(map("one", list()))}`,
  2038  				nil,
  2039  				true,
  2040  			},
  2041  		},
  2042  	})
  2043  }
  2044  
  2045  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
  2046  	variable, _ := hil.InterfaceToVariable(input)
  2047  	return variable
  2048  }
  2049  
  2050  func TestInterpolateFuncElement(t *testing.T) {
  2051  	testFunction(t, testFunctionConfig{
  2052  		Vars: map[string]ast.Variable{
  2053  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo", "baz"}),
  2054  			"var.a_short_list":  interfaceToVariableSwallowError([]string{"foo"}),
  2055  			"var.empty_list":    interfaceToVariableSwallowError([]interface{}{}),
  2056  			"var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}),
  2057  		},
  2058  		Cases: []testFunctionCase{
  2059  			{
  2060  				`${element(var.a_list, "1")}`,
  2061  				"baz",
  2062  				false,
  2063  			},
  2064  
  2065  			{
  2066  				`${element(var.a_short_list, "0")}`,
  2067  				"foo",
  2068  				false,
  2069  			},
  2070  
  2071  			// Invalid index should wrap vs. out-of-bounds
  2072  			{
  2073  				`${element(var.a_list, "2")}`,
  2074  				"foo",
  2075  				false,
  2076  			},
  2077  
  2078  			// Negative number should fail
  2079  			{
  2080  				`${element(var.a_short_list, "-1")}`,
  2081  				nil,
  2082  				true,
  2083  			},
  2084  
  2085  			// Empty list should fail
  2086  			{
  2087  				`${element(var.empty_list, 0)}`,
  2088  				nil,
  2089  				true,
  2090  			},
  2091  
  2092  			// Too many args
  2093  			{
  2094  				`${element(var.a_list, "0", "2")}`,
  2095  				nil,
  2096  				true,
  2097  			},
  2098  
  2099  			// Only works on single-level lists
  2100  			{
  2101  				`${element(var.a_nested_list, "0")}`,
  2102  				nil,
  2103  				true,
  2104  			},
  2105  		},
  2106  	})
  2107  }
  2108  
  2109  func TestInterpolateFuncChunklist(t *testing.T) {
  2110  	testFunction(t, testFunctionConfig{
  2111  		Cases: []testFunctionCase{
  2112  			// normal usage
  2113  			{
  2114  				`${chunklist(list("a", "b", "c"), 1)}`,
  2115  				[]interface{}{
  2116  					[]interface{}{"a"},
  2117  					[]interface{}{"b"},
  2118  					[]interface{}{"c"},
  2119  				},
  2120  				false,
  2121  			},
  2122  			// `size` is pair and the list has an impair number of items
  2123  			{
  2124  				`${chunklist(list("a", "b", "c"), 2)}`,
  2125  				[]interface{}{
  2126  					[]interface{}{"a", "b"},
  2127  					[]interface{}{"c"},
  2128  				},
  2129  				false,
  2130  			},
  2131  			// list made of the same list, since size is 0
  2132  			{
  2133  				`${chunklist(list("a", "b", "c"), 0)}`,
  2134  				[]interface{}{[]interface{}{"a", "b", "c"}},
  2135  				false,
  2136  			},
  2137  			// negative size of chunks
  2138  			{
  2139  				`${chunklist(list("a", "b", "c"), -1)}`,
  2140  				nil,
  2141  				true,
  2142  			},
  2143  		},
  2144  	})
  2145  }
  2146  
  2147  func TestInterpolateFuncBasename(t *testing.T) {
  2148  	testFunction(t, testFunctionConfig{
  2149  		Cases: []testFunctionCase{
  2150  			{
  2151  				`${basename("/foo/bar/baz")}`,
  2152  				"baz",
  2153  				false,
  2154  			},
  2155  		},
  2156  	})
  2157  }
  2158  
  2159  func TestInterpolateFuncBase64Encode(t *testing.T) {
  2160  	testFunction(t, testFunctionConfig{
  2161  		Cases: []testFunctionCase{
  2162  			// Regular base64 encoding
  2163  			{
  2164  				`${base64encode("abc123!?$*&()'-=@~")}`,
  2165  				"YWJjMTIzIT8kKiYoKSctPUB+",
  2166  				false,
  2167  			},
  2168  		},
  2169  	})
  2170  }
  2171  
  2172  func TestInterpolateFuncBase64Decode(t *testing.T) {
  2173  	testFunction(t, testFunctionConfig{
  2174  		Cases: []testFunctionCase{
  2175  			// Regular base64 decoding
  2176  			{
  2177  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
  2178  				"abc123!?$*&()'-=@~",
  2179  				false,
  2180  			},
  2181  
  2182  			// Invalid base64 data decoding
  2183  			{
  2184  				`${base64decode("this-is-an-invalid-base64-data")}`,
  2185  				nil,
  2186  				true,
  2187  			},
  2188  		},
  2189  	})
  2190  }
  2191  
  2192  func TestInterpolateFuncLower(t *testing.T) {
  2193  	testFunction(t, testFunctionConfig{
  2194  		Cases: []testFunctionCase{
  2195  			{
  2196  				`${lower("HELLO")}`,
  2197  				"hello",
  2198  				false,
  2199  			},
  2200  
  2201  			{
  2202  				`${lower("")}`,
  2203  				"",
  2204  				false,
  2205  			},
  2206  
  2207  			{
  2208  				`${lower()}`,
  2209  				nil,
  2210  				true,
  2211  			},
  2212  		},
  2213  	})
  2214  }
  2215  
  2216  func TestInterpolateFuncUpper(t *testing.T) {
  2217  	testFunction(t, testFunctionConfig{
  2218  		Cases: []testFunctionCase{
  2219  			{
  2220  				`${upper("hello")}`,
  2221  				"HELLO",
  2222  				false,
  2223  			},
  2224  
  2225  			{
  2226  				`${upper("")}`,
  2227  				"",
  2228  				false,
  2229  			},
  2230  
  2231  			{
  2232  				`${upper()}`,
  2233  				nil,
  2234  				true,
  2235  			},
  2236  		},
  2237  	})
  2238  }
  2239  
  2240  func TestInterpolateFuncSha1(t *testing.T) {
  2241  	testFunction(t, testFunctionConfig{
  2242  		Cases: []testFunctionCase{
  2243  			{
  2244  				`${sha1("test")}`,
  2245  				"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
  2246  				false,
  2247  			},
  2248  		},
  2249  	})
  2250  }
  2251  
  2252  func TestInterpolateFuncSha256(t *testing.T) {
  2253  	testFunction(t, testFunctionConfig{
  2254  		Cases: []testFunctionCase{
  2255  			{ // hexadecimal representation of sha256 sum
  2256  				`${sha256("test")}`,
  2257  				"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  2258  				false,
  2259  			},
  2260  		},
  2261  	})
  2262  }
  2263  
  2264  func TestInterpolateFuncSha512(t *testing.T) {
  2265  	testFunction(t, testFunctionConfig{
  2266  		Cases: []testFunctionCase{
  2267  			{
  2268  				`${sha512("test")}`,
  2269  				"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
  2270  				false,
  2271  			},
  2272  		},
  2273  	})
  2274  }
  2275  
  2276  func TestInterpolateFuncTitle(t *testing.T) {
  2277  	testFunction(t, testFunctionConfig{
  2278  		Cases: []testFunctionCase{
  2279  			{
  2280  				`${title("hello")}`,
  2281  				"Hello",
  2282  				false,
  2283  			},
  2284  
  2285  			{
  2286  				`${title("hello world")}`,
  2287  				"Hello World",
  2288  				false,
  2289  			},
  2290  
  2291  			{
  2292  				`${title("")}`,
  2293  				"",
  2294  				false,
  2295  			},
  2296  
  2297  			{
  2298  				`${title()}`,
  2299  				nil,
  2300  				true,
  2301  			},
  2302  		},
  2303  	})
  2304  }
  2305  
  2306  func TestInterpolateFuncTrimSpace(t *testing.T) {
  2307  	testFunction(t, testFunctionConfig{
  2308  		Cases: []testFunctionCase{
  2309  			{
  2310  				`${trimspace(" test ")}`,
  2311  				"test",
  2312  				false,
  2313  			},
  2314  		},
  2315  	})
  2316  }
  2317  
  2318  func TestInterpolateFuncBase64Gzip(t *testing.T) {
  2319  	testFunction(t, testFunctionConfig{
  2320  		Cases: []testFunctionCase{
  2321  			{
  2322  				`${base64gzip("test")}`,
  2323  				"H4sIAAAAAAAA/ypJLS4BAAAA//8BAAD//wx+f9gEAAAA",
  2324  				false,
  2325  			},
  2326  		},
  2327  	})
  2328  }
  2329  
  2330  func TestInterpolateFuncBase64Sha256(t *testing.T) {
  2331  	testFunction(t, testFunctionConfig{
  2332  		Cases: []testFunctionCase{
  2333  			{
  2334  				`${base64sha256("test")}`,
  2335  				"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
  2336  				false,
  2337  			},
  2338  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  2339  				`${base64encode(sha256("test"))}`,
  2340  				"OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==",
  2341  				false,
  2342  			},
  2343  		},
  2344  	})
  2345  }
  2346  
  2347  func TestInterpolateFuncBase64Sha512(t *testing.T) {
  2348  	testFunction(t, testFunctionConfig{
  2349  		Cases: []testFunctionCase{
  2350  			{
  2351  				`${base64sha512("test")}`,
  2352  				"7iaw3Ur350mqGo7jwQrpkj9hiYB3Lkc/iBml1JQODbJ6wYX4oOHV+E+IvIh/1nsUNzLDBMxfqa2Ob1f1ACio/w==",
  2353  				false,
  2354  			},
  2355  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  2356  				`${base64encode(sha512("test"))}`,
  2357  				"ZWUyNmIwZGQ0YWY3ZTc0OWFhMWE4ZWUzYzEwYWU5OTIzZjYxODk4MDc3MmU0NzNmODgxOWE1ZDQ5NDBlMGRiMjdhYzE4NWY4YTBlMWQ1Zjg0Zjg4YmM4ODdmZDY3YjE0MzczMmMzMDRjYzVmYTlhZDhlNmY1N2Y1MDAyOGE4ZmY=",
  2358  				false,
  2359  			},
  2360  		},
  2361  	})
  2362  }
  2363  
  2364  func TestInterpolateFuncMd5(t *testing.T) {
  2365  	testFunction(t, testFunctionConfig{
  2366  		Cases: []testFunctionCase{
  2367  			{
  2368  				`${md5("tada")}`,
  2369  				"ce47d07243bb6eaf5e1322c81baf9bbf",
  2370  				false,
  2371  			},
  2372  			{ // Confirm that we're not trimming any whitespaces
  2373  				`${md5(" tada ")}`,
  2374  				"aadf191a583e53062de2d02c008141c4",
  2375  				false,
  2376  			},
  2377  			{ // We accept empty string too
  2378  				`${md5("")}`,
  2379  				"d41d8cd98f00b204e9800998ecf8427e",
  2380  				false,
  2381  			},
  2382  		},
  2383  	})
  2384  }
  2385  
  2386  func TestInterpolateFuncUUID(t *testing.T) {
  2387  	results := make(map[string]bool)
  2388  
  2389  	for i := 0; i < 100; i++ {
  2390  		ast, err := hil.Parse("${uuid()}")
  2391  		if err != nil {
  2392  			t.Fatalf("err: %s", err)
  2393  		}
  2394  
  2395  		result, err := hil.Eval(ast, langEvalConfig(nil))
  2396  		if err != nil {
  2397  			t.Fatalf("err: %s", err)
  2398  		}
  2399  
  2400  		if results[result.Value.(string)] {
  2401  			t.Fatalf("Got unexpected duplicate uuid: %s", result.Value)
  2402  		}
  2403  
  2404  		results[result.Value.(string)] = true
  2405  	}
  2406  }
  2407  
  2408  func TestInterpolateFuncTimestamp(t *testing.T) {
  2409  	currentTime := time.Now().UTC()
  2410  	ast, err := hil.Parse("${timestamp()}")
  2411  	if err != nil {
  2412  		t.Fatalf("err: %s", err)
  2413  	}
  2414  
  2415  	result, err := hil.Eval(ast, langEvalConfig(nil))
  2416  	if err != nil {
  2417  		t.Fatalf("err: %s", err)
  2418  	}
  2419  	resultTime, err := time.Parse(time.RFC3339, result.Value.(string))
  2420  	if err != nil {
  2421  		t.Fatalf("Error parsing timestamp: %s", err)
  2422  	}
  2423  
  2424  	if resultTime.Sub(currentTime).Seconds() > 10.0 {
  2425  		t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string))
  2426  	}
  2427  }
  2428  
  2429  func TestInterpolateFuncTimeAdd(t *testing.T) {
  2430  	testFunction(t, testFunctionConfig{
  2431  		Cases: []testFunctionCase{
  2432  			{
  2433  				`${timeadd("2017-11-22T00:00:00Z", "1s")}`,
  2434  				"2017-11-22T00:00:01Z",
  2435  				false,
  2436  			},
  2437  			{
  2438  				`${timeadd("2017-11-22T00:00:00Z", "10m1s")}`,
  2439  				"2017-11-22T00:10:01Z",
  2440  				false,
  2441  			},
  2442  			{ // also support subtraction
  2443  				`${timeadd("2017-11-22T00:00:00Z", "-1h")}`,
  2444  				"2017-11-21T23:00:00Z",
  2445  				false,
  2446  			},
  2447  			{ // Invalid format timestamp
  2448  				`${timeadd("2017-11-22", "-1h")}`,
  2449  				nil,
  2450  				true,
  2451  			},
  2452  			{ // Invalid format duration (day is not supported by ParseDuration)
  2453  				`${timeadd("2017-11-22T00:00:00Z", "1d")}`,
  2454  				nil,
  2455  				true,
  2456  			},
  2457  		},
  2458  	})
  2459  }
  2460  
  2461  type testFunctionConfig struct {
  2462  	Cases []testFunctionCase
  2463  	Vars  map[string]ast.Variable
  2464  }
  2465  
  2466  type testFunctionCase struct {
  2467  	Input  string
  2468  	Result interface{}
  2469  	Error  bool
  2470  }
  2471  
  2472  func testFunction(t *testing.T, config testFunctionConfig) {
  2473  	t.Helper()
  2474  	for _, tc := range config.Cases {
  2475  		t.Run(tc.Input, func(t *testing.T) {
  2476  			ast, err := hil.Parse(tc.Input)
  2477  			if err != nil {
  2478  				t.Fatalf("unexpected parse error: %s", err)
  2479  			}
  2480  
  2481  			result, err := hil.Eval(ast, langEvalConfig(config.Vars))
  2482  			if err != nil != tc.Error {
  2483  				t.Fatalf("unexpected eval error: %s", err)
  2484  			}
  2485  
  2486  			if !reflect.DeepEqual(result.Value, tc.Result) {
  2487  				t.Errorf("wrong result\ngiven: %s\ngot:   %#v\nwant:  %#v", tc.Input, result.Value, tc.Result)
  2488  			}
  2489  		})
  2490  	}
  2491  }
  2492  
  2493  func TestInterpolateFuncPathExpand(t *testing.T) {
  2494  	homePath, err := homedir.Dir()
  2495  	if err != nil {
  2496  		t.Fatalf("Error getting home directory: %v", err)
  2497  	}
  2498  	testFunction(t, testFunctionConfig{
  2499  		Cases: []testFunctionCase{
  2500  			{
  2501  				`${pathexpand("~/test-file")}`,
  2502  				filepath.Join(homePath, "test-file"),
  2503  				false,
  2504  			},
  2505  			{
  2506  				`${pathexpand("~/another/test/file")}`,
  2507  				filepath.Join(homePath, "another/test/file"),
  2508  				false,
  2509  			},
  2510  			{
  2511  				`${pathexpand("/root/file")}`,
  2512  				"/root/file",
  2513  				false,
  2514  			},
  2515  			{
  2516  				`${pathexpand("/")}`,
  2517  				"/",
  2518  				false,
  2519  			},
  2520  			{
  2521  				`${pathexpand()}`,
  2522  				nil,
  2523  				true,
  2524  			},
  2525  		},
  2526  	})
  2527  }
  2528  
  2529  func TestInterpolateFuncSubstr(t *testing.T) {
  2530  	testFunction(t, testFunctionConfig{
  2531  		Cases: []testFunctionCase{
  2532  			{
  2533  				`${substr("foobar", 0, 0)}`,
  2534  				"",
  2535  				false,
  2536  			},
  2537  			{
  2538  				`${substr("foobar", 0, -1)}`,
  2539  				"foobar",
  2540  				false,
  2541  			},
  2542  			{
  2543  				`${substr("foobar", 0, 3)}`,
  2544  				"foo",
  2545  				false,
  2546  			},
  2547  			{
  2548  				`${substr("foobar", 3, 3)}`,
  2549  				"bar",
  2550  				false,
  2551  			},
  2552  			{
  2553  				`${substr("foobar", -3, 3)}`,
  2554  				"bar",
  2555  				false,
  2556  			},
  2557  
  2558  			// empty string
  2559  			{
  2560  				`${substr("", 0, 0)}`,
  2561  				"",
  2562  				false,
  2563  			},
  2564  
  2565  			// invalid offset
  2566  			{
  2567  				`${substr("", 1, 0)}`,
  2568  				nil,
  2569  				true,
  2570  			},
  2571  			{
  2572  				`${substr("foo", -4, -1)}`,
  2573  				nil,
  2574  				true,
  2575  			},
  2576  
  2577  			// invalid length
  2578  			{
  2579  				`${substr("", 0, 1)}`,
  2580  				nil,
  2581  				true,
  2582  			},
  2583  			{
  2584  				`${substr("", 0, -2)}`,
  2585  				nil,
  2586  				true,
  2587  			},
  2588  		},
  2589  	})
  2590  }
  2591  
  2592  func TestInterpolateFuncBcrypt(t *testing.T) {
  2593  	node, err := hil.Parse(`${bcrypt("test")}`)
  2594  	if err != nil {
  2595  		t.Fatalf("err: %s", err)
  2596  	}
  2597  
  2598  	result, err := hil.Eval(node, langEvalConfig(nil))
  2599  	if err != nil {
  2600  		t.Fatalf("err: %s", err)
  2601  	}
  2602  	err = bcrypt.CompareHashAndPassword([]byte(result.Value.(string)), []byte("test"))
  2603  
  2604  	if err != nil {
  2605  		t.Fatalf("Error comparing hash and password: %s", err)
  2606  	}
  2607  
  2608  	testFunction(t, testFunctionConfig{
  2609  		Cases: []testFunctionCase{
  2610  			//Negative test for more than two parameters
  2611  			{
  2612  				`${bcrypt("test", 15, 12)}`,
  2613  				nil,
  2614  				true,
  2615  			},
  2616  		},
  2617  	})
  2618  }
  2619  
  2620  func TestInterpolateFuncFlatten(t *testing.T) {
  2621  	testFunction(t, testFunctionConfig{
  2622  		Cases: []testFunctionCase{
  2623  			// empty string within array
  2624  			{
  2625  				`${flatten(split(",", "a,,b"))}`,
  2626  				[]interface{}{"a", "", "b"},
  2627  				false,
  2628  			},
  2629  
  2630  			// typical array
  2631  			{
  2632  				`${flatten(split(",", "a,b,c"))}`,
  2633  				[]interface{}{"a", "b", "c"},
  2634  				false,
  2635  			},
  2636  
  2637  			// empty array
  2638  			{
  2639  				`${flatten(split(",", ""))}`,
  2640  				[]interface{}{""},
  2641  				false,
  2642  			},
  2643  
  2644  			// list of lists
  2645  			{
  2646  				`${flatten(list(list("a"), list("b")))}`,
  2647  				[]interface{}{"a", "b"},
  2648  				false,
  2649  			},
  2650  			// list of lists of lists
  2651  			{
  2652  				`${flatten(list(list("a"), list(list("b","c"))))}`,
  2653  				[]interface{}{"a", "b", "c"},
  2654  				false,
  2655  			},
  2656  			// list of strings
  2657  			{
  2658  				`${flatten(list("a", "b", "c"))}`,
  2659  				[]interface{}{"a", "b", "c"},
  2660  				false,
  2661  			},
  2662  		},
  2663  	})
  2664  }
  2665  
  2666  func TestInterpolateFuncURLEncode(t *testing.T) {
  2667  	testFunction(t, testFunctionConfig{
  2668  		Cases: []testFunctionCase{
  2669  			{
  2670  				`${urlencode("abc123-_")}`,
  2671  				"abc123-_",
  2672  				false,
  2673  			},
  2674  			{
  2675  				`${urlencode("foo:bar@localhost?foo=bar&bar=baz")}`,
  2676  				"foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz",
  2677  				false,
  2678  			},
  2679  			{
  2680  				`${urlencode("mailto:email?subject=this+is+my+subject")}`,
  2681  				"mailto%3Aemail%3Fsubject%3Dthis%2Bis%2Bmy%2Bsubject",
  2682  				false,
  2683  			},
  2684  			{
  2685  				`${urlencode("foo/bar")}`,
  2686  				"foo%2Fbar",
  2687  				false,
  2688  			},
  2689  		},
  2690  	})
  2691  }
  2692  
  2693  func TestInterpolateFuncTranspose(t *testing.T) {
  2694  	testFunction(t, testFunctionConfig{
  2695  		Vars: map[string]ast.Variable{
  2696  			"var.map": ast.Variable{
  2697  				Type: ast.TypeMap,
  2698  				Value: map[string]ast.Variable{
  2699  					"key1": ast.Variable{
  2700  						Type: ast.TypeList,
  2701  						Value: []ast.Variable{
  2702  							{Type: ast.TypeString, Value: "a"},
  2703  							{Type: ast.TypeString, Value: "b"},
  2704  						},
  2705  					},
  2706  					"key2": ast.Variable{
  2707  						Type: ast.TypeList,
  2708  						Value: []ast.Variable{
  2709  							{Type: ast.TypeString, Value: "a"},
  2710  							{Type: ast.TypeString, Value: "b"},
  2711  							{Type: ast.TypeString, Value: "c"},
  2712  						},
  2713  					},
  2714  					"key3": ast.Variable{
  2715  						Type: ast.TypeList,
  2716  						Value: []ast.Variable{
  2717  							{Type: ast.TypeString, Value: "c"},
  2718  						},
  2719  					},
  2720  					"key4": ast.Variable{
  2721  						Type:  ast.TypeList,
  2722  						Value: []ast.Variable{},
  2723  					},
  2724  				}},
  2725  			"var.badmap": ast.Variable{
  2726  				Type: ast.TypeMap,
  2727  				Value: map[string]ast.Variable{
  2728  					"key1": ast.Variable{
  2729  						Type: ast.TypeList,
  2730  						Value: []ast.Variable{
  2731  							{Type: ast.TypeList, Value: []ast.Variable{}},
  2732  							{Type: ast.TypeList, Value: []ast.Variable{}},
  2733  						},
  2734  					},
  2735  				}},
  2736  			"var.worsemap": ast.Variable{
  2737  				Type: ast.TypeMap,
  2738  				Value: map[string]ast.Variable{
  2739  					"key1": ast.Variable{
  2740  						Type:  ast.TypeString,
  2741  						Value: "not-a-list",
  2742  					},
  2743  				}},
  2744  		},
  2745  		Cases: []testFunctionCase{
  2746  			{
  2747  				`${transpose(var.map)}`,
  2748  				map[string]interface{}{
  2749  					"a": []interface{}{"key1", "key2"},
  2750  					"b": []interface{}{"key1", "key2"},
  2751  					"c": []interface{}{"key2", "key3"},
  2752  				},
  2753  				false,
  2754  			},
  2755  			{
  2756  				`${transpose(var.badmap)}`,
  2757  				nil,
  2758  				true,
  2759  			},
  2760  			{
  2761  				`${transpose(var.worsemap)}`,
  2762  				nil,
  2763  				true,
  2764  			},
  2765  		},
  2766  	})
  2767  }
  2768  
  2769  func TestInterpolateFuncAbs(t *testing.T) {
  2770  	testFunction(t, testFunctionConfig{
  2771  		Cases: []testFunctionCase{
  2772  			{
  2773  				`${abs()}`,
  2774  				nil,
  2775  				true,
  2776  			},
  2777  			{
  2778  				`${abs("")}`,
  2779  				nil,
  2780  				true,
  2781  			},
  2782  			{
  2783  				`${abs(0)}`,
  2784  				"0",
  2785  				false,
  2786  			},
  2787  			{
  2788  				`${abs(1)}`,
  2789  				"1",
  2790  				false,
  2791  			},
  2792  			{
  2793  				`${abs(-1)}`,
  2794  				"1",
  2795  				false,
  2796  			},
  2797  			{
  2798  				`${abs(1.0)}`,
  2799  				"1",
  2800  				false,
  2801  			},
  2802  			{
  2803  				`${abs(-1.0)}`,
  2804  				"1",
  2805  				false,
  2806  			},
  2807  			{
  2808  				`${abs(-3.14)}`,
  2809  				"3.14",
  2810  				false,
  2811  			},
  2812  			{
  2813  				`${abs(-42.001)}`,
  2814  				"42.001",
  2815  				false,
  2816  			},
  2817  		},
  2818  	})
  2819  }
  2820  
  2821  func TestInterpolateFuncRsaDecrypt(t *testing.T) {
  2822  	testFunction(t, testFunctionConfig{
  2823  		Vars: map[string]ast.Variable{
  2824  			"var.cipher_base64": ast.Variable{
  2825  				Type:  ast.TypeString,
  2826  				Value: "eczGaDhXDbOFRZGhjx2etVzWbRqWDlmq0bvNt284JHVbwCgObiuyX9uV0LSAMY707IEgMkExJqXmsB4OWKxvB7epRB9G/3+F+pcrQpODlDuL9oDUAsa65zEpYF0Wbn7Oh7nrMQncyUPpyr9WUlALl0gRWytOA23S+y5joa4M34KFpawFgoqTu/2EEH4Xl1zo+0fy73fEto+nfkUY+meuyGZ1nUx/+DljP7ZqxHBFSlLODmtuTMdswUbHbXbWneW51D7Jm7xB8nSdiA2JQNK5+Sg5x8aNfgvFTt/m2w2+qpsyFa5Wjeu6fZmXSl840CA07aXbk9vN4I81WmJyblD/ZA==",
  2827  			},
  2828  			"var.private_key": ast.Variable{
  2829  				Type: ast.TypeString,
  2830  				Value: `
  2831  -----BEGIN RSA PRIVATE KEY-----
  2832  MIIEowIBAAKCAQEAgUElV5mwqkloIrM8ZNZ72gSCcnSJt7+/Usa5G+D15YQUAdf9
  2833  c1zEekTfHgDP+04nw/uFNFaE5v1RbHaPxhZYVg5ZErNCa/hzn+x10xzcepeS3KPV
  2834  Xcxae4MR0BEegvqZqJzN9loXsNL/c3H/B+2Gle3hTxjlWFb3F5qLgR+4Mf4ruhER
  2835  1v6eHQa/nchi03MBpT4UeJ7MrL92hTJYLdpSyCqmr8yjxkKJDVC2uRrr+sTSxfh7
  2836  r6v24u/vp/QTmBIAlNPgadVAZw17iNNb7vjV7Gwl/5gHXonCUKURaV++dBNLrHIZ
  2837  pqcAM8wHRph8mD1EfL9hsz77pHewxolBATV+7QIDAQABAoIBAC1rK+kFW3vrAYm3
  2838  +8/fQnQQw5nec4o6+crng6JVQXLeH32qXShNf8kLLG/Jj0vaYcTPPDZw9JCKkTMQ
  2839  0mKj9XR/5DLbBMsV6eNXXuvJJ3x4iKW5eD9WkLD4FKlNarBRyO7j8sfPTqXW7uat
  2840  NxWdFH7YsSRvNh/9pyQHLWA5OituidMrYbc3EUx8B1GPNyJ9W8Q8znNYLfwYOjU4
  2841  Wv1SLE6qGQQH9Q0WzA2WUf8jklCYyMYTIywAjGb8kbAJlKhmj2t2Igjmqtwt1PYc
  2842  pGlqbtQBDUiWXt5S4YX/1maIQ/49yeNUajjpbJiH3DbhJbHwFTzP3pZ9P9GHOzlG
  2843  kYR+wSECgYEAw/Xida8kSv8n86V3qSY/I+fYQ5V+jDtXIE+JhRnS8xzbOzz3v0WS
  2844  Oo5H+o4nJx5eL3Ghb3Gcm0Jn46dHrxinHbm+3RjXv/X6tlbxIYjRSQfHOTSMCTvd
  2845  qcliF5vC6RCLXuc7R+IWR1Ky6eDEZGtrvt3DyeYABsp9fRUFR/6NluUCgYEAqNsw
  2846  1aSl7WJa27F0DoJdlU9LWerpXcazlJcIdOz/S9QDmSK3RDQTdqfTxRmrxiYI9LEs
  2847  mkOkvzlnnOBMpnZ3ZOU5qIRfprecRIi37KDAOHWGnlC0EWGgl46YLb7/jXiWf0AG
  2848  Y+DfJJNd9i6TbIDWu8254/erAS6bKMhW/3q7f2kCgYAZ7Id/BiKJAWRpqTRBXlvw
  2849  BhXoKvjI2HjYP21z/EyZ+PFPzur/lNaZhIUlMnUfibbwE9pFggQzzf8scM7c7Sf+
  2850  mLoVSdoQ/Rujz7CqvQzi2nKSsM7t0curUIb3lJWee5/UeEaxZcmIufoNUrzohAWH
  2851  BJOIPDM4ssUTLRq7wYM9uQKBgHCBau5OP8gE6mjKuXsZXWUoahpFLKwwwmJUp2vQ
  2852  pOFPJ/6WZOlqkTVT6QPAcPUbTohKrF80hsZqZyDdSfT3peFx4ZLocBrS56m6NmHR
  2853  UYHMvJ8rQm76T1fryHVidz85g3zRmfBeWg8yqT5oFg4LYgfLsPm1gRjOhs8LfPvI
  2854  OLlRAoGBAIZ5Uv4Z3s8O7WKXXUe/lq6j7vfiVkR1NW/Z/WLKXZpnmvJ7FgxN4e56
  2855  RXT7GwNQHIY8eDjDnsHxzrxd+raOxOZeKcMHj3XyjCX3NHfTscnsBPAGYpY/Wxzh
  2856  T8UYnFu6RzkixElTf2rseEav7rkdKkI3LAeIZy7B0HulKKsmqVQ7
  2857  -----END RSA PRIVATE KEY-----
  2858  `,
  2859  			},
  2860  			"var.wrong_private_key": ast.Variable{
  2861  				Type: ast.TypeString,
  2862  				Value: `
  2863  -----BEGIN RSA PRIVATE KEY-----
  2864  MIIEowIBAAKCAQEAlrCgnEVgmNKCq7KPc+zUU5IrxPu1ClMNJS7RTsTPEkbwe5SB
  2865  p+6V6WtCbD/X/lDRRGbOENChh1Phulb7lViqgrdpHydgsrKoS5ah3DfSIxLFLE00
  2866  9Yo4TCYwgw6+s59j16ZAFVinaQ9l6Kmrb2ll136hMrz8QKh+qw+onOLd38WFgm+W
  2867  ZtUqSXf2LANzfzzy4OWFNyFqKaCAolSkPdTS9Nz+svtScvp002DQp8OdP1AgPO+l
  2868  o5N3M38Fftapwg0pCtJ5Zq0NRWIXEonXiTEMA6zy3gEZVOmDxoIFUWnmrqlMJLFy
  2869  5S6LDrHSdqJhCxDK6WRZj43X9j8spktk3eGhMwIDAQABAoIBAAem8ID/BOi9x+Tw
  2870  LFi2rhGQWqimH4tmrEQ3HGnjlKBY+d1MrUjZ1MMFr1nP5CgF8pqGnfA8p/c3Sz8r
  2871  K5tp5T6+EZiDZ2WrrOApxg5ox0MAsQKO6SGO40z6o3wEQ6rbbTaGOrraxaWQIpyu
  2872  AQanU4Sd6ZGqByVBaS1GnklZO+shCHqw73b7g1cpLEmFzcYnKHYHlUUIsstMe8E1
  2873  BaCY0CH7JbWBjcbiTnBVwIRZuu+EjGiQuhTilYL2OWqoMVg1WU0L2IFpR8lkf/2W
  2874  SBx5J6xhwbBGASOpM+qidiN580GdPzGhWYSqKGroHEzBm6xPSmV1tadNA26WFG4p
  2875  pthLiAECgYEA5BsPRpNYJAQLu5B0N7mj9eEp0HABVEgL/MpwiImjaKdAwp78HM64
  2876  IuPvJxs7r+xESiIz4JyjR8zrQjYOCKJsARYkmNlEuAz0SkHabCw1BdEBwUhjUGVB
  2877  efoERK6GxfAoNqmSDwsOvHFOtsmDIlbHmg7G2rUxNVpeou415BSB0B8CgYEAqR4J
  2878  YHKk2Ibr9rU+rBU33TcdTGw0aAkFNAVeqM9j0haWuFXmV3RArgoy09lH+2Ha6z/g
  2879  fTX2xSDAWV7QUlLOlBRIhurPAo2jO2yCrGHPZcWiugstrR2hTTInigaSnCmK3i7F
  2880  6sYmL3S7K01IcVNxSlWvGijtClT92Cl2WUCTfG0CgYAiEjyk4QtQTd5mxLvnOu5X
  2881  oqs5PBGmwiAwQRiv/EcRMbJFn7Oupd3xMDSflbzDmTnWDOfMy/jDl8MoH6TW+1PA
  2882  kcsjnYhbKWwvz0hN0giVdtOZSDO1ZXpzOrn6fEsbM7T9/TQY1SD9WrtUKCNTNL0Z
  2883  sM1ZC6lu+7GZCpW4HKwLJwKBgQCRT0yxQXBg1/UxwuO5ynV4rx2Oh76z0WRWIXMH
  2884  S0MyxdP1SWGkrS/SGtM3cg/GcHtA/V6vV0nUcWK0p6IJyjrTw2XZ/zGluPuTWJYi
  2885  9dvVT26Vunshrz7kbH7KuwEICy3V4IyQQHeY+QzFlR70uMS0IVFWAepCoWqHbIDT
  2886  CYhwNQKBgGPcLXmjpGtkZvggl0aZr9LsvCTckllSCFSI861kivL/rijdNoCHGxZv
  2887  dfDkLTLcz9Gk41rD9Gxn/3sqodnTAc3Z2PxFnzg1Q/u3+x6YAgBwI/g/jE2xutGW
  2888  H7CurtMwALQ/n/6LUKFmjRZjqbKX9SO2QSaC3grd6sY9Tu+bZjLe
  2889  -----END RSA PRIVATE KEY-----
  2890  `,
  2891  			},
  2892  		},
  2893  		Cases: []testFunctionCase{
  2894  			// Base-64 encoded cipher decrypts correctly
  2895  			{
  2896  				`${rsadecrypt(var.cipher_base64, var.private_key)}`,
  2897  				"message",
  2898  				false,
  2899  			},
  2900  			// Raw cipher
  2901  			{
  2902  				`${rsadecrypt(base64decode(var.cipher_base64), var.private_key)}`,
  2903  				nil,
  2904  				true,
  2905  			},
  2906  			// Wrong key
  2907  			{
  2908  				`${rsadecrypt(var.cipher_base64, var.wrong_private_key)}`,
  2909  				nil,
  2910  				true,
  2911  			},
  2912  			// Bad key
  2913  			{
  2914  				`${rsadecrypt(var.cipher_base64, "bad key")}`,
  2915  				nil,
  2916  				true,
  2917  			},
  2918  			// Empty key
  2919  			{
  2920  				`${rsadecrypt(var.cipher_base64, "")}`,
  2921  				nil,
  2922  				true,
  2923  			},
  2924  			// Bad cipher
  2925  			{
  2926  				`${rsadecrypt("bad cipher", var.private_key)}`,
  2927  				nil,
  2928  				true,
  2929  			},
  2930  			// Bad base64-encoded cipher
  2931  			{
  2932  				`${rsadecrypt(base64encode("bad cipher"), var.private_key)}`,
  2933  				nil,
  2934  				true,
  2935  			},
  2936  			// Empty cipher
  2937  			{
  2938  				`${rsadecrypt("", var.private_key)}`,
  2939  				nil,
  2940  				true,
  2941  			},
  2942  			// Too many arguments
  2943  			{
  2944  				`${rsadecrypt("", "", "")}`,
  2945  				nil,
  2946  				true,
  2947  			},
  2948  			// One argument
  2949  			{
  2950  				`${rsadecrypt("")}`,
  2951  				nil,
  2952  				true,
  2953  			},
  2954  			// No arguments
  2955  			{
  2956  				`${rsadecrypt()}`,
  2957  				nil,
  2958  				true,
  2959  			},
  2960  		},
  2961  	})
  2962  }