| package hcldec |
| |
| import ( |
| "fmt" |
| "reflect" |
| "testing" |
| |
| "github.com/apparentlymart/go-dump/dump" |
| "github.com/zclconf/go-cty/cty" |
| |
| "github.com/hashicorp/hcl/v2" |
| "github.com/hashicorp/hcl/v2/hclsyntax" |
| ) |
| |
| // Verify that all of our spec types implement the necessary interfaces |
| var _ Spec = ObjectSpec(nil) |
| var _ Spec = TupleSpec(nil) |
| var _ Spec = (*AttrSpec)(nil) |
| var _ Spec = (*LiteralSpec)(nil) |
| var _ Spec = (*ExprSpec)(nil) |
| var _ Spec = (*BlockSpec)(nil) |
| var _ Spec = (*BlockListSpec)(nil) |
| var _ Spec = (*BlockSetSpec)(nil) |
| var _ Spec = (*BlockMapSpec)(nil) |
| var _ Spec = (*BlockAttrsSpec)(nil) |
| var _ Spec = (*BlockLabelSpec)(nil) |
| var _ Spec = (*DefaultSpec)(nil) |
| var _ Spec = (*TransformExprSpec)(nil) |
| var _ Spec = (*TransformFuncSpec)(nil) |
| var _ Spec = (*ValidateSpec)(nil) |
| |
| var _ attrSpec = (*AttrSpec)(nil) |
| var _ attrSpec = (*DefaultSpec)(nil) |
| |
| var _ blockSpec = (*BlockSpec)(nil) |
| var _ blockSpec = (*BlockListSpec)(nil) |
| var _ blockSpec = (*BlockSetSpec)(nil) |
| var _ blockSpec = (*BlockMapSpec)(nil) |
| var _ blockSpec = (*BlockAttrsSpec)(nil) |
| var _ blockSpec = (*DefaultSpec)(nil) |
| |
| var _ specNeedingVariables = (*AttrSpec)(nil) |
| var _ specNeedingVariables = (*BlockSpec)(nil) |
| var _ specNeedingVariables = (*BlockListSpec)(nil) |
| var _ specNeedingVariables = (*BlockSetSpec)(nil) |
| var _ specNeedingVariables = (*BlockMapSpec)(nil) |
| var _ specNeedingVariables = (*BlockAttrsSpec)(nil) |
| |
| func TestDefaultSpec(t *testing.T) { |
| config := ` |
| foo = fooval |
| bar = barval |
| ` |
| f, diags := hclsyntax.ParseConfig([]byte(config), "", hcl.Pos{Line: 1, Column: 1}) |
| if diags.HasErrors() { |
| t.Fatal(diags.Error()) |
| } |
| |
| t.Run("primary set", func(t *testing.T) { |
| spec := &DefaultSpec{ |
| Primary: &AttrSpec{ |
| Name: "foo", |
| Type: cty.String, |
| }, |
| Default: &AttrSpec{ |
| Name: "bar", |
| Type: cty.String, |
| }, |
| } |
| |
| gotVars := Variables(f.Body, spec) |
| wantVars := []hcl.Traversal{ |
| { |
| hcl.TraverseRoot{ |
| Name: "fooval", |
| SrcRange: hcl.Range{ |
| Filename: "", |
| Start: hcl.Pos{Line: 2, Column: 7, Byte: 7}, |
| End: hcl.Pos{Line: 2, Column: 13, Byte: 13}, |
| }, |
| }, |
| }, |
| { |
| hcl.TraverseRoot{ |
| Name: "barval", |
| SrcRange: hcl.Range{ |
| Filename: "", |
| Start: hcl.Pos{Line: 3, Column: 7, Byte: 20}, |
| End: hcl.Pos{Line: 3, Column: 13, Byte: 26}, |
| }, |
| }, |
| }, |
| } |
| if !reflect.DeepEqual(gotVars, wantVars) { |
| t.Errorf("wrong Variables result\ngot: %s\nwant: %s", dump.Value(gotVars), dump.Value(wantVars)) |
| } |
| |
| ctx := &hcl.EvalContext{ |
| Variables: map[string]cty.Value{ |
| "fooval": cty.StringVal("foo value"), |
| "barval": cty.StringVal("bar value"), |
| }, |
| } |
| |
| got, err := Decode(f.Body, spec, ctx) |
| if err != nil { |
| t.Fatal(err) |
| } |
| want := cty.StringVal("foo value") |
| if !got.RawEquals(want) { |
| t.Errorf("wrong Decode result\ngot: %#v\nwant: %#v", got, want) |
| } |
| }) |
| |
| t.Run("primary not set", func(t *testing.T) { |
| spec := &DefaultSpec{ |
| Primary: &AttrSpec{ |
| Name: "foo", |
| Type: cty.String, |
| }, |
| Default: &AttrSpec{ |
| Name: "bar", |
| Type: cty.String, |
| }, |
| } |
| |
| ctx := &hcl.EvalContext{ |
| Variables: map[string]cty.Value{ |
| "fooval": cty.NullVal(cty.String), |
| "barval": cty.StringVal("bar value"), |
| }, |
| } |
| |
| got, err := Decode(f.Body, spec, ctx) |
| if err != nil { |
| t.Fatal(err) |
| } |
| want := cty.StringVal("bar value") |
| if !got.RawEquals(want) { |
| t.Errorf("wrong Decode result\ngot: %#v\nwant: %#v", got, want) |
| } |
| }) |
| } |
| |
| func TestValidateFuncSpec(t *testing.T) { |
| config := ` |
| foo = "invalid" |
| ` |
| f, diags := hclsyntax.ParseConfig([]byte(config), "", hcl.Pos{Line: 1, Column: 1}) |
| if diags.HasErrors() { |
| t.Fatal(diags.Error()) |
| } |
| |
| expectRange := map[string]*hcl.Range{ |
| "without_range": nil, |
| "with_range": &hcl.Range{ |
| Filename: "foobar", |
| Start: hcl.Pos{Line: 99, Column: 99}, |
| End: hcl.Pos{Line: 999, Column: 999}, |
| }, |
| } |
| |
| for name := range expectRange { |
| t.Run(name, func(t *testing.T) { |
| spec := &ValidateSpec{ |
| Wrapped: &AttrSpec{ |
| Name: "foo", |
| Type: cty.String, |
| }, |
| Func: func(value cty.Value) hcl.Diagnostics { |
| if value.AsString() != "invalid" { |
| return hcl.Diagnostics{ |
| &hcl.Diagnostic{ |
| Severity: hcl.DiagError, |
| Summary: "incorrect value", |
| Detail: fmt.Sprintf("invalid value passed in: %s", value.GoString()), |
| }, |
| } |
| } |
| |
| return hcl.Diagnostics{ |
| &hcl.Diagnostic{ |
| Severity: hcl.DiagWarning, |
| Summary: "OK", |
| Detail: "validation called correctly", |
| Subject: expectRange[name], |
| }, |
| } |
| }, |
| } |
| |
| _, diags = Decode(f.Body, spec, nil) |
| if len(diags) != 1 || |
| diags[0].Severity != hcl.DiagWarning || |
| diags[0].Summary != "OK" || |
| diags[0].Detail != "validation called correctly" { |
| t.Fatalf("unexpected diagnostics: %s", diags.Error()) |
| } |
| |
| if expectRange[name] == nil && diags[0].Subject == nil { |
| t.Fatal("returned diagnostic subject missing") |
| } |
| |
| if expectRange[name] != nil && !reflect.DeepEqual(expectRange[name], diags[0].Subject) { |
| t.Fatalf("expected range %s, got range %s", expectRange[name], diags[0].Subject) |
| } |
| }) |
| } |
| } |