hcl/hclsyntax/structure_test.go

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),
)
}
})
}
}