194 lines
4.7 KiB
Go
194 lines
4.7 KiB
Go
|
package tryfunc
|
||
|
|
||
|
import (
|
||
|
"testing"
|
||
|
|
||
|
"github.com/hashicorp/hcl/v2"
|
||
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||
|
"github.com/zclconf/go-cty/cty"
|
||
|
"github.com/zclconf/go-cty/cty/function"
|
||
|
)
|
||
|
|
||
|
func TestTryFunc(t *testing.T) {
|
||
|
tests := map[string]struct {
|
||
|
expr string
|
||
|
vars map[string]cty.Value
|
||
|
want cty.Value
|
||
|
wantErr string
|
||
|
}{
|
||
|
"one argument succeeds": {
|
||
|
`try(1)`,
|
||
|
nil,
|
||
|
cty.NumberIntVal(1),
|
||
|
``,
|
||
|
},
|
||
|
"two arguments, first succeeds": {
|
||
|
`try(1, 2)`,
|
||
|
nil,
|
||
|
cty.NumberIntVal(1),
|
||
|
``,
|
||
|
},
|
||
|
"two arguments, first fails": {
|
||
|
`try(nope, 2)`,
|
||
|
nil,
|
||
|
cty.NumberIntVal(2),
|
||
|
``,
|
||
|
},
|
||
|
"two arguments, first depends on unknowns": {
|
||
|
`try(unknown, 2)`,
|
||
|
map[string]cty.Value{
|
||
|
"unknown": cty.UnknownVal(cty.Number),
|
||
|
},
|
||
|
cty.DynamicVal, // can't proceed until first argument is known
|
||
|
``,
|
||
|
},
|
||
|
"two arguments, first succeeds and second depends on unknowns": {
|
||
|
`try(1, unknown)`,
|
||
|
map[string]cty.Value{
|
||
|
"unknown": cty.UnknownVal(cty.Number),
|
||
|
},
|
||
|
cty.NumberIntVal(1), // we know 1st succeeds, so it doesn't matter that 2nd is unknown
|
||
|
``,
|
||
|
},
|
||
|
"two arguments, first depends on unknowns deeply": {
|
||
|
`try(has_unknowns, 2)`,
|
||
|
map[string]cty.Value{
|
||
|
"has_unknowns": cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
|
||
|
},
|
||
|
cty.DynamicVal, // can't proceed until first argument is wholly known
|
||
|
``,
|
||
|
},
|
||
|
"two arguments, first traverses through an unkown": {
|
||
|
`try(unknown.baz, 2)`,
|
||
|
map[string]cty.Value{
|
||
|
"unknown": cty.UnknownVal(cty.Map(cty.String)),
|
||
|
},
|
||
|
cty.DynamicVal, // can't proceed until first argument is wholly known
|
||
|
``,
|
||
|
},
|
||
|
"three arguments, all fail": {
|
||
|
`try(this, that, this_thing_in_particular)`,
|
||
|
nil,
|
||
|
cty.NumberIntVal(2),
|
||
|
// The grammar of this stringification of the message is unfortunate,
|
||
|
// but caller can type-assert our result to get the original
|
||
|
// diagnostics directly in order to produce a better result.
|
||
|
`test.hcl:1,1-5: Error in function call; Call to function "try" failed: no expression succeeded:
|
||
|
- Variables not allowed (at test.hcl:1,5-9)
|
||
|
Variables may not be used here.
|
||
|
- Variables not allowed (at test.hcl:1,11-15)
|
||
|
Variables may not be used here.
|
||
|
- Variables not allowed (at test.hcl:1,17-41)
|
||
|
Variables may not be used here.
|
||
|
|
||
|
At least one expression must produce a successful result.`,
|
||
|
},
|
||
|
"no arguments": {
|
||
|
`try()`,
|
||
|
nil,
|
||
|
cty.NilVal,
|
||
|
`test.hcl:1,1-5: Error in function call; Call to function "try" failed: at least one argument is required.`,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for k, test := range tests {
|
||
|
t.Run(k, func(t *testing.T) {
|
||
|
expr, diags := hclsyntax.ParseExpression([]byte(test.expr), "test.hcl", hcl.Pos{Line: 1, Column: 1})
|
||
|
if diags.HasErrors() {
|
||
|
t.Fatalf("unexpected problems: %s", diags.Error())
|
||
|
}
|
||
|
|
||
|
ctx := &hcl.EvalContext{
|
||
|
Variables: test.vars,
|
||
|
Functions: map[string]function.Function{
|
||
|
"try": TryFunc,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
got, err := expr.Value(ctx)
|
||
|
|
||
|
if err != nil {
|
||
|
if test.wantErr != "" {
|
||
|
if got, want := err.Error(), test.wantErr; got != want {
|
||
|
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
|
||
|
}
|
||
|
} else {
|
||
|
t.Errorf("unexpected error\ngot: %s\nwant: <nil>", err)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
if test.wantErr != "" {
|
||
|
t.Errorf("wrong error\ngot: <nil>\nwant: %s", test.wantErr)
|
||
|
}
|
||
|
|
||
|
if !test.want.RawEquals(got) {
|
||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCanFunc(t *testing.T) {
|
||
|
tests := map[string]struct {
|
||
|
expr string
|
||
|
vars map[string]cty.Value
|
||
|
want cty.Value
|
||
|
}{
|
||
|
"succeeds": {
|
||
|
`can(1)`,
|
||
|
nil,
|
||
|
cty.True,
|
||
|
},
|
||
|
"fails": {
|
||
|
`can(nope)`,
|
||
|
nil,
|
||
|
cty.False,
|
||
|
},
|
||
|
"simple unknown": {
|
||
|
`can(unknown)`,
|
||
|
map[string]cty.Value{
|
||
|
"unknown": cty.UnknownVal(cty.Number),
|
||
|
},
|
||
|
cty.UnknownVal(cty.Bool),
|
||
|
},
|
||
|
"traversal through unknown": {
|
||
|
`can(unknown.foo)`,
|
||
|
map[string]cty.Value{
|
||
|
"unknown": cty.UnknownVal(cty.Map(cty.Number)),
|
||
|
},
|
||
|
cty.UnknownVal(cty.Bool),
|
||
|
},
|
||
|
"deep unknown": {
|
||
|
`can(has_unknown)`,
|
||
|
map[string]cty.Value{
|
||
|
"has_unknown": cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}),
|
||
|
},
|
||
|
cty.UnknownVal(cty.Bool),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for k, test := range tests {
|
||
|
t.Run(k, func(t *testing.T) {
|
||
|
expr, diags := hclsyntax.ParseExpression([]byte(test.expr), "test.hcl", hcl.Pos{Line: 1, Column: 1})
|
||
|
if diags.HasErrors() {
|
||
|
t.Fatalf("unexpected problems: %s", diags.Error())
|
||
|
}
|
||
|
|
||
|
ctx := &hcl.EvalContext{
|
||
|
Variables: test.vars,
|
||
|
Functions: map[string]function.Function{
|
||
|
"can": CanFunc,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
got, err := expr.Value(ctx)
|
||
|
if err != nil {
|
||
|
t.Errorf("unexpected error\ngot: %s\nwant: <nil>", err)
|
||
|
}
|
||
|
if !test.want.RawEquals(got) {
|
||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|