634 lines
12 KiB
Go
634 lines
12 KiB
Go
package hclspecutils
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hashicorp/hcl/v2/hcldec"
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/hashicorp/nomad/plugins/shared/hclspec"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
type testConversions struct {
|
|
Name string
|
|
Input *hclspec.Spec
|
|
Expected hcldec.Spec
|
|
ExpectedError string
|
|
}
|
|
|
|
func testSpecConversions(t *testing.T, cases []testConversions) {
|
|
t.Helper()
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.Name, func(t *testing.T) {
|
|
act, diag := Convert(c.Input)
|
|
if diag.HasErrors() {
|
|
if c.ExpectedError == "" {
|
|
t.Fatalf("Convert %q failed: %v", c.Name, diag.Error())
|
|
}
|
|
|
|
require.Contains(t, diag.Error(), c.ExpectedError)
|
|
} else if c.ExpectedError != "" {
|
|
t.Fatalf("Expected error %q", c.ExpectedError)
|
|
}
|
|
|
|
require.EqualValues(t, c.Expected, act)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDec_Convert_Object(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "Object w/ only attributes",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Object{
|
|
Object: &hclspec.Object{
|
|
Attributes: map[string]*hclspec.Spec{
|
|
"foo": {
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Type: "string",
|
|
Required: false,
|
|
},
|
|
},
|
|
},
|
|
"bar": {
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Type: "number",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
"baz": {
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Type: "bool",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: hcldec.ObjectSpec(map[string]hcldec.Spec{
|
|
"foo": &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: false,
|
|
},
|
|
"bar": &hcldec.AttrSpec{
|
|
Name: "bar",
|
|
Type: cty.Number,
|
|
Required: true,
|
|
},
|
|
"baz": &hcldec.AttrSpec{
|
|
Name: "baz",
|
|
Type: cty.Bool,
|
|
Required: false,
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_Array(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "array basic",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Array{
|
|
Array: &hclspec.Array{
|
|
Values: []*hclspec.Spec{
|
|
{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Required: true,
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "bar",
|
|
Required: true,
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: hcldec.TupleSpec{
|
|
&hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
&hcldec.AttrSpec{
|
|
Name: "bar",
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_Attr(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "attr basic type",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Required: true,
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
},
|
|
{
|
|
Name: "attr object type",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Required: true,
|
|
Type: "object({name1 = string, name2 = bool})",
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"name1": cty.String,
|
|
"name2": cty.Bool,
|
|
}),
|
|
Required: true,
|
|
},
|
|
},
|
|
{
|
|
Name: "attr no name",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Required: true,
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
ExpectedError: "Missing name in attribute spec",
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_Block(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "block with attr",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockValue{
|
|
BlockValue: &hclspec.Block{
|
|
Name: "test",
|
|
Required: true,
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.BlockSpec{
|
|
TypeName: "test",
|
|
Required: true,
|
|
Nested: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "block with nested block",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockValue{
|
|
BlockValue: &hclspec.Block{
|
|
Name: "test",
|
|
Required: true,
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockValue{
|
|
BlockValue: &hclspec.Block{
|
|
Name: "test",
|
|
Required: true,
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.BlockSpec{
|
|
TypeName: "test",
|
|
Required: true,
|
|
Nested: &hcldec.BlockSpec{
|
|
TypeName: "test",
|
|
Required: true,
|
|
Nested: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_BlockAttrs(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "block attr",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockAttrs{
|
|
BlockAttrs: &hclspec.BlockAttrs{
|
|
Name: "test",
|
|
Type: "string",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.BlockAttrsSpec{
|
|
TypeName: "test",
|
|
ElementType: cty.String,
|
|
Required: true,
|
|
},
|
|
},
|
|
{
|
|
Name: "block list no name",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockAttrs{
|
|
BlockAttrs: &hclspec.BlockAttrs{
|
|
Type: "string",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
ExpectedError: "Missing name in block_attrs spec",
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_BlockList(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "block list with attr",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockList{
|
|
BlockList: &hclspec.BlockList{
|
|
Name: "test",
|
|
MinItems: 1,
|
|
MaxItems: 3,
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.BlockListSpec{
|
|
TypeName: "test",
|
|
MinItems: 1,
|
|
MaxItems: 3,
|
|
Nested: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "block list no name",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockList{
|
|
BlockList: &hclspec.BlockList{
|
|
MinItems: 1,
|
|
MaxItems: 3,
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ExpectedError: "Missing name in block_list spec",
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_BlockSet(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "block set with attr",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockSet{
|
|
BlockSet: &hclspec.BlockSet{
|
|
Name: "test",
|
|
MinItems: 1,
|
|
MaxItems: 3,
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.BlockSetSpec{
|
|
TypeName: "test",
|
|
MinItems: 1,
|
|
MaxItems: 3,
|
|
Nested: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "block set missing name",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockSet{
|
|
BlockSet: &hclspec.BlockSet{
|
|
MinItems: 1,
|
|
MaxItems: 3,
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ExpectedError: "Missing name in block_set spec",
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_BlockMap(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "block map with attr",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockMap{
|
|
BlockMap: &hclspec.BlockMap{
|
|
Name: "test",
|
|
Labels: []string{"key1", "key2"},
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.BlockMapSpec{
|
|
TypeName: "test",
|
|
LabelNames: []string{"key1", "key2"},
|
|
Nested: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: false,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "block map missing name",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockMap{
|
|
BlockMap: &hclspec.BlockMap{
|
|
Labels: []string{"key1", "key2"},
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ExpectedError: "Missing name in block_map spec",
|
|
},
|
|
{
|
|
Name: "block map missing labels",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_BlockMap{
|
|
BlockMap: &hclspec.BlockMap{
|
|
Name: "foo",
|
|
Nested: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ExpectedError: "Invalid block label name list",
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_Default(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "default attr",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Default{
|
|
Default: &hclspec.Default{
|
|
Primary: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Attr{
|
|
Attr: &hclspec.Attr{
|
|
Name: "foo",
|
|
Type: "string",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
Default: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Literal{
|
|
Literal: &hclspec.Literal{
|
|
Value: "\"hi\"",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.DefaultSpec{
|
|
Primary: &hcldec.AttrSpec{
|
|
Name: "foo",
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
Default: &hcldec.LiteralSpec{
|
|
Value: cty.StringVal("hi"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|
|
|
|
func TestDec_Convert_Literal(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tests := []testConversions{
|
|
{
|
|
Name: "bool: true",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Literal{
|
|
Literal: &hclspec.Literal{
|
|
Value: "true",
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.LiteralSpec{
|
|
Value: cty.BoolVal(true),
|
|
},
|
|
},
|
|
{
|
|
Name: "bool: false",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Literal{
|
|
Literal: &hclspec.Literal{
|
|
Value: "false",
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.LiteralSpec{
|
|
Value: cty.BoolVal(false),
|
|
},
|
|
},
|
|
{
|
|
Name: "string",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Literal{
|
|
Literal: &hclspec.Literal{
|
|
Value: "\"hi\"",
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.LiteralSpec{
|
|
Value: cty.StringVal("hi"),
|
|
},
|
|
},
|
|
{
|
|
Name: "string w/ func",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Literal{
|
|
Literal: &hclspec.Literal{
|
|
Value: "reverse(\"hi\")",
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.LiteralSpec{
|
|
Value: cty.StringVal("ih"),
|
|
},
|
|
},
|
|
{
|
|
Name: "list string",
|
|
Input: &hclspec.Spec{
|
|
Block: &hclspec.Spec_Literal{
|
|
Literal: &hclspec.Literal{
|
|
Value: "[\"hi\", \"bye\"]",
|
|
},
|
|
},
|
|
},
|
|
Expected: &hcldec.LiteralSpec{
|
|
Value: cty.TupleVal([]cty.Value{cty.StringVal("hi"), cty.StringVal("bye")}),
|
|
},
|
|
},
|
|
}
|
|
|
|
testSpecConversions(t, tests)
|
|
}
|