github.com/hashicorp/hcl/v2@v2.20.0/ext/userfunc/decode_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package userfunc 5 6 import ( 7 "fmt" 8 "testing" 9 10 "github.com/hashicorp/hcl/v2" 11 "github.com/hashicorp/hcl/v2/hclsyntax" 12 "github.com/zclconf/go-cty/cty" 13 ) 14 15 func TestDecodeUserFunctions(t *testing.T) { 16 tests := []struct { 17 src string 18 testExpr string 19 baseCtx *hcl.EvalContext 20 want cty.Value 21 diagCount int 22 }{ 23 { 24 ` 25 function "greet" { 26 params = [name] 27 result = "Hello, ${name}." 28 } 29 `, 30 `greet("Ermintrude")`, 31 nil, 32 cty.StringVal("Hello, Ermintrude."), 33 0, 34 }, 35 { 36 ` 37 function "greet" { 38 params = [name] 39 result = "Hello, ${name}." 40 } 41 `, 42 `greet()`, 43 nil, 44 cty.DynamicVal, 45 1, // missing value for "name" 46 }, 47 { 48 ` 49 function "greet" { 50 params = [name] 51 result = "Hello, ${name}." 52 } 53 `, 54 `greet("Ermintrude", "extra")`, 55 nil, 56 cty.DynamicVal, 57 1, // too many arguments 58 }, 59 { 60 ` 61 function "add" { 62 params = [a, b] 63 result = a + b 64 } 65 `, 66 `add(1, 5)`, 67 nil, 68 cty.NumberIntVal(6), 69 0, 70 }, 71 { 72 ` 73 function "argstuple" { 74 params = [] 75 variadic_param = args 76 result = args 77 } 78 `, 79 `argstuple("a", true, 1)`, 80 nil, 81 cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.True, cty.NumberIntVal(1)}), 82 0, 83 }, 84 { 85 ` 86 function "missing_var" { 87 params = [] 88 result = nonexist 89 } 90 `, 91 `missing_var()`, 92 nil, 93 cty.DynamicVal, 94 1, // no variable named "nonexist" 95 }, 96 { 97 ` 98 function "closure" { 99 params = [] 100 result = upvalue 101 } 102 `, 103 `closure()`, 104 &hcl.EvalContext{ 105 Variables: map[string]cty.Value{ 106 "upvalue": cty.True, 107 }, 108 }, 109 cty.True, 110 0, 111 }, 112 { 113 ` 114 function "neg" { 115 params = [val] 116 result = -val 117 } 118 function "add" { 119 params = [a, b] 120 result = a + b 121 } 122 `, 123 `neg(add(1, 3))`, 124 nil, 125 cty.NumberIntVal(-4), 126 0, 127 }, 128 { 129 ` 130 function "neg" { 131 parrams = [val] 132 result = -val 133 } 134 `, 135 `null`, 136 nil, 137 cty.NullVal(cty.DynamicPseudoType), 138 2, // missing attribute "params", and unknown attribute "parrams" 139 }, 140 } 141 142 for i, test := range tests { 143 t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { 144 f, diags := hclsyntax.ParseConfig([]byte(test.src), "config", hcl.Pos{Line: 1, Column: 1}) 145 if f == nil || f.Body == nil { 146 t.Fatalf("got nil file or body") 147 } 148 149 funcs, _, funcsDiags := decodeUserFunctions(f.Body, "function", func() *hcl.EvalContext { 150 return test.baseCtx 151 }) 152 diags = append(diags, funcsDiags...) 153 154 expr, exprParseDiags := hclsyntax.ParseExpression([]byte(test.testExpr), "testexpr", hcl.Pos{Line: 1, Column: 1}) 155 diags = append(diags, exprParseDiags...) 156 if expr == nil { 157 t.Fatalf("parsing test expr returned nil") 158 } 159 160 got, exprDiags := expr.Value(&hcl.EvalContext{ 161 Functions: funcs, 162 }) 163 diags = append(diags, exprDiags...) 164 165 if len(diags) != test.diagCount { 166 t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount) 167 for _, diag := range diags { 168 t.Logf("- %s", diag) 169 } 170 } 171 172 if !got.RawEquals(test.want) { 173 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) 174 } 175 }) 176 } 177 }