hcl/hcldec/public_test.go

304 lines
4.9 KiB
Go

package hcldec
import (
"fmt"
"reflect"
"testing"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
"github.com/hashicorp/hcl2/hcl"
"github.com/zclconf/go-cty/cty"
)
func TestDecode(t *testing.T) {
tests := []struct {
config string
spec Spec
ctx *hcl.EvalContext
want cty.Value
diagCount int
}{
{
``,
&ObjectSpec{},
nil,
cty.EmptyObjectVal,
0,
},
{
"a = 1\n",
&ObjectSpec{},
nil,
cty.EmptyObjectVal,
1, // attribute named "a" is not expected here
},
{
"a = 1\n",
&ObjectSpec{
"a": &AttrSpec{
Name: "a",
Type: cty.Number,
},
},
nil,
cty.ObjectVal(map[string]cty.Value{
"a": cty.NumberIntVal(1),
}),
0,
},
{
"a = 1\n",
&AttrSpec{
Name: "a",
Type: cty.Number,
},
nil,
cty.NumberIntVal(1),
0,
},
{
"a = \"1\"\n",
&AttrSpec{
Name: "a",
Type: cty.Number,
},
nil,
cty.NumberIntVal(1),
0,
},
{
"a = true\n",
&AttrSpec{
Name: "a",
Type: cty.Number,
},
nil,
cty.UnknownVal(cty.Number),
1, // incorrect type - number required.
},
{
``,
&AttrSpec{
Name: "a",
Type: cty.Number,
Required: true,
},
nil,
cty.NullVal(cty.Number),
1, // attribute "a" is required
},
{
`
b {
}
`,
&BlockSpec{
TypeName: "b",
Nested: ObjectSpec{},
},
nil,
cty.EmptyObjectVal,
0,
},
{
``,
&BlockSpec{
TypeName: "b",
Nested: ObjectSpec{},
},
nil,
cty.NullVal(cty.DynamicPseudoType),
0,
},
{
"a {}\n",
&BlockSpec{
TypeName: "b",
Nested: ObjectSpec{},
},
nil,
cty.NullVal(cty.DynamicPseudoType),
1, // blocks of type "a" are not supported
},
{
``,
&BlockSpec{
TypeName: "b",
Nested: ObjectSpec{},
Required: true,
},
nil,
cty.NullVal(cty.DynamicPseudoType),
1, // a block of type "b" is required
},
{
`
b {}
b {}
`,
&BlockSpec{
TypeName: "b",
Nested: ObjectSpec{},
Required: true,
},
nil,
cty.EmptyObjectVal,
1, // only one "b" block is allowed
},
{
`
b {}
b {}
`,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
},
nil,
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
0,
},
{
``,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
},
nil,
cty.ListValEmpty(cty.DynamicPseudoType),
0,
},
{
`
b {}
b {}
b {}
`,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
MaxItems: 2,
},
nil,
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal, cty.EmptyObjectVal}),
1, // too many b blocks
},
{
`
b {}
b {}
`,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
MinItems: 10,
},
nil,
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
1, // insufficient b blocks
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) {
file, parseDiags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0})
body := file.Body
got, valDiags := Decode(body, test.spec, test.ctx)
var diags hcl.Diagnostics
diags = append(diags, parseDiags...)
diags = append(diags, valDiags...)
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 !got.RawEquals(test.want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
}
})
}
}
func TestSourceRange(t *testing.T) {
tests := []struct {
config string
spec Spec
want hcl.Range
}{
{
"a = 1\n",
&AttrSpec{
Name: "a",
},
hcl.Range{
Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
End: hcl.Pos{Line: 1, Column: 6, Byte: 5},
},
},
{
`
b {
a = 1
}
`,
&BlockSpec{
TypeName: "b",
Nested: &AttrSpec{
Name: "a",
},
},
hcl.Range{
Start: hcl.Pos{Line: 3, Column: 7, Byte: 11},
End: hcl.Pos{Line: 3, Column: 8, Byte: 12},
},
},
{
`
b {
c {
a = 1
}
}
`,
&BlockSpec{
TypeName: "b",
Nested: &BlockSpec{
TypeName: "c",
Nested: &AttrSpec{
Name: "a",
},
},
},
hcl.Range{
Start: hcl.Pos{Line: 4, Column: 9, Byte: 19},
End: hcl.Pos{Line: 4, Column: 10, Byte: 20},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) {
file, diags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0})
if len(diags) != 0 {
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), 0)
for _, diag := range diags {
t.Logf(" - %s", diag.Error())
}
}
body := file.Body
got := SourceRange(body, test.spec)
if !reflect.DeepEqual(got, test.want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
}
})
}
}