diff options
Diffstat (limited to 'ext/tryfunc/tryfunc_test.go')
| -rw-r--r-- | ext/tryfunc/tryfunc_test.go | 193 |
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) + } + }) + } +} |
