github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plugin/convert/schema_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package convert
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/google/go-cmp/cmp/cmpopts"
    11  	"github.com/terramate-io/tf/configs/configschema"
    12  	proto "github.com/terramate-io/tf/tfplugin5"
    13  	"github.com/zclconf/go-cty/cty"
    14  )
    15  
    16  var (
    17  	equateEmpty   = cmpopts.EquateEmpty()
    18  	typeComparer  = cmp.Comparer(cty.Type.Equals)
    19  	valueComparer = cmp.Comparer(cty.Value.RawEquals)
    20  )
    21  
    22  // Test that we can convert configschema to protobuf types and back again.
    23  func TestConvertSchemaBlocks(t *testing.T) {
    24  	tests := map[string]struct {
    25  		Block *proto.Schema_Block
    26  		Want  *configschema.Block
    27  	}{
    28  		"attributes": {
    29  			&proto.Schema_Block{
    30  				Attributes: []*proto.Schema_Attribute{
    31  					{
    32  						Name:     "computed",
    33  						Type:     []byte(`["list","bool"]`),
    34  						Computed: true,
    35  					},
    36  					{
    37  						Name:     "optional",
    38  						Type:     []byte(`"string"`),
    39  						Optional: true,
    40  					},
    41  					{
    42  						Name:     "optional_computed",
    43  						Type:     []byte(`["map","bool"]`),
    44  						Optional: true,
    45  						Computed: true,
    46  					},
    47  					{
    48  						Name:     "required",
    49  						Type:     []byte(`"number"`),
    50  						Required: true,
    51  					},
    52  				},
    53  			},
    54  			&configschema.Block{
    55  				Attributes: map[string]*configschema.Attribute{
    56  					"computed": {
    57  						Type:     cty.List(cty.Bool),
    58  						Computed: true,
    59  					},
    60  					"optional": {
    61  						Type:     cty.String,
    62  						Optional: true,
    63  					},
    64  					"optional_computed": {
    65  						Type:     cty.Map(cty.Bool),
    66  						Optional: true,
    67  						Computed: true,
    68  					},
    69  					"required": {
    70  						Type:     cty.Number,
    71  						Required: true,
    72  					},
    73  				},
    74  			},
    75  		},
    76  		"blocks": {
    77  			&proto.Schema_Block{
    78  				BlockTypes: []*proto.Schema_NestedBlock{
    79  					{
    80  						TypeName: "list",
    81  						Nesting:  proto.Schema_NestedBlock_LIST,
    82  						Block:    &proto.Schema_Block{},
    83  					},
    84  					{
    85  						TypeName: "map",
    86  						Nesting:  proto.Schema_NestedBlock_MAP,
    87  						Block:    &proto.Schema_Block{},
    88  					},
    89  					{
    90  						TypeName: "set",
    91  						Nesting:  proto.Schema_NestedBlock_SET,
    92  						Block:    &proto.Schema_Block{},
    93  					},
    94  					{
    95  						TypeName: "single",
    96  						Nesting:  proto.Schema_NestedBlock_SINGLE,
    97  						Block: &proto.Schema_Block{
    98  							Attributes: []*proto.Schema_Attribute{
    99  								{
   100  									Name:     "foo",
   101  									Type:     []byte(`"dynamic"`),
   102  									Required: true,
   103  								},
   104  							},
   105  						},
   106  					},
   107  				},
   108  			},
   109  			&configschema.Block{
   110  				BlockTypes: map[string]*configschema.NestedBlock{
   111  					"list": &configschema.NestedBlock{
   112  						Nesting: configschema.NestingList,
   113  					},
   114  					"map": &configschema.NestedBlock{
   115  						Nesting: configschema.NestingMap,
   116  					},
   117  					"set": &configschema.NestedBlock{
   118  						Nesting: configschema.NestingSet,
   119  					},
   120  					"single": &configschema.NestedBlock{
   121  						Nesting: configschema.NestingSingle,
   122  						Block: configschema.Block{
   123  							Attributes: map[string]*configschema.Attribute{
   124  								"foo": {
   125  									Type:     cty.DynamicPseudoType,
   126  									Required: true,
   127  								},
   128  							},
   129  						},
   130  					},
   131  				},
   132  			},
   133  		},
   134  		"deep block nesting": {
   135  			&proto.Schema_Block{
   136  				BlockTypes: []*proto.Schema_NestedBlock{
   137  					{
   138  						TypeName: "single",
   139  						Nesting:  proto.Schema_NestedBlock_SINGLE,
   140  						Block: &proto.Schema_Block{
   141  							BlockTypes: []*proto.Schema_NestedBlock{
   142  								{
   143  									TypeName: "list",
   144  									Nesting:  proto.Schema_NestedBlock_LIST,
   145  									Block: &proto.Schema_Block{
   146  										BlockTypes: []*proto.Schema_NestedBlock{
   147  											{
   148  												TypeName: "set",
   149  												Nesting:  proto.Schema_NestedBlock_SET,
   150  												Block:    &proto.Schema_Block{},
   151  											},
   152  										},
   153  									},
   154  								},
   155  							},
   156  						},
   157  					},
   158  				},
   159  			},
   160  			&configschema.Block{
   161  				BlockTypes: map[string]*configschema.NestedBlock{
   162  					"single": &configschema.NestedBlock{
   163  						Nesting: configschema.NestingSingle,
   164  						Block: configschema.Block{
   165  							BlockTypes: map[string]*configschema.NestedBlock{
   166  								"list": &configschema.NestedBlock{
   167  									Nesting: configschema.NestingList,
   168  									Block: configschema.Block{
   169  										BlockTypes: map[string]*configschema.NestedBlock{
   170  											"set": &configschema.NestedBlock{
   171  												Nesting: configschema.NestingSet,
   172  											},
   173  										},
   174  									},
   175  								},
   176  							},
   177  						},
   178  					},
   179  				},
   180  			},
   181  		},
   182  	}
   183  
   184  	for name, tc := range tests {
   185  		t.Run(name, func(t *testing.T) {
   186  			converted := ProtoToConfigSchema(tc.Block)
   187  			if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) {
   188  				t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty))
   189  			}
   190  		})
   191  	}
   192  }
   193  
   194  // Test that we can convert configschema to protobuf types and back again.
   195  func TestConvertProtoSchemaBlocks(t *testing.T) {
   196  	tests := map[string]struct {
   197  		Want  *proto.Schema_Block
   198  		Block *configschema.Block
   199  	}{
   200  		"attributes": {
   201  			&proto.Schema_Block{
   202  				Attributes: []*proto.Schema_Attribute{
   203  					{
   204  						Name:     "computed",
   205  						Type:     []byte(`["list","bool"]`),
   206  						Computed: true,
   207  					},
   208  					{
   209  						Name:     "optional",
   210  						Type:     []byte(`"string"`),
   211  						Optional: true,
   212  					},
   213  					{
   214  						Name:     "optional_computed",
   215  						Type:     []byte(`["map","bool"]`),
   216  						Optional: true,
   217  						Computed: true,
   218  					},
   219  					{
   220  						Name:     "required",
   221  						Type:     []byte(`"number"`),
   222  						Required: true,
   223  					},
   224  				},
   225  			},
   226  			&configschema.Block{
   227  				Attributes: map[string]*configschema.Attribute{
   228  					"computed": {
   229  						Type:     cty.List(cty.Bool),
   230  						Computed: true,
   231  					},
   232  					"optional": {
   233  						Type:     cty.String,
   234  						Optional: true,
   235  					},
   236  					"optional_computed": {
   237  						Type:     cty.Map(cty.Bool),
   238  						Optional: true,
   239  						Computed: true,
   240  					},
   241  					"required": {
   242  						Type:     cty.Number,
   243  						Required: true,
   244  					},
   245  				},
   246  			},
   247  		},
   248  		"blocks": {
   249  			&proto.Schema_Block{
   250  				BlockTypes: []*proto.Schema_NestedBlock{
   251  					{
   252  						TypeName: "list",
   253  						Nesting:  proto.Schema_NestedBlock_LIST,
   254  						Block:    &proto.Schema_Block{},
   255  					},
   256  					{
   257  						TypeName: "map",
   258  						Nesting:  proto.Schema_NestedBlock_MAP,
   259  						Block:    &proto.Schema_Block{},
   260  					},
   261  					{
   262  						TypeName: "set",
   263  						Nesting:  proto.Schema_NestedBlock_SET,
   264  						Block:    &proto.Schema_Block{},
   265  					},
   266  					{
   267  						TypeName: "single",
   268  						Nesting:  proto.Schema_NestedBlock_SINGLE,
   269  						Block: &proto.Schema_Block{
   270  							Attributes: []*proto.Schema_Attribute{
   271  								{
   272  									Name:     "foo",
   273  									Type:     []byte(`"dynamic"`),
   274  									Required: true,
   275  								},
   276  							},
   277  						},
   278  					},
   279  				},
   280  			},
   281  			&configschema.Block{
   282  				BlockTypes: map[string]*configschema.NestedBlock{
   283  					"list": &configschema.NestedBlock{
   284  						Nesting: configschema.NestingList,
   285  					},
   286  					"map": &configschema.NestedBlock{
   287  						Nesting: configschema.NestingMap,
   288  					},
   289  					"set": &configschema.NestedBlock{
   290  						Nesting: configschema.NestingSet,
   291  					},
   292  					"single": &configschema.NestedBlock{
   293  						Nesting: configschema.NestingSingle,
   294  						Block: configschema.Block{
   295  							Attributes: map[string]*configschema.Attribute{
   296  								"foo": {
   297  									Type:     cty.DynamicPseudoType,
   298  									Required: true,
   299  								},
   300  							},
   301  						},
   302  					},
   303  				},
   304  			},
   305  		},
   306  		"deep block nesting": {
   307  			&proto.Schema_Block{
   308  				BlockTypes: []*proto.Schema_NestedBlock{
   309  					{
   310  						TypeName: "single",
   311  						Nesting:  proto.Schema_NestedBlock_SINGLE,
   312  						Block: &proto.Schema_Block{
   313  							BlockTypes: []*proto.Schema_NestedBlock{
   314  								{
   315  									TypeName: "list",
   316  									Nesting:  proto.Schema_NestedBlock_LIST,
   317  									Block: &proto.Schema_Block{
   318  										BlockTypes: []*proto.Schema_NestedBlock{
   319  											{
   320  												TypeName: "set",
   321  												Nesting:  proto.Schema_NestedBlock_SET,
   322  												Block:    &proto.Schema_Block{},
   323  											},
   324  										},
   325  									},
   326  								},
   327  							},
   328  						},
   329  					},
   330  				},
   331  			},
   332  			&configschema.Block{
   333  				BlockTypes: map[string]*configschema.NestedBlock{
   334  					"single": &configschema.NestedBlock{
   335  						Nesting: configschema.NestingSingle,
   336  						Block: configschema.Block{
   337  							BlockTypes: map[string]*configschema.NestedBlock{
   338  								"list": &configschema.NestedBlock{
   339  									Nesting: configschema.NestingList,
   340  									Block: configschema.Block{
   341  										BlockTypes: map[string]*configschema.NestedBlock{
   342  											"set": &configschema.NestedBlock{
   343  												Nesting: configschema.NestingSet,
   344  											},
   345  										},
   346  									},
   347  								},
   348  							},
   349  						},
   350  					},
   351  				},
   352  			},
   353  		},
   354  	}
   355  
   356  	for name, tc := range tests {
   357  		t.Run(name, func(t *testing.T) {
   358  			converted := ConfigSchemaToProto(tc.Block)
   359  			if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported) {
   360  				t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported))
   361  			}
   362  		})
   363  	}
   364  }