package userfunc import ( "fmt" "testing" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/zclconf/go-cty/cty" ) func TestDecodeUserFunctions(t *testing.T) { tests := []struct { src string testExpr string baseCtx *hcl.EvalContext want cty.Value diagCount int }{ { ` function "greet" { params = [name] result = "Hello, ${name}." } `, `greet("Ermintrude")`, nil, cty.StringVal("Hello, Ermintrude."), 0, }, { ` function "greet" { params = [name] result = "Hello, ${name}." } `, `greet()`, nil, cty.DynamicVal, 1, // missing value for "name" }, { ` function "greet" { params = [name] result = "Hello, ${name}." } `, `greet("Ermintrude", "extra")`, nil, cty.DynamicVal, 1, // too many arguments }, { ` function "add" { params = [a, b] result = a + b } `, `add(1, 5)`, nil, cty.NumberIntVal(6), 0, }, { ` function "argstuple" { params = [] variadic_param = args result = args } `, `argstuple("a", true, 1)`, nil, cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.True, cty.NumberIntVal(1)}), 0, }, { ` function "missing_var" { params = [] result = nonexist } `, `missing_var()`, nil, cty.DynamicVal, 1, // no variable named "nonexist" }, { ` function "closure" { params = [] result = upvalue } `, `closure()`, &hcl.EvalContext{ Variables: map[string]cty.Value{ "upvalue": cty.True, }, }, cty.True, 0, }, { ` function "neg" { params = [val] result = -val } function "add" { params = [a, b] result = a + b } `, `neg(add(1, 3))`, nil, cty.NumberIntVal(-4), 0, }, { ` function "neg" { parrams = [val] result = -val } `, `null`, nil, cty.NullVal(cty.DynamicPseudoType), 2, // missing attribute "params", and unknown attribute "parrams" }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { f, diags := hclsyntax.ParseConfig([]byte(test.src), "config", hcl.Pos{Line: 1, Column: 1}) if f == nil || f.Body == nil { t.Fatalf("got nil file or body") } funcs, _, funcsDiags := decodeUserFunctions(f.Body, "function", func() *hcl.EvalContext { return test.baseCtx }) diags = append(diags, funcsDiags...) expr, exprParseDiags := hclsyntax.ParseExpression([]byte(test.testExpr), "testexpr", hcl.Pos{Line: 1, Column: 1}) diags = append(diags, exprParseDiags...) if expr == nil { t.Fatalf("parsing test expr returned nil") } got, exprDiags := expr.Value(&hcl.EvalContext{ Functions: funcs, }) diags = append(diags, exprDiags...) if len(diags) != test.diagCount { t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount) for _, diag := range diags { t.Logf("- %s", diag) } } if !got.RawEquals(test.want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } }