hcl/hclsyntax/structure_test.go
Martin Atkins 6c4344623b Unfold the "hcl" directory up into the root
The main HCL package is more visible this way, and so it's easier than
having to pick it out from dozens of other package directories.
2019-09-09 16:08:19 -07:00

534 lines
8.4 KiB
Go

package hclsyntax
import (
"fmt"
"reflect"
"testing"
"github.com/hashicorp/hcl/v2"
"github.com/kylelemons/godebug/pretty"
"github.com/zclconf/go-cty/cty"
)
func TestBodyContent(t *testing.T) {
tests := []struct {
body *Body
schema *hcl.BodySchema
partial bool
want *hcl.BodyContent
diagCount int
}{
{
&Body{},
&hcl.BodySchema{},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
0,
},
// Attributes
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
},
},
},
&hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{
"foo": &hcl.Attribute{
Name: "foo",
},
},
},
0,
},
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
},
},
},
&hcl.BodySchema{},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
1, // attribute "foo" is not expected
},
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
},
},
},
&hcl.BodySchema{},
true,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
0, // in partial mode, so extra "foo" is acceptable
},
{
&Body{
Attributes: Attributes{},
},
&hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
0, // "foo" not required, so no error
},
{
&Body{
Attributes: Attributes{},
},
&hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "foo",
Required: true,
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
1, // "foo" is required
},
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
1, // attribute "foo" not expected (it's defined as a block)
},
// Blocks
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
Blocks: hcl.Blocks{
{
Type: "foo",
Body: (*Body)(nil),
},
},
},
0,
},
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
},
&Block{
Type: "foo",
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
Blocks: hcl.Blocks{
{
Type: "foo",
Body: (*Body)(nil),
},
{
Type: "foo",
Body: (*Body)(nil),
},
},
},
0,
},
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
},
&Block{
Type: "bar",
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
Blocks: hcl.Blocks{
{
Type: "foo",
Body: (*Body)(nil),
},
},
},
1, // blocks of type "bar" not expected
},
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
},
&Block{
Type: "bar",
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
},
},
},
true,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
Blocks: hcl.Blocks{
{
Type: "foo",
Body: (*Body)(nil),
},
},
},
0, // extra "bar" allowed because we're in partial mode
},
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
Labels: []string{"bar"},
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
LabelNames: []string{"name"},
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
Blocks: hcl.Blocks{
{
Type: "foo",
Labels: []string{"bar"},
Body: (*Body)(nil),
},
},
},
0,
},
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
LabelNames: []string{"name"},
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
1, // missing label "name"
},
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
Labels: []string{"bar"},
LabelRanges: []hcl.Range{{}},
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
1, // no labels expected
},
{
&Body{
Blocks: Blocks{
&Block{
Type: "foo",
Labels: []string{"bar", "baz"},
LabelRanges: []hcl.Range{{}, {}},
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
LabelNames: []string{"name"},
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
1, // too many labels
},
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
},
},
},
&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "foo",
},
},
},
false,
&hcl.BodyContent{
Attributes: hcl.Attributes{},
},
1, // should've been a block, not an attribute
},
}
prettyConfig := &pretty.Config{
Diffable: true,
IncludeUnexported: true,
PrintStringers: true,
}
for i, test := range tests {
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
var got *hcl.BodyContent
var diags hcl.Diagnostics
if test.partial {
got, _, diags = test.body.PartialContent(test.schema)
} else {
got, diags = test.body.Content(test.schema)
}
if len(diags) != test.diagCount {
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount)
for _, diag := range diags {
t.Logf(" - %s", diag.Error())
}
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf(
"wrong result\ndiff: %s",
prettyConfig.Compare(test.want, got),
)
}
})
}
}
func TestBodyJustAttributes(t *testing.T) {
tests := []struct {
body *Body
want hcl.Attributes
diagCount int
}{
{
&Body{},
hcl.Attributes{},
0,
},
{
&Body{
Attributes: Attributes{},
},
hcl.Attributes{},
0,
},
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
Expr: &LiteralValueExpr{
Val: cty.StringVal("bar"),
},
},
},
},
hcl.Attributes{
"foo": &hcl.Attribute{
Name: "foo",
Expr: &LiteralValueExpr{
Val: cty.StringVal("bar"),
},
},
},
0,
},
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
Expr: &LiteralValueExpr{
Val: cty.StringVal("bar"),
},
},
},
Blocks: Blocks{
{
Type: "foo",
},
},
},
hcl.Attributes{
"foo": &hcl.Attribute{
Name: "foo",
Expr: &LiteralValueExpr{
Val: cty.StringVal("bar"),
},
},
},
1, // blocks are not allowed here
},
{
&Body{
Attributes: Attributes{
"foo": &Attribute{
Name: "foo",
Expr: &LiteralValueExpr{
Val: cty.StringVal("bar"),
},
},
},
hiddenAttrs: map[string]struct{}{
"foo": struct{}{},
},
},
hcl.Attributes{},
0,
},
}
prettyConfig := &pretty.Config{
Diffable: true,
IncludeUnexported: true,
PrintStringers: true,
}
for i, test := range tests {
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
got, diags := test.body.JustAttributes()
if len(diags) != test.diagCount {
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount)
for _, diag := range diags {
t.Logf(" - %s", diag.Error())
}
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf(
"wrong result\nbody: %s\ndiff: %s",
prettyConfig.Sprint(test.body),
prettyConfig.Compare(test.want, got),
)
}
})
}
}