diff --git a/.changelog/158.txt b/.changelog/158.txt new file mode 100644 index 00000000..73da40bb --- /dev/null +++ b/.changelog/158.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +tfprotov5: Added `ValueType()` methods to `Schema`, `SchemaAttribute`, `SchemaBlock`, and `SchemaNestedBlock` types. +``` + +```release-note:enhancement +tfprotov6: Added `ValueType()` methods to `Schema`, `SchemaAttribute`, `SchemaBlock`, `SchemaNestedBlock`, and `SchemaObject` types. +``` diff --git a/tfprotov5/schema.go b/tfprotov5/schema.go index 5f1acc34..b0b6dff2 100644 --- a/tfprotov5/schema.go +++ b/tfprotov5/schema.go @@ -68,6 +68,19 @@ type Schema struct { Block *SchemaBlock } +// ValueType returns the tftypes.Type for a Schema. +// +// If Schema is missing, an empty Object is returned. +func (s *Schema) ValueType() tftypes.Type { + if s == nil { + return tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + } + } + + return s.Block.ValueType() +} + // SchemaBlock represents a block in a schema. Blocks are how Terraform creates // groupings of attributes. In configurations, they don't use the equals sign // and use dynamic instead of list comprehensions. @@ -105,6 +118,51 @@ type SchemaBlock struct { Deprecated bool } +// ValueType returns the tftypes.Type for a SchemaBlock. +// +// If SchemaBlock is missing, an empty Object is returned. +func (s *SchemaBlock) ValueType() tftypes.Type { + if s == nil { + return tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + } + } + + attributeTypes := map[string]tftypes.Type{} + + for _, attribute := range s.Attributes { + if attribute == nil { + continue + } + + attributeType := attribute.ValueType() + + if attributeType == nil { + continue + } + + attributeTypes[attribute.Name] = attributeType + } + + for _, block := range s.BlockTypes { + if block == nil { + continue + } + + blockType := block.ValueType() + + if blockType == nil { + continue + } + + attributeTypes[block.TypeName] = blockType + } + + return tftypes.Object{ + AttributeTypes: attributeTypes, + } +} + // SchemaAttribute represents a single attribute within a schema block. // Attributes are the fields users can set in configuration using the equals // sign, can assign to variables, can interpolate, and can use list @@ -162,6 +220,17 @@ type SchemaAttribute struct { Deprecated bool } +// ValueType returns the tftypes.Type for a SchemaAttribute. +// +// If SchemaAttribute is missing, nil is returned. +func (s *SchemaAttribute) ValueType() tftypes.Type { + if s == nil { + return nil + } + + return s.Type +} + // SchemaNestedBlock is a nested block within another block. See SchemaBlock // for more information on blocks. type SchemaNestedBlock struct { @@ -198,6 +267,39 @@ type SchemaNestedBlock struct { MaxItems int64 } +// ValueType returns the tftypes.Type for a SchemaNestedBlock. +// +// If SchemaNestedBlock is missing or the Nesting mode is invalid, nil is +// returned. +func (s *SchemaNestedBlock) ValueType() tftypes.Type { + if s == nil { + return nil + } + + blockType := s.Block.ValueType() + + switch s.Nesting { + case SchemaNestedBlockNestingModeGroup: + return blockType + case SchemaNestedBlockNestingModeList: + return tftypes.List{ + ElementType: blockType, + } + case SchemaNestedBlockNestingModeMap: + return tftypes.Map{ + ElementType: blockType, + } + case SchemaNestedBlockNestingModeSet: + return tftypes.Set{ + ElementType: blockType, + } + case SchemaNestedBlockNestingModeSingle: + return blockType + default: + return nil + } +} + // SchemaNestedBlockNestingMode indicates the nesting mode for // SchemaNestedBlocks. The nesting mode determines the number of instances of // the block allowed, how many labels the block expects, and the data structure diff --git a/tfprotov5/schema_test.go b/tfprotov5/schema_test.go new file mode 100644 index 00000000..40568283 --- /dev/null +++ b/tfprotov5/schema_test.go @@ -0,0 +1,610 @@ +package tfprotov5_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestSchemaAttributeValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schemaAttribute *tfprotov5.SchemaAttribute + expected tftypes.Type + }{ + "nil": { + schemaAttribute: nil, + expected: nil, + }, + "Bool": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.Bool, + }, + expected: tftypes.Bool, + }, + "DynamicPseudoType": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.DynamicPseudoType, + }, + expected: tftypes.DynamicPseudoType, + }, + "List-String": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.List{ + ElementType: tftypes.String, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.String, + }, + }, + "Map-String": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.Map{ + ElementType: tftypes.String, + }, + }, + expected: tftypes.Map{ + ElementType: tftypes.String, + }, + }, + "Number": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.Number, + }, + expected: tftypes.Number, + }, + "Object-String": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + "Set-String": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.Set{ + ElementType: tftypes.String, + }, + }, + expected: tftypes.Set{ + ElementType: tftypes.String, + }, + }, + "String": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.String, + }, + expected: tftypes.String, + }, + "Tuple-String": { + schemaAttribute: &tfprotov5.SchemaAttribute{ + Type: tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + expected: tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schemaAttribute.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} + +func TestSchemaBlockValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schemaBlock *tfprotov5.SchemaBlock + expected tftypes.Type + }{ + "nil": { + schemaBlock: nil, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + "Attribute-String": { + schemaBlock: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "Block-NestingList-String": { + schemaBlock: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schemaBlock.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} + +func TestSchemaNestedBlockValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schemaNestedBlock *tfprotov5.SchemaNestedBlock + expected tftypes.Type + }{ + "nil": { + schemaNestedBlock: nil, + expected: nil, + }, + "NestingGroup-Attribute-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeGroup, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "NestingGroup-Block-NestingList-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeGroup, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + "NestingInvalid": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Nesting: tfprotov5.SchemaNestedBlockNestingModeInvalid, + }, + expected: nil, + }, + "NestingList-missing-Block": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + }, + "NestingList-Attribute-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + "NestingList-Block-NestingList-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "NestingMap-Attribute-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeMap, + }, + expected: tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + "NestingMap-Block-NestingList-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeMap, + }, + expected: tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "NestingSet-Attribute-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeSet, + }, + expected: tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + "NestingSet-Block-NestingList-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeSet, + }, + expected: tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "NestingSingle-Attribute-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeSingle, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "NestingSingle-Block-NestingList-String": { + schemaNestedBlock: &tfprotov5.SchemaNestedBlock{ + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeSingle, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schemaNestedBlock.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} + +func TestSchemaValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema *tfprotov5.Schema + expected tftypes.Type + }{ + "nil": { + schema: nil, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + "missing-Block": { + schema: &tfprotov5.Schema{}, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + "Attribute-String": { + schema: &tfprotov5.Schema{ + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "Block-NestingList-String": { + schema: &tfprotov5.Schema{ + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} diff --git a/tfprotov6/schema.go b/tfprotov6/schema.go index 131d64c7..c17e2cce 100644 --- a/tfprotov6/schema.go +++ b/tfprotov6/schema.go @@ -96,6 +96,19 @@ type Schema struct { Block *SchemaBlock } +// ValueType returns the tftypes.Type for a Schema. +// +// If Schema is missing, an empty Object is returned. +func (s *Schema) ValueType() tftypes.Type { + if s == nil { + return tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + } + } + + return s.Block.ValueType() +} + // SchemaBlock represents a block in a schema. Blocks are how Terraform creates // groupings of attributes. In configurations, they don't use the equals sign // and use dynamic instead of list comprehensions. @@ -133,6 +146,51 @@ type SchemaBlock struct { Deprecated bool } +// ValueType returns the tftypes.Type for a SchemaBlock. +// +// If SchemaBlock is missing, an empty Object is returned. +func (s *SchemaBlock) ValueType() tftypes.Type { + if s == nil { + return tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + } + } + + attributeTypes := map[string]tftypes.Type{} + + for _, attribute := range s.Attributes { + if attribute == nil { + continue + } + + attributeType := attribute.ValueType() + + if attributeType == nil { + continue + } + + attributeTypes[attribute.Name] = attributeType + } + + for _, block := range s.BlockTypes { + if block == nil { + continue + } + + blockType := block.ValueType() + + if blockType == nil { + continue + } + + attributeTypes[block.TypeName] = blockType + } + + return tftypes.Object{ + AttributeTypes: attributeTypes, + } +} + // SchemaAttribute represents a single attribute within a schema block. // Attributes are the fields users can set in configuration using the equals // sign, can assign to variables, can interpolate, and can use list @@ -194,6 +252,22 @@ type SchemaAttribute struct { Deprecated bool } +// ValueType returns the tftypes.Type for a SchemaAttribute. +// +// If SchemaAttribute is missing, nil is returned. +func (s *SchemaAttribute) ValueType() tftypes.Type { + if s == nil { + return nil + } + + // It is not valid to set both NestedType and Type. + if s.NestedType != nil { + return s.NestedType.ValueType() + } + + return s.Type +} + // SchemaNestedBlock is a nested block within another block. See SchemaBlock // for more information on blocks. type SchemaNestedBlock struct { @@ -230,6 +304,39 @@ type SchemaNestedBlock struct { MaxItems int64 } +// ValueType returns the tftypes.Type for a SchemaNestedBlock. +// +// If SchemaNestedBlock is missing or the Nesting mode is invalid, nil is +// returned. +func (s *SchemaNestedBlock) ValueType() tftypes.Type { + if s == nil { + return nil + } + + blockType := s.Block.ValueType() + + switch s.Nesting { + case SchemaNestedBlockNestingModeGroup: + return blockType + case SchemaNestedBlockNestingModeList: + return tftypes.List{ + ElementType: blockType, + } + case SchemaNestedBlockNestingModeMap: + return tftypes.Map{ + ElementType: blockType, + } + case SchemaNestedBlockNestingModeSet: + return tftypes.Set{ + ElementType: blockType, + } + case SchemaNestedBlockNestingModeSingle: + return blockType + default: + return nil + } +} + // SchemaNestedBlockNestingMode indicates the nesting mode for // SchemaNestedBlocks. The nesting mode determines the number of instances of // the block allowed, how many labels the block expects, and the data structure @@ -262,6 +369,54 @@ type SchemaObject struct { Nesting SchemaObjectNestingMode } +// ValueType returns the tftypes.Type for a SchemaObject. +// +// If SchemaObject is missing or the Nesting mode is invalid, nil is returned. +func (s *SchemaObject) ValueType() tftypes.Type { + if s == nil { + return nil + } + + attributeTypes := map[string]tftypes.Type{} + + for _, attribute := range s.Attributes { + if attribute == nil { + continue + } + + attributeType := attribute.ValueType() + + if attributeType == nil { + continue + } + + attributeTypes[attribute.Name] = attributeType + } + + objectType := tftypes.Object{ + AttributeTypes: attributeTypes, + } + + switch s.Nesting { + case SchemaObjectNestingModeList: + return tftypes.List{ + ElementType: objectType, + } + case SchemaObjectNestingModeMap: + return tftypes.Map{ + ElementType: objectType, + } + case SchemaObjectNestingModeSet: + return tftypes.Set{ + ElementType: objectType, + } + case SchemaObjectNestingModeSingle: + return objectType + default: + return nil + } +} + // SchemaObjectNestingMode indicates the nesting mode for // SchemaNestedBlocks. The nesting mode determines the number of instances of // the nested type allowed and the data structure used for the block in config diff --git a/tfprotov6/schema_test.go b/tfprotov6/schema_test.go new file mode 100644 index 00000000..5d22bda1 --- /dev/null +++ b/tfprotov6/schema_test.go @@ -0,0 +1,1146 @@ +package tfprotov6_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestSchemaAttributeValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schemaAttribute *tfprotov6.SchemaAttribute + expected tftypes.Type + }{ + "nil": { + schemaAttribute: nil, + expected: nil, + }, + "Bool": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.Bool, + }, + expected: tftypes.Bool, + }, + "DynamicPseudoType": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.DynamicPseudoType, + }, + expected: tftypes.DynamicPseudoType, + }, + "List-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.List{ + ElementType: tftypes.String, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.String, + }, + }, + "Map-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.Map{ + ElementType: tftypes.String, + }, + }, + expected: tftypes.Map{ + ElementType: tftypes.String, + }, + }, + "NestedList-Bool": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "bool": tftypes.Bool, + }, + }, + }, + }, + "NestedList-DynamicPseudoType": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "dynamic", + Type: tftypes.DynamicPseudoType, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "dynamic": tftypes.DynamicPseudoType, + }, + }, + }, + }, + "NestedList-List-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "list", + Type: tftypes.List{ + ElementType: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "list": tftypes.List{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + "NestedList-Map-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "map", + Type: tftypes.Map{ + ElementType: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "map": tftypes.Map{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + "NestedList-Number": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "number", + Type: tftypes.Number, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "number": tftypes.Number, + }, + }, + }, + }, + "NestedList-Object-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "object", + Type: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "object": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + }, + }, + "NestedList-Set-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "set", + Type: tftypes.Set{ + ElementType: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "set": tftypes.Set{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + "NestedList-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + "NestedList-Tuple-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "tuple", + Type: tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "tuple": tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + }, + }, + }, + "NestedMap-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeMap, + }, + }, + expected: tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + "NestedSet-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeSet, + }, + }, + expected: tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + "NestedSingle-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + NestedType: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeSingle, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + "Number": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.Number, + }, + expected: tftypes.Number, + }, + "Object-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + "Set-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.Set{ + ElementType: tftypes.String, + }, + }, + expected: tftypes.Set{ + ElementType: tftypes.String, + }, + }, + "String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.String, + }, + expected: tftypes.String, + }, + "Tuple-String": { + schemaAttribute: &tfprotov6.SchemaAttribute{ + Type: tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + expected: tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schemaAttribute.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} + +func TestSchemaBlockValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schemaBlock *tfprotov6.SchemaBlock + expected tftypes.Type + }{ + "nil": { + schemaBlock: nil, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + "Attribute-String": { + schemaBlock: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "Block-NestingList-String": { + schemaBlock: &tfprotov6.SchemaBlock{ + BlockTypes: []*tfprotov6.SchemaNestedBlock{ + { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schemaBlock.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} + +func TestSchemaNestedBlockValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schemaNestedBlock *tfprotov6.SchemaNestedBlock + expected tftypes.Type + }{ + "nil": { + schemaNestedBlock: nil, + expected: nil, + }, + "NestingGroup-Attribute-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeGroup, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "NestingGroup-Block-NestingList-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + BlockTypes: []*tfprotov6.SchemaNestedBlock{ + { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeGroup, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + "NestingInvalid": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Nesting: tfprotov6.SchemaNestedBlockNestingModeInvalid, + }, + expected: nil, + }, + "NestingList-missing-Block": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + }, + "NestingList-Attribute-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + "NestingList-Block-NestingList-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + BlockTypes: []*tfprotov6.SchemaNestedBlock{ + { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "NestingMap-Attribute-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeMap, + }, + expected: tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + "NestingMap-Block-NestingList-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + BlockTypes: []*tfprotov6.SchemaNestedBlock{ + { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeMap, + }, + expected: tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "NestingSet-Attribute-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeSet, + }, + expected: tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + "NestingSet-Block-NestingList-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + BlockTypes: []*tfprotov6.SchemaNestedBlock{ + { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeSet, + }, + expected: tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "NestingSingle-Attribute-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeSingle, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "NestingSingle-Block-NestingList-String": { + schemaNestedBlock: &tfprotov6.SchemaNestedBlock{ + Block: &tfprotov6.SchemaBlock{ + BlockTypes: []*tfprotov6.SchemaNestedBlock{ + { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeSingle, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schemaNestedBlock.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} + +func TestSchemaObjectValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schemaObject *tfprotov6.SchemaObject + expected tftypes.Type + }{ + "nil": { + schemaObject: nil, + expected: nil, + }, + "NestedList-Bool": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "bool": tftypes.Bool, + }, + }, + }, + }, + "NestedList-DynamicPseudoType": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "dynamic", + Type: tftypes.DynamicPseudoType, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "dynamic": tftypes.DynamicPseudoType, + }, + }, + }, + }, + "NestedList-List-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "list", + Type: tftypes.List{ + ElementType: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "list": tftypes.List{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + "NestedList-Map-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "map", + Type: tftypes.Map{ + ElementType: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "map": tftypes.Map{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + "NestedList-Number": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "number", + Type: tftypes.Number, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "number": tftypes.Number, + }, + }, + }, + }, + "NestedList-Object-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "object", + Type: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "object": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + }, + }, + "NestedList-Set-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "set", + Type: tftypes.Set{ + ElementType: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "set": tftypes.Set{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + "NestedList-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + "NestedList-Tuple-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "tuple", + Type: tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeList, + }, + expected: tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "tuple": tftypes.Tuple{ + ElementTypes: []tftypes.Type{tftypes.String}, + }, + }, + }, + }, + }, + "NestedMap-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeMap, + }, + expected: tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + "NestedSet-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeSet, + }, + expected: tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + }, + "NestedSingle-String": { + schemaObject: &tfprotov6.SchemaObject{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "string", + Type: tftypes.String, + }, + }, + Nesting: tfprotov6.SchemaObjectNestingModeSingle, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schemaObject.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +} + +func TestSchemaValueType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema *tfprotov6.Schema + expected tftypes.Type + }{ + "nil": { + schema: nil, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + "missing-Block": { + schema: &tfprotov6.Schema{}, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{}, + }, + }, + "Attribute-String": { + schema: &tfprotov6.Schema{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + "Block-NestingList-String": { + schema: &tfprotov6.Schema{ + Block: &tfprotov6.SchemaBlock{ + BlockTypes: []*tfprotov6.SchemaNestedBlock{ + { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_string_attribute", + Type: tftypes.String, + }, + }, + }, + Nesting: tfprotov6.SchemaNestedBlockNestingModeList, + TypeName: "test_list_block", + }, + }, + }, + }, + expected: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_list_block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_string_attribute": tftypes.String, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.ValueType() + + if testCase.expected == nil { + if got == nil { + return + } + + t.Fatalf("expected nil, got: %s", got) + } + + if !testCase.expected.Equal(got) { + t.Errorf("expected %s, got: %s", testCase.expected, got) + } + }) + } +}