summaryrefslogtreecommitdiff
path: root/ext/tryfunc/tryfunc_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'ext/tryfunc/tryfunc_test.go')
-rw-r--r--ext/tryfunc/tryfunc_test.go193
1 files changed, 193 insertions, 0 deletions
diff --git a/ext/tryfunc/tryfunc_test.go b/ext/tryfunc/tryfunc_test.go
new file mode 100644
index 0000000..063adab
--- /dev/null
+++ b/ext/tryfunc/tryfunc_test.go
@@ -0,0 +1,193 @@
+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)
+ }
+ })
+ }
+}