hcl/hcldec/spec_test.go
Martin Atkins bb724af7fd hcldec: BlockAttrsSpec spec type
This is the hcldec interface to Body.JustAttributes, producing a map whose
keys are the child attribute names and whose values are the results of
evaluating those expressions.

We can't just expose a JustAttributes-style spec directly here because
it's not really compatible with how hcldec thinks about things, but we
can expose a spec that decodes a specific child block because that can
then compose properly with other specs at the same level without
interfering with their operation.

The primary use for this is to allow the use of the block syntax to define
a map:

    dynamic_stuff {
      foo = "bar"
    }

JustAttributes is normally used in static analysis situations such as
enumerating the contents of a block to decide what to include in the
final EvalContext. That's not really possible with the hcldec model
because both structural decoding and expression evaluation happen
together. Therefore the use of this is pretty limited: it's useful if you
want to be compatible with an existing format based on legacy HCL where a
map was conventionally defined using block syntax, relying on the fact
that HCL did not make a strong distinction between attribute and block
syntax.
2018-08-09 16:53:16 -07:00

142 lines
3.3 KiB
Go

package hcldec
import (
"reflect"
"testing"
"github.com/apparentlymart/go-dump/dump"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/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 _ 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)
}
})
}