github.com/sl1pm4t/terraform@v0.6.4-0.20170725213156-870617d22df3/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 TestInterpolateFuncJoin(t *testing.T) {
  1358  	testFunction(t, testFunctionConfig{
  1359  		Vars: map[string]ast.Variable{
  1360  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo"}),
  1361  			"var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}),
  1362  			"var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}),
  1363  		},
  1364  		Cases: []testFunctionCase{
  1365  			{
  1366  				`${join(",")}`,
  1367  				nil,
  1368  				true,
  1369  			},
  1370  
  1371  			{
  1372  				`${join(",", var.a_list)}`,
  1373  				"foo",
  1374  				false,
  1375  			},
  1376  
  1377  			{
  1378  				`${join(".", var.a_longer_list)}`,
  1379  				"foo.bar.baz",
  1380  				false,
  1381  			},
  1382  
  1383  			{
  1384  				`${join(".", var.list_of_lists)}`,
  1385  				nil,
  1386  				true,
  1387  			},
  1388  			{
  1389  				`${join(".", list(list("nested")))}`,
  1390  				nil,
  1391  				true,
  1392  			},
  1393  		},
  1394  	})
  1395  }
  1396  
  1397  func TestInterpolateFuncJSONEncode(t *testing.T) {
  1398  	testFunction(t, testFunctionConfig{
  1399  		Vars: map[string]ast.Variable{
  1400  			"easy": ast.Variable{
  1401  				Value: "test",
  1402  				Type:  ast.TypeString,
  1403  			},
  1404  			"hard": ast.Variable{
  1405  				Value: " foo \\ \n \t \" bar ",
  1406  				Type:  ast.TypeString,
  1407  			},
  1408  			"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
  1409  			// XXX can't use InterfaceToVariable as it converts empty slice into empty
  1410  			// map.
  1411  			"emptylist": ast.Variable{
  1412  				Value: []ast.Variable{},
  1413  				Type:  ast.TypeList,
  1414  			},
  1415  			"map": interfaceToVariableSwallowError(map[string]string{
  1416  				"foo":     "bar",
  1417  				"ba \n z": "q\\x",
  1418  			}),
  1419  			"emptymap": interfaceToVariableSwallowError(map[string]string{}),
  1420  
  1421  			// Not yet supported (but it would be nice)
  1422  			"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
  1423  			"nestedmap":  interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
  1424  		},
  1425  		Cases: []testFunctionCase{
  1426  			{
  1427  				`${jsonencode("test")}`,
  1428  				`"test"`,
  1429  				false,
  1430  			},
  1431  			{
  1432  				`${jsonencode(easy)}`,
  1433  				`"test"`,
  1434  				false,
  1435  			},
  1436  			{
  1437  				`${jsonencode(hard)}`,
  1438  				`" foo \\ \n \t \" bar "`,
  1439  				false,
  1440  			},
  1441  			{
  1442  				`${jsonencode("")}`,
  1443  				`""`,
  1444  				false,
  1445  			},
  1446  			{
  1447  				`${jsonencode()}`,
  1448  				nil,
  1449  				true,
  1450  			},
  1451  			{
  1452  				`${jsonencode(list)}`,
  1453  				`["foo","bar\tbaz"]`,
  1454  				false,
  1455  			},
  1456  			{
  1457  				`${jsonencode(emptylist)}`,
  1458  				`[]`,
  1459  				false,
  1460  			},
  1461  			{
  1462  				`${jsonencode(map)}`,
  1463  				`{"ba \n z":"q\\x","foo":"bar"}`,
  1464  				false,
  1465  			},
  1466  			{
  1467  				`${jsonencode(emptymap)}`,
  1468  				`{}`,
  1469  				false,
  1470  			},
  1471  			{
  1472  				`${jsonencode(nestedlist)}`,
  1473  				nil,
  1474  				true,
  1475  			},
  1476  			{
  1477  				`${jsonencode(nestedmap)}`,
  1478  				nil,
  1479  				true,
  1480  			},
  1481  		},
  1482  	})
  1483  }
  1484  
  1485  func TestInterpolateFuncReplace(t *testing.T) {
  1486  	testFunction(t, testFunctionConfig{
  1487  		Cases: []testFunctionCase{
  1488  			// Regular search and replace
  1489  			{
  1490  				`${replace("hello", "hel", "bel")}`,
  1491  				"bello",
  1492  				false,
  1493  			},
  1494  
  1495  			// Search string doesn't match
  1496  			{
  1497  				`${replace("hello", "nope", "bel")}`,
  1498  				"hello",
  1499  				false,
  1500  			},
  1501  
  1502  			// Regular expression
  1503  			{
  1504  				`${replace("hello", "/l/", "L")}`,
  1505  				"heLLo",
  1506  				false,
  1507  			},
  1508  
  1509  			{
  1510  				`${replace("helo", "/(l)/", "$1$1")}`,
  1511  				"hello",
  1512  				false,
  1513  			},
  1514  
  1515  			// Bad regexp
  1516  			{
  1517  				`${replace("helo", "/(l/", "$1$1")}`,
  1518  				nil,
  1519  				true,
  1520  			},
  1521  		},
  1522  	})
  1523  }
  1524  
  1525  func TestInterpolateFuncLength(t *testing.T) {
  1526  	testFunction(t, testFunctionConfig{
  1527  		Cases: []testFunctionCase{
  1528  			// Raw strings
  1529  			{
  1530  				`${length("")}`,
  1531  				"0",
  1532  				false,
  1533  			},
  1534  			{
  1535  				`${length("a")}`,
  1536  				"1",
  1537  				false,
  1538  			},
  1539  			{
  1540  				`${length(" ")}`,
  1541  				"1",
  1542  				false,
  1543  			},
  1544  			{
  1545  				`${length(" a ,")}`,
  1546  				"4",
  1547  				false,
  1548  			},
  1549  			{
  1550  				`${length("aaa")}`,
  1551  				"3",
  1552  				false,
  1553  			},
  1554  
  1555  			// Lists
  1556  			{
  1557  				`${length(split(",", "a"))}`,
  1558  				"1",
  1559  				false,
  1560  			},
  1561  			{
  1562  				`${length(split(",", "foo,"))}`,
  1563  				"2",
  1564  				false,
  1565  			},
  1566  			{
  1567  				`${length(split(",", ",foo,"))}`,
  1568  				"3",
  1569  				false,
  1570  			},
  1571  			{
  1572  				`${length(split(",", "foo,bar"))}`,
  1573  				"2",
  1574  				false,
  1575  			},
  1576  			{
  1577  				`${length(split(".", "one.two.three.four.five"))}`,
  1578  				"5",
  1579  				false,
  1580  			},
  1581  			// Want length 0 if we split an empty string then compact
  1582  			{
  1583  				`${length(compact(split(",", "")))}`,
  1584  				"0",
  1585  				false,
  1586  			},
  1587  			// Works for maps
  1588  			{
  1589  				`${length(map("k", "v"))}`,
  1590  				"1",
  1591  				false,
  1592  			},
  1593  			{
  1594  				`${length(map("k1", "v1", "k2", "v2"))}`,
  1595  				"2",
  1596  				false,
  1597  			},
  1598  		},
  1599  	})
  1600  }
  1601  
  1602  func TestInterpolateFuncSignum(t *testing.T) {
  1603  	testFunction(t, testFunctionConfig{
  1604  		Cases: []testFunctionCase{
  1605  			{
  1606  				`${signum()}`,
  1607  				nil,
  1608  				true,
  1609  			},
  1610  
  1611  			{
  1612  				`${signum("")}`,
  1613  				nil,
  1614  				true,
  1615  			},
  1616  
  1617  			{
  1618  				`${signum(0)}`,
  1619  				"0",
  1620  				false,
  1621  			},
  1622  
  1623  			{
  1624  				`${signum(15)}`,
  1625  				"1",
  1626  				false,
  1627  			},
  1628  
  1629  			{
  1630  				`${signum(-29)}`,
  1631  				"-1",
  1632  				false,
  1633  			},
  1634  		},
  1635  	})
  1636  }
  1637  
  1638  func TestInterpolateFuncSlice(t *testing.T) {
  1639  	testFunction(t, testFunctionConfig{
  1640  		Cases: []testFunctionCase{
  1641  			// Negative from index
  1642  			{
  1643  				`${slice(list("a"), -1, 0)}`,
  1644  				nil,
  1645  				true,
  1646  			},
  1647  			// From index > to index
  1648  			{
  1649  				`${slice(list("a", "b", "c"), 2, 1)}`,
  1650  				nil,
  1651  				true,
  1652  			},
  1653  			// To index too large
  1654  			{
  1655  				`${slice(var.list_of_strings, 1, 4)}`,
  1656  				nil,
  1657  				true,
  1658  			},
  1659  			// Empty slice
  1660  			{
  1661  				`${slice(var.list_of_strings, 1, 1)}`,
  1662  				[]interface{}{},
  1663  				false,
  1664  			},
  1665  			{
  1666  				`${slice(var.list_of_strings, 1, 2)}`,
  1667  				[]interface{}{"b"},
  1668  				false,
  1669  			},
  1670  			{
  1671  				`${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`,
  1672  				[]interface{}{"a", "b"},
  1673  				false,
  1674  			},
  1675  		},
  1676  		Vars: map[string]ast.Variable{
  1677  			"var.list_of_strings": {
  1678  				Type: ast.TypeList,
  1679  				Value: []ast.Variable{
  1680  					{
  1681  						Type:  ast.TypeString,
  1682  						Value: "a",
  1683  					},
  1684  					{
  1685  						Type:  ast.TypeString,
  1686  						Value: "b",
  1687  					},
  1688  					{
  1689  						Type:  ast.TypeString,
  1690  						Value: "c",
  1691  					},
  1692  				},
  1693  			},
  1694  		},
  1695  	})
  1696  }
  1697  
  1698  func TestInterpolateFuncSort(t *testing.T) {
  1699  	testFunction(t, testFunctionConfig{
  1700  		Vars: map[string]ast.Variable{
  1701  			"var.strings": ast.Variable{
  1702  				Type: ast.TypeList,
  1703  				Value: []ast.Variable{
  1704  					{Type: ast.TypeString, Value: "c"},
  1705  					{Type: ast.TypeString, Value: "a"},
  1706  					{Type: ast.TypeString, Value: "b"},
  1707  				},
  1708  			},
  1709  			"var.notstrings": ast.Variable{
  1710  				Type: ast.TypeList,
  1711  				Value: []ast.Variable{
  1712  					{Type: ast.TypeList, Value: []ast.Variable{}},
  1713  					{Type: ast.TypeString, Value: "b"},
  1714  				},
  1715  			},
  1716  		},
  1717  		Cases: []testFunctionCase{
  1718  			{
  1719  				`${sort(var.strings)}`,
  1720  				[]interface{}{"a", "b", "c"},
  1721  				false,
  1722  			},
  1723  			{
  1724  				`${sort(var.notstrings)}`,
  1725  				nil,
  1726  				true,
  1727  			},
  1728  		},
  1729  	})
  1730  }
  1731  
  1732  func TestInterpolateFuncSplit(t *testing.T) {
  1733  	testFunction(t, testFunctionConfig{
  1734  		Cases: []testFunctionCase{
  1735  			{
  1736  				`${split(",")}`,
  1737  				nil,
  1738  				true,
  1739  			},
  1740  
  1741  			{
  1742  				`${split(",", "")}`,
  1743  				[]interface{}{""},
  1744  				false,
  1745  			},
  1746  
  1747  			{
  1748  				`${split(",", "foo")}`,
  1749  				[]interface{}{"foo"},
  1750  				false,
  1751  			},
  1752  
  1753  			{
  1754  				`${split(",", ",,,")}`,
  1755  				[]interface{}{"", "", "", ""},
  1756  				false,
  1757  			},
  1758  
  1759  			{
  1760  				`${split(",", "foo,")}`,
  1761  				[]interface{}{"foo", ""},
  1762  				false,
  1763  			},
  1764  
  1765  			{
  1766  				`${split(",", ",foo,")}`,
  1767  				[]interface{}{"", "foo", ""},
  1768  				false,
  1769  			},
  1770  
  1771  			{
  1772  				`${split(".", "foo.bar.baz")}`,
  1773  				[]interface{}{"foo", "bar", "baz"},
  1774  				false,
  1775  			},
  1776  		},
  1777  	})
  1778  }
  1779  
  1780  func TestInterpolateFuncLookup(t *testing.T) {
  1781  	testFunction(t, testFunctionConfig{
  1782  		Vars: map[string]ast.Variable{
  1783  			"var.foo": {
  1784  				Type: ast.TypeMap,
  1785  				Value: map[string]ast.Variable{
  1786  					"bar": {
  1787  						Type:  ast.TypeString,
  1788  						Value: "baz",
  1789  					},
  1790  				},
  1791  			},
  1792  			"var.map_of_lists": ast.Variable{
  1793  				Type: ast.TypeMap,
  1794  				Value: map[string]ast.Variable{
  1795  					"bar": {
  1796  						Type: ast.TypeList,
  1797  						Value: []ast.Variable{
  1798  							{
  1799  								Type:  ast.TypeString,
  1800  								Value: "baz",
  1801  							},
  1802  						},
  1803  					},
  1804  				},
  1805  			},
  1806  		},
  1807  		Cases: []testFunctionCase{
  1808  			{
  1809  				`${lookup(var.foo, "bar")}`,
  1810  				"baz",
  1811  				false,
  1812  			},
  1813  
  1814  			// Invalid key
  1815  			{
  1816  				`${lookup(var.foo, "baz")}`,
  1817  				nil,
  1818  				true,
  1819  			},
  1820  
  1821  			// Supplied default with valid key
  1822  			{
  1823  				`${lookup(var.foo, "bar", "")}`,
  1824  				"baz",
  1825  				false,
  1826  			},
  1827  
  1828  			// Supplied default with invalid key
  1829  			{
  1830  				`${lookup(var.foo, "zip", "")}`,
  1831  				"",
  1832  				false,
  1833  			},
  1834  
  1835  			// Too many args
  1836  			{
  1837  				`${lookup(var.foo, "bar", "", "abc")}`,
  1838  				nil,
  1839  				true,
  1840  			},
  1841  
  1842  			// Cannot lookup into map of lists
  1843  			{
  1844  				`${lookup(var.map_of_lists, "bar")}`,
  1845  				nil,
  1846  				true,
  1847  			},
  1848  
  1849  			// Non-empty default
  1850  			{
  1851  				`${lookup(var.foo, "zap", "xyz")}`,
  1852  				"xyz",
  1853  				false,
  1854  			},
  1855  		},
  1856  	})
  1857  }
  1858  
  1859  func TestInterpolateFuncKeys(t *testing.T) {
  1860  	testFunction(t, testFunctionConfig{
  1861  		Vars: map[string]ast.Variable{
  1862  			"var.foo": ast.Variable{
  1863  				Type: ast.TypeMap,
  1864  				Value: map[string]ast.Variable{
  1865  					"bar": ast.Variable{
  1866  						Value: "baz",
  1867  						Type:  ast.TypeString,
  1868  					},
  1869  					"qux": ast.Variable{
  1870  						Value: "quack",
  1871  						Type:  ast.TypeString,
  1872  					},
  1873  				},
  1874  			},
  1875  			"var.str": ast.Variable{
  1876  				Value: "astring",
  1877  				Type:  ast.TypeString,
  1878  			},
  1879  		},
  1880  		Cases: []testFunctionCase{
  1881  			{
  1882  				`${keys(var.foo)}`,
  1883  				[]interface{}{"bar", "qux"},
  1884  				false,
  1885  			},
  1886  
  1887  			// Invalid key
  1888  			{
  1889  				`${keys(var.not)}`,
  1890  				nil,
  1891  				true,
  1892  			},
  1893  
  1894  			// Too many args
  1895  			{
  1896  				`${keys(var.foo, "bar")}`,
  1897  				nil,
  1898  				true,
  1899  			},
  1900  
  1901  			// Not a map
  1902  			{
  1903  				`${keys(var.str)}`,
  1904  				nil,
  1905  				true,
  1906  			},
  1907  		},
  1908  	})
  1909  }
  1910  
  1911  // Confirm that keys return in sorted order, and values return in the order of
  1912  // their sorted keys.
  1913  func TestInterpolateFuncKeyValOrder(t *testing.T) {
  1914  	testFunction(t, testFunctionConfig{
  1915  		Vars: map[string]ast.Variable{
  1916  			"var.foo": ast.Variable{
  1917  				Type: ast.TypeMap,
  1918  				Value: map[string]ast.Variable{
  1919  					"D": ast.Variable{
  1920  						Value: "2",
  1921  						Type:  ast.TypeString,
  1922  					},
  1923  					"C": ast.Variable{
  1924  						Value: "Y",
  1925  						Type:  ast.TypeString,
  1926  					},
  1927  					"A": ast.Variable{
  1928  						Value: "X",
  1929  						Type:  ast.TypeString,
  1930  					},
  1931  					"10": ast.Variable{
  1932  						Value: "Z",
  1933  						Type:  ast.TypeString,
  1934  					},
  1935  					"1": ast.Variable{
  1936  						Value: "4",
  1937  						Type:  ast.TypeString,
  1938  					},
  1939  					"3": ast.Variable{
  1940  						Value: "W",
  1941  						Type:  ast.TypeString,
  1942  					},
  1943  				},
  1944  			},
  1945  		},
  1946  		Cases: []testFunctionCase{
  1947  			{
  1948  				`${keys(var.foo)}`,
  1949  				[]interface{}{"1", "10", "3", "A", "C", "D"},
  1950  				false,
  1951  			},
  1952  
  1953  			{
  1954  				`${values(var.foo)}`,
  1955  				[]interface{}{"4", "Z", "W", "X", "Y", "2"},
  1956  				false,
  1957  			},
  1958  		},
  1959  	})
  1960  }
  1961  
  1962  func TestInterpolateFuncValues(t *testing.T) {
  1963  	testFunction(t, testFunctionConfig{
  1964  		Vars: map[string]ast.Variable{
  1965  			"var.foo": ast.Variable{
  1966  				Type: ast.TypeMap,
  1967  				Value: map[string]ast.Variable{
  1968  					"bar": ast.Variable{
  1969  						Value: "quack",
  1970  						Type:  ast.TypeString,
  1971  					},
  1972  					"qux": ast.Variable{
  1973  						Value: "baz",
  1974  						Type:  ast.TypeString,
  1975  					},
  1976  				},
  1977  			},
  1978  			"var.str": ast.Variable{
  1979  				Value: "astring",
  1980  				Type:  ast.TypeString,
  1981  			},
  1982  		},
  1983  		Cases: []testFunctionCase{
  1984  			{
  1985  				`${values(var.foo)}`,
  1986  				[]interface{}{"quack", "baz"},
  1987  				false,
  1988  			},
  1989  
  1990  			// Invalid key
  1991  			{
  1992  				`${values(var.not)}`,
  1993  				nil,
  1994  				true,
  1995  			},
  1996  
  1997  			// Too many args
  1998  			{
  1999  				`${values(var.foo, "bar")}`,
  2000  				nil,
  2001  				true,
  2002  			},
  2003  
  2004  			// Not a map
  2005  			{
  2006  				`${values(var.str)}`,
  2007  				nil,
  2008  				true,
  2009  			},
  2010  
  2011  			// Map of lists
  2012  			{
  2013  				`${values(map("one", list()))}`,
  2014  				nil,
  2015  				true,
  2016  			},
  2017  		},
  2018  	})
  2019  }
  2020  
  2021  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
  2022  	variable, _ := hil.InterfaceToVariable(input)
  2023  	return variable
  2024  }
  2025  
  2026  func TestInterpolateFuncElement(t *testing.T) {
  2027  	testFunction(t, testFunctionConfig{
  2028  		Vars: map[string]ast.Variable{
  2029  			"var.a_list":        interfaceToVariableSwallowError([]string{"foo", "baz"}),
  2030  			"var.a_short_list":  interfaceToVariableSwallowError([]string{"foo"}),
  2031  			"var.empty_list":    interfaceToVariableSwallowError([]interface{}{}),
  2032  			"var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}),
  2033  		},
  2034  		Cases: []testFunctionCase{
  2035  			{
  2036  				`${element(var.a_list, "1")}`,
  2037  				"baz",
  2038  				false,
  2039  			},
  2040  
  2041  			{
  2042  				`${element(var.a_short_list, "0")}`,
  2043  				"foo",
  2044  				false,
  2045  			},
  2046  
  2047  			// Invalid index should wrap vs. out-of-bounds
  2048  			{
  2049  				`${element(var.a_list, "2")}`,
  2050  				"foo",
  2051  				false,
  2052  			},
  2053  
  2054  			// Negative number should fail
  2055  			{
  2056  				`${element(var.a_short_list, "-1")}`,
  2057  				nil,
  2058  				true,
  2059  			},
  2060  
  2061  			// Empty list should fail
  2062  			{
  2063  				`${element(var.empty_list, 0)}`,
  2064  				nil,
  2065  				true,
  2066  			},
  2067  
  2068  			// Too many args
  2069  			{
  2070  				`${element(var.a_list, "0", "2")}`,
  2071  				nil,
  2072  				true,
  2073  			},
  2074  
  2075  			// Only works on single-level lists
  2076  			{
  2077  				`${element(var.a_nested_list, "0")}`,
  2078  				nil,
  2079  				true,
  2080  			},
  2081  		},
  2082  	})
  2083  }
  2084  
  2085  func TestInterpolateFuncBasename(t *testing.T) {
  2086  	testFunction(t, testFunctionConfig{
  2087  		Cases: []testFunctionCase{
  2088  			{
  2089  				`${basename("/foo/bar/baz")}`,
  2090  				"baz",
  2091  				false,
  2092  			},
  2093  		},
  2094  	})
  2095  }
  2096  
  2097  func TestInterpolateFuncBase64Encode(t *testing.T) {
  2098  	testFunction(t, testFunctionConfig{
  2099  		Cases: []testFunctionCase{
  2100  			// Regular base64 encoding
  2101  			{
  2102  				`${base64encode("abc123!?$*&()'-=@~")}`,
  2103  				"YWJjMTIzIT8kKiYoKSctPUB+",
  2104  				false,
  2105  			},
  2106  		},
  2107  	})
  2108  }
  2109  
  2110  func TestInterpolateFuncBase64Decode(t *testing.T) {
  2111  	testFunction(t, testFunctionConfig{
  2112  		Cases: []testFunctionCase{
  2113  			// Regular base64 decoding
  2114  			{
  2115  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
  2116  				"abc123!?$*&()'-=@~",
  2117  				false,
  2118  			},
  2119  
  2120  			// Invalid base64 data decoding
  2121  			{
  2122  				`${base64decode("this-is-an-invalid-base64-data")}`,
  2123  				nil,
  2124  				true,
  2125  			},
  2126  		},
  2127  	})
  2128  }
  2129  
  2130  func TestInterpolateFuncLower(t *testing.T) {
  2131  	testFunction(t, testFunctionConfig{
  2132  		Cases: []testFunctionCase{
  2133  			{
  2134  				`${lower("HELLO")}`,
  2135  				"hello",
  2136  				false,
  2137  			},
  2138  
  2139  			{
  2140  				`${lower("")}`,
  2141  				"",
  2142  				false,
  2143  			},
  2144  
  2145  			{
  2146  				`${lower()}`,
  2147  				nil,
  2148  				true,
  2149  			},
  2150  		},
  2151  	})
  2152  }
  2153  
  2154  func TestInterpolateFuncUpper(t *testing.T) {
  2155  	testFunction(t, testFunctionConfig{
  2156  		Cases: []testFunctionCase{
  2157  			{
  2158  				`${upper("hello")}`,
  2159  				"HELLO",
  2160  				false,
  2161  			},
  2162  
  2163  			{
  2164  				`${upper("")}`,
  2165  				"",
  2166  				false,
  2167  			},
  2168  
  2169  			{
  2170  				`${upper()}`,
  2171  				nil,
  2172  				true,
  2173  			},
  2174  		},
  2175  	})
  2176  }
  2177  
  2178  func TestInterpolateFuncSha1(t *testing.T) {
  2179  	testFunction(t, testFunctionConfig{
  2180  		Cases: []testFunctionCase{
  2181  			{
  2182  				`${sha1("test")}`,
  2183  				"a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
  2184  				false,
  2185  			},
  2186  		},
  2187  	})
  2188  }
  2189  
  2190  func TestInterpolateFuncSha256(t *testing.T) {
  2191  	testFunction(t, testFunctionConfig{
  2192  		Cases: []testFunctionCase{
  2193  			{ // hexadecimal representation of sha256 sum
  2194  				`${sha256("test")}`,
  2195  				"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  2196  				false,
  2197  			},
  2198  		},
  2199  	})
  2200  }
  2201  
  2202  func TestInterpolateFuncSha512(t *testing.T) {
  2203  	testFunction(t, testFunctionConfig{
  2204  		Cases: []testFunctionCase{
  2205  			{
  2206  				`${sha512("test")}`,
  2207  				"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff",
  2208  				false,
  2209  			},
  2210  		},
  2211  	})
  2212  }
  2213  
  2214  func TestInterpolateFuncTitle(t *testing.T) {
  2215  	testFunction(t, testFunctionConfig{
  2216  		Cases: []testFunctionCase{
  2217  			{
  2218  				`${title("hello")}`,
  2219  				"Hello",
  2220  				false,
  2221  			},
  2222  
  2223  			{
  2224  				`${title("hello world")}`,
  2225  				"Hello World",
  2226  				false,
  2227  			},
  2228  
  2229  			{
  2230  				`${title("")}`,
  2231  				"",
  2232  				false,
  2233  			},
  2234  
  2235  			{
  2236  				`${title()}`,
  2237  				nil,
  2238  				true,
  2239  			},
  2240  		},
  2241  	})
  2242  }
  2243  
  2244  func TestInterpolateFuncTrimSpace(t *testing.T) {
  2245  	testFunction(t, testFunctionConfig{
  2246  		Cases: []testFunctionCase{
  2247  			{
  2248  				`${trimspace(" test ")}`,
  2249  				"test",
  2250  				false,
  2251  			},
  2252  		},
  2253  	})
  2254  }
  2255  
  2256  func TestInterpolateFuncBase64Sha256(t *testing.T) {
  2257  	testFunction(t, testFunctionConfig{
  2258  		Cases: []testFunctionCase{
  2259  			{
  2260  				`${base64sha256("test")}`,
  2261  				"n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=",
  2262  				false,
  2263  			},
  2264  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  2265  				`${base64encode(sha256("test"))}`,
  2266  				"OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==",
  2267  				false,
  2268  			},
  2269  		},
  2270  	})
  2271  }
  2272  
  2273  func TestInterpolateFuncBase64Sha512(t *testing.T) {
  2274  	testFunction(t, testFunctionConfig{
  2275  		Cases: []testFunctionCase{
  2276  			{
  2277  				`${base64sha512("test")}`,
  2278  				"7iaw3Ur350mqGo7jwQrpkj9hiYB3Lkc/iBml1JQODbJ6wYX4oOHV+E+IvIh/1nsUNzLDBMxfqa2Ob1f1ACio/w==",
  2279  				false,
  2280  			},
  2281  			{ // This will differ because we're base64-encoding hex represantiation, not raw bytes
  2282  				`${base64encode(sha512("test"))}`,
  2283  				"ZWUyNmIwZGQ0YWY3ZTc0OWFhMWE4ZWUzYzEwYWU5OTIzZjYxODk4MDc3MmU0NzNmODgxOWE1ZDQ5NDBlMGRiMjdhYzE4NWY4YTBlMWQ1Zjg0Zjg4YmM4ODdmZDY3YjE0MzczMmMzMDRjYzVmYTlhZDhlNmY1N2Y1MDAyOGE4ZmY=",
  2284  				false,
  2285  			},
  2286  		},
  2287  	})
  2288  }
  2289  
  2290  func TestInterpolateFuncMd5(t *testing.T) {
  2291  	testFunction(t, testFunctionConfig{
  2292  		Cases: []testFunctionCase{
  2293  			{
  2294  				`${md5("tada")}`,
  2295  				"ce47d07243bb6eaf5e1322c81baf9bbf",
  2296  				false,
  2297  			},
  2298  			{ // Confirm that we're not trimming any whitespaces
  2299  				`${md5(" tada ")}`,
  2300  				"aadf191a583e53062de2d02c008141c4",
  2301  				false,
  2302  			},
  2303  			{ // We accept empty string too
  2304  				`${md5("")}`,
  2305  				"d41d8cd98f00b204e9800998ecf8427e",
  2306  				false,
  2307  			},
  2308  		},
  2309  	})
  2310  }
  2311  
  2312  func TestInterpolateFuncUUID(t *testing.T) {
  2313  	results := make(map[string]bool)
  2314  
  2315  	for i := 0; i < 100; i++ {
  2316  		ast, err := hil.Parse("${uuid()}")
  2317  		if err != nil {
  2318  			t.Fatalf("err: %s", err)
  2319  		}
  2320  
  2321  		result, err := hil.Eval(ast, langEvalConfig(nil))
  2322  		if err != nil {
  2323  			t.Fatalf("err: %s", err)
  2324  		}
  2325  
  2326  		if results[result.Value.(string)] {
  2327  			t.Fatalf("Got unexpected duplicate uuid: %s", result.Value)
  2328  		}
  2329  
  2330  		results[result.Value.(string)] = true
  2331  	}
  2332  }
  2333  
  2334  func TestInterpolateFuncTimestamp(t *testing.T) {
  2335  	currentTime := time.Now().UTC()
  2336  	ast, err := hil.Parse("${timestamp()}")
  2337  	if err != nil {
  2338  		t.Fatalf("err: %s", err)
  2339  	}
  2340  
  2341  	result, err := hil.Eval(ast, langEvalConfig(nil))
  2342  	if err != nil {
  2343  		t.Fatalf("err: %s", err)
  2344  	}
  2345  	resultTime, err := time.Parse(time.RFC3339, result.Value.(string))
  2346  	if err != nil {
  2347  		t.Fatalf("Error parsing timestamp: %s", err)
  2348  	}
  2349  
  2350  	if resultTime.Sub(currentTime).Seconds() > 10.0 {
  2351  		t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string))
  2352  	}
  2353  }
  2354  
  2355  type testFunctionConfig struct {
  2356  	Cases []testFunctionCase
  2357  	Vars  map[string]ast.Variable
  2358  }
  2359  
  2360  type testFunctionCase struct {
  2361  	Input  string
  2362  	Result interface{}
  2363  	Error  bool
  2364  }
  2365  
  2366  func testFunction(t *testing.T, config testFunctionConfig) {
  2367  	for i, tc := range config.Cases {
  2368  		ast, err := hil.Parse(tc.Input)
  2369  		if err != nil {
  2370  			t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err)
  2371  		}
  2372  
  2373  		result, err := hil.Eval(ast, langEvalConfig(config.Vars))
  2374  		if err != nil != tc.Error {
  2375  			t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err)
  2376  		}
  2377  
  2378  		if !reflect.DeepEqual(result.Value, tc.Result) {
  2379  			t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v",
  2380  				i, tc.Input, result.Value, tc.Result)
  2381  		}
  2382  	}
  2383  }
  2384  
  2385  func TestInterpolateFuncPathExpand(t *testing.T) {
  2386  	homePath, err := homedir.Dir()
  2387  	if err != nil {
  2388  		t.Fatalf("Error getting home directory: %v", err)
  2389  	}
  2390  	testFunction(t, testFunctionConfig{
  2391  		Cases: []testFunctionCase{
  2392  			{
  2393  				`${pathexpand("~/test-file")}`,
  2394  				filepath.Join(homePath, "test-file"),
  2395  				false,
  2396  			},
  2397  			{
  2398  				`${pathexpand("~/another/test/file")}`,
  2399  				filepath.Join(homePath, "another/test/file"),
  2400  				false,
  2401  			},
  2402  			{
  2403  				`${pathexpand("/root/file")}`,
  2404  				"/root/file",
  2405  				false,
  2406  			},
  2407  			{
  2408  				`${pathexpand("/")}`,
  2409  				"/",
  2410  				false,
  2411  			},
  2412  			{
  2413  				`${pathexpand()}`,
  2414  				nil,
  2415  				true,
  2416  			},
  2417  		},
  2418  	})
  2419  }
  2420  
  2421  func TestInterpolateFuncSubstr(t *testing.T) {
  2422  	testFunction(t, testFunctionConfig{
  2423  		Cases: []testFunctionCase{
  2424  			{
  2425  				`${substr("foobar", 0, 0)}`,
  2426  				"",
  2427  				false,
  2428  			},
  2429  			{
  2430  				`${substr("foobar", 0, -1)}`,
  2431  				"foobar",
  2432  				false,
  2433  			},
  2434  			{
  2435  				`${substr("foobar", 0, 3)}`,
  2436  				"foo",
  2437  				false,
  2438  			},
  2439  			{
  2440  				`${substr("foobar", 3, 3)}`,
  2441  				"bar",
  2442  				false,
  2443  			},
  2444  			{
  2445  				`${substr("foobar", -3, 3)}`,
  2446  				"bar",
  2447  				false,
  2448  			},
  2449  
  2450  			// empty string
  2451  			{
  2452  				`${substr("", 0, 0)}`,
  2453  				"",
  2454  				false,
  2455  			},
  2456  
  2457  			// invalid offset
  2458  			{
  2459  				`${substr("", 1, 0)}`,
  2460  				nil,
  2461  				true,
  2462  			},
  2463  
  2464  			// invalid length
  2465  			{
  2466  				`${substr("", 0, 1)}`,
  2467  				nil,
  2468  				true,
  2469  			},
  2470  			{
  2471  				`${substr("", 0, -2)}`,
  2472  				nil,
  2473  				true,
  2474  			},
  2475  		},
  2476  	})
  2477  }
  2478  
  2479  func TestInterpolateFuncBcrypt(t *testing.T) {
  2480  	node, err := hil.Parse(`${bcrypt("test")}`)
  2481  	if err != nil {
  2482  		t.Fatalf("err: %s", err)
  2483  	}
  2484  
  2485  	result, err := hil.Eval(node, langEvalConfig(nil))
  2486  	if err != nil {
  2487  		t.Fatalf("err: %s", err)
  2488  	}
  2489  	err = bcrypt.CompareHashAndPassword([]byte(result.Value.(string)), []byte("test"))
  2490  
  2491  	if err != nil {
  2492  		t.Fatalf("Error comparing hash and password: %s", err)
  2493  	}
  2494  
  2495  	testFunction(t, testFunctionConfig{
  2496  		Cases: []testFunctionCase{
  2497  			//Negative test for more than two parameters
  2498  			{
  2499  				`${bcrypt("test", 15, 12)}`,
  2500  				nil,
  2501  				true,
  2502  			},
  2503  		},
  2504  	})
  2505  }