github.com/pulumi/terraform@v1.4.0/pkg/plugin6/convert/schema_test.go (about)

     1  package convert
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/google/go-cmp/cmp"
     7  	"github.com/google/go-cmp/cmp/cmpopts"
     8  	"github.com/pulumi/terraform/pkg/configs/configschema"
     9  	proto "github.com/pulumi/terraform/pkg/tfplugin6"
    10  	"github.com/zclconf/go-cty/cty"
    11  )
    12  
    13  var (
    14  	equateEmpty   = cmpopts.EquateEmpty()
    15  	typeComparer  = cmp.Comparer(cty.Type.Equals)
    16  	valueComparer = cmp.Comparer(cty.Value.RawEquals)
    17  )
    18  
    19  // Test that we can convert configschema to protobuf types and back again.
    20  func TestConvertSchemaBlocks(t *testing.T) {
    21  	tests := map[string]struct {
    22  		Block *proto.Schema_Block
    23  		Want  *configschema.Block
    24  	}{
    25  		"attributes": {
    26  			&proto.Schema_Block{
    27  				Attributes: []*proto.Schema_Attribute{
    28  					{
    29  						Name:     "computed",
    30  						Type:     []byte(`["list","bool"]`),
    31  						Computed: true,
    32  					},
    33  					{
    34  						Name:     "optional",
    35  						Type:     []byte(`"string"`),
    36  						Optional: true,
    37  					},
    38  					{
    39  						Name:     "optional_computed",
    40  						Type:     []byte(`["map","bool"]`),
    41  						Optional: true,
    42  						Computed: true,
    43  					},
    44  					{
    45  						Name:     "required",
    46  						Type:     []byte(`"number"`),
    47  						Required: true,
    48  					},
    49  					{
    50  						Name: "nested_type",
    51  						NestedType: &proto.Schema_Object{
    52  							Nesting: proto.Schema_Object_SINGLE,
    53  							Attributes: []*proto.Schema_Attribute{
    54  								{
    55  									Name:     "computed",
    56  									Type:     []byte(`["list","bool"]`),
    57  									Computed: true,
    58  								},
    59  								{
    60  									Name:     "optional",
    61  									Type:     []byte(`"string"`),
    62  									Optional: true,
    63  								},
    64  								{
    65  									Name:     "optional_computed",
    66  									Type:     []byte(`["map","bool"]`),
    67  									Optional: true,
    68  									Computed: true,
    69  								},
    70  								{
    71  									Name:     "required",
    72  									Type:     []byte(`"number"`),
    73  									Required: true,
    74  								},
    75  							},
    76  						},
    77  						Required: true,
    78  					},
    79  					{
    80  						Name: "deeply_nested_type",
    81  						NestedType: &proto.Schema_Object{
    82  							Nesting: proto.Schema_Object_SINGLE,
    83  							Attributes: []*proto.Schema_Attribute{
    84  								{
    85  									Name: "first_level",
    86  									NestedType: &proto.Schema_Object{
    87  										Nesting: proto.Schema_Object_SINGLE,
    88  										Attributes: []*proto.Schema_Attribute{
    89  											{
    90  												Name:     "computed",
    91  												Type:     []byte(`["list","bool"]`),
    92  												Computed: true,
    93  											},
    94  											{
    95  												Name:     "optional",
    96  												Type:     []byte(`"string"`),
    97  												Optional: true,
    98  											},
    99  											{
   100  												Name:     "optional_computed",
   101  												Type:     []byte(`["map","bool"]`),
   102  												Optional: true,
   103  												Computed: true,
   104  											},
   105  											{
   106  												Name:     "required",
   107  												Type:     []byte(`"number"`),
   108  												Required: true,
   109  											},
   110  										},
   111  									},
   112  									Computed: true,
   113  								},
   114  							},
   115  						},
   116  						Required: true,
   117  					},
   118  					{
   119  						Name: "nested_list",
   120  						NestedType: &proto.Schema_Object{
   121  							Nesting: proto.Schema_Object_LIST,
   122  							Attributes: []*proto.Schema_Attribute{
   123  								{
   124  									Name:     "required",
   125  									Type:     []byte(`"string"`),
   126  									Computed: true,
   127  								},
   128  							},
   129  						},
   130  						Required: true,
   131  					},
   132  					{
   133  						Name: "nested_set",
   134  						NestedType: &proto.Schema_Object{
   135  							Nesting: proto.Schema_Object_SET,
   136  							Attributes: []*proto.Schema_Attribute{
   137  								{
   138  									Name:     "required",
   139  									Type:     []byte(`"string"`),
   140  									Computed: true,
   141  								},
   142  							},
   143  						},
   144  						Required: true,
   145  					},
   146  					{
   147  						Name: "nested_map",
   148  						NestedType: &proto.Schema_Object{
   149  							Nesting: proto.Schema_Object_MAP,
   150  							Attributes: []*proto.Schema_Attribute{
   151  								{
   152  									Name:     "required",
   153  									Type:     []byte(`"string"`),
   154  									Computed: true,
   155  								},
   156  							},
   157  						},
   158  						Required: true,
   159  					},
   160  				},
   161  			},
   162  			&configschema.Block{
   163  				Attributes: map[string]*configschema.Attribute{
   164  					"computed": {
   165  						Type:     cty.List(cty.Bool),
   166  						Computed: true,
   167  					},
   168  					"optional": {
   169  						Type:     cty.String,
   170  						Optional: true,
   171  					},
   172  					"optional_computed": {
   173  						Type:     cty.Map(cty.Bool),
   174  						Optional: true,
   175  						Computed: true,
   176  					},
   177  					"required": {
   178  						Type:     cty.Number,
   179  						Required: true,
   180  					},
   181  					"nested_type": {
   182  						NestedType: &configschema.Object{
   183  							Attributes: map[string]*configschema.Attribute{
   184  								"computed": {
   185  									Type:     cty.List(cty.Bool),
   186  									Computed: true,
   187  								},
   188  								"optional": {
   189  									Type:     cty.String,
   190  									Optional: true,
   191  								},
   192  								"optional_computed": {
   193  									Type:     cty.Map(cty.Bool),
   194  									Optional: true,
   195  									Computed: true,
   196  								},
   197  								"required": {
   198  									Type:     cty.Number,
   199  									Required: true,
   200  								},
   201  							},
   202  							Nesting: configschema.NestingSingle,
   203  						},
   204  						Required: true,
   205  					},
   206  					"deeply_nested_type": {
   207  						NestedType: &configschema.Object{
   208  							Attributes: map[string]*configschema.Attribute{
   209  								"first_level": {
   210  									NestedType: &configschema.Object{
   211  										Nesting: configschema.NestingSingle,
   212  										Attributes: map[string]*configschema.Attribute{
   213  											"computed": {
   214  												Type:     cty.List(cty.Bool),
   215  												Computed: true,
   216  											},
   217  											"optional": {
   218  												Type:     cty.String,
   219  												Optional: true,
   220  											},
   221  											"optional_computed": {
   222  												Type:     cty.Map(cty.Bool),
   223  												Optional: true,
   224  												Computed: true,
   225  											},
   226  											"required": {
   227  												Type:     cty.Number,
   228  												Required: true,
   229  											},
   230  										},
   231  									},
   232  									Computed: true,
   233  								},
   234  							},
   235  							Nesting: configschema.NestingSingle,
   236  						},
   237  						Required: true,
   238  					},
   239  					"nested_list": {
   240  						NestedType: &configschema.Object{
   241  							Nesting: configschema.NestingList,
   242  							Attributes: map[string]*configschema.Attribute{
   243  								"required": {
   244  									Type:     cty.String,
   245  									Computed: true,
   246  								},
   247  							},
   248  						},
   249  						Required: true,
   250  					},
   251  					"nested_map": {
   252  						NestedType: &configschema.Object{
   253  							Nesting: configschema.NestingMap,
   254  							Attributes: map[string]*configschema.Attribute{
   255  								"required": {
   256  									Type:     cty.String,
   257  									Computed: true,
   258  								},
   259  							},
   260  						},
   261  						Required: true,
   262  					},
   263  					"nested_set": {
   264  						NestedType: &configschema.Object{
   265  							Nesting: configschema.NestingSet,
   266  							Attributes: map[string]*configschema.Attribute{
   267  								"required": {
   268  									Type:     cty.String,
   269  									Computed: true,
   270  								},
   271  							},
   272  						},
   273  						Required: true,
   274  					},
   275  				},
   276  			},
   277  		},
   278  		"blocks": {
   279  			&proto.Schema_Block{
   280  				BlockTypes: []*proto.Schema_NestedBlock{
   281  					{
   282  						TypeName: "list",
   283  						Nesting:  proto.Schema_NestedBlock_LIST,
   284  						Block:    &proto.Schema_Block{},
   285  					},
   286  					{
   287  						TypeName: "map",
   288  						Nesting:  proto.Schema_NestedBlock_MAP,
   289  						Block:    &proto.Schema_Block{},
   290  					},
   291  					{
   292  						TypeName: "set",
   293  						Nesting:  proto.Schema_NestedBlock_SET,
   294  						Block:    &proto.Schema_Block{},
   295  					},
   296  					{
   297  						TypeName: "single",
   298  						Nesting:  proto.Schema_NestedBlock_SINGLE,
   299  						Block: &proto.Schema_Block{
   300  							Attributes: []*proto.Schema_Attribute{
   301  								{
   302  									Name:     "foo",
   303  									Type:     []byte(`"dynamic"`),
   304  									Required: true,
   305  								},
   306  							},
   307  						},
   308  					},
   309  				},
   310  			},
   311  			&configschema.Block{
   312  				BlockTypes: map[string]*configschema.NestedBlock{
   313  					"list": &configschema.NestedBlock{
   314  						Nesting: configschema.NestingList,
   315  					},
   316  					"map": &configschema.NestedBlock{
   317  						Nesting: configschema.NestingMap,
   318  					},
   319  					"set": &configschema.NestedBlock{
   320  						Nesting: configschema.NestingSet,
   321  					},
   322  					"single": &configschema.NestedBlock{
   323  						Nesting: configschema.NestingSingle,
   324  						Block: configschema.Block{
   325  							Attributes: map[string]*configschema.Attribute{
   326  								"foo": {
   327  									Type:     cty.DynamicPseudoType,
   328  									Required: true,
   329  								},
   330  							},
   331  						},
   332  					},
   333  				},
   334  			},
   335  		},
   336  		"deep block nesting": {
   337  			&proto.Schema_Block{
   338  				BlockTypes: []*proto.Schema_NestedBlock{
   339  					{
   340  						TypeName: "single",
   341  						Nesting:  proto.Schema_NestedBlock_SINGLE,
   342  						Block: &proto.Schema_Block{
   343  							BlockTypes: []*proto.Schema_NestedBlock{
   344  								{
   345  									TypeName: "list",
   346  									Nesting:  proto.Schema_NestedBlock_LIST,
   347  									Block: &proto.Schema_Block{
   348  										BlockTypes: []*proto.Schema_NestedBlock{
   349  											{
   350  												TypeName: "set",
   351  												Nesting:  proto.Schema_NestedBlock_SET,
   352  												Block:    &proto.Schema_Block{},
   353  											},
   354  										},
   355  									},
   356  								},
   357  							},
   358  						},
   359  					},
   360  				},
   361  			},
   362  			&configschema.Block{
   363  				BlockTypes: map[string]*configschema.NestedBlock{
   364  					"single": &configschema.NestedBlock{
   365  						Nesting: configschema.NestingSingle,
   366  						Block: configschema.Block{
   367  							BlockTypes: map[string]*configschema.NestedBlock{
   368  								"list": &configschema.NestedBlock{
   369  									Nesting: configschema.NestingList,
   370  									Block: configschema.Block{
   371  										BlockTypes: map[string]*configschema.NestedBlock{
   372  											"set": &configschema.NestedBlock{
   373  												Nesting: configschema.NestingSet,
   374  											},
   375  										},
   376  									},
   377  								},
   378  							},
   379  						},
   380  					},
   381  				},
   382  			},
   383  		},
   384  	}
   385  
   386  	for name, tc := range tests {
   387  		t.Run(name, func(t *testing.T) {
   388  			converted := ProtoToConfigSchema(tc.Block)
   389  			if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) {
   390  				t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty))
   391  			}
   392  		})
   393  	}
   394  }
   395  
   396  // Test that we can convert configschema to protobuf types and back again.
   397  func TestConvertProtoSchemaBlocks(t *testing.T) {
   398  	tests := map[string]struct {
   399  		Want  *proto.Schema_Block
   400  		Block *configschema.Block
   401  	}{
   402  		"attributes": {
   403  			&proto.Schema_Block{
   404  				Attributes: []*proto.Schema_Attribute{
   405  					{
   406  						Name:     "computed",
   407  						Type:     []byte(`["list","bool"]`),
   408  						Computed: true,
   409  					},
   410  					{
   411  						Name:     "optional",
   412  						Type:     []byte(`"string"`),
   413  						Optional: true,
   414  					},
   415  					{
   416  						Name:     "optional_computed",
   417  						Type:     []byte(`["map","bool"]`),
   418  						Optional: true,
   419  						Computed: true,
   420  					},
   421  					{
   422  						Name:     "required",
   423  						Type:     []byte(`"number"`),
   424  						Required: true,
   425  					},
   426  				},
   427  			},
   428  			&configschema.Block{
   429  				Attributes: map[string]*configschema.Attribute{
   430  					"computed": {
   431  						Type:     cty.List(cty.Bool),
   432  						Computed: true,
   433  					},
   434  					"optional": {
   435  						Type:     cty.String,
   436  						Optional: true,
   437  					},
   438  					"optional_computed": {
   439  						Type:     cty.Map(cty.Bool),
   440  						Optional: true,
   441  						Computed: true,
   442  					},
   443  					"required": {
   444  						Type:     cty.Number,
   445  						Required: true,
   446  					},
   447  				},
   448  			},
   449  		},
   450  		"blocks": {
   451  			&proto.Schema_Block{
   452  				BlockTypes: []*proto.Schema_NestedBlock{
   453  					{
   454  						TypeName: "list",
   455  						Nesting:  proto.Schema_NestedBlock_LIST,
   456  						Block:    &proto.Schema_Block{},
   457  					},
   458  					{
   459  						TypeName: "map",
   460  						Nesting:  proto.Schema_NestedBlock_MAP,
   461  						Block:    &proto.Schema_Block{},
   462  					},
   463  					{
   464  						TypeName: "set",
   465  						Nesting:  proto.Schema_NestedBlock_SET,
   466  						Block:    &proto.Schema_Block{},
   467  					},
   468  					{
   469  						TypeName: "single",
   470  						Nesting:  proto.Schema_NestedBlock_SINGLE,
   471  						Block: &proto.Schema_Block{
   472  							Attributes: []*proto.Schema_Attribute{
   473  								{
   474  									Name:     "foo",
   475  									Type:     []byte(`"dynamic"`),
   476  									Required: true,
   477  								},
   478  							},
   479  						},
   480  					},
   481  				},
   482  			},
   483  			&configschema.Block{
   484  				BlockTypes: map[string]*configschema.NestedBlock{
   485  					"list": &configschema.NestedBlock{
   486  						Nesting: configschema.NestingList,
   487  					},
   488  					"map": &configschema.NestedBlock{
   489  						Nesting: configschema.NestingMap,
   490  					},
   491  					"set": &configschema.NestedBlock{
   492  						Nesting: configschema.NestingSet,
   493  					},
   494  					"single": &configschema.NestedBlock{
   495  						Nesting: configschema.NestingSingle,
   496  						Block: configschema.Block{
   497  							Attributes: map[string]*configschema.Attribute{
   498  								"foo": {
   499  									Type:     cty.DynamicPseudoType,
   500  									Required: true,
   501  								},
   502  							},
   503  						},
   504  					},
   505  				},
   506  			},
   507  		},
   508  		"deep block nesting": {
   509  			&proto.Schema_Block{
   510  				BlockTypes: []*proto.Schema_NestedBlock{
   511  					{
   512  						TypeName: "single",
   513  						Nesting:  proto.Schema_NestedBlock_SINGLE,
   514  						Block: &proto.Schema_Block{
   515  							BlockTypes: []*proto.Schema_NestedBlock{
   516  								{
   517  									TypeName: "list",
   518  									Nesting:  proto.Schema_NestedBlock_LIST,
   519  									Block: &proto.Schema_Block{
   520  										BlockTypes: []*proto.Schema_NestedBlock{
   521  											{
   522  												TypeName: "set",
   523  												Nesting:  proto.Schema_NestedBlock_SET,
   524  												Block:    &proto.Schema_Block{},
   525  											},
   526  										},
   527  									},
   528  								},
   529  							},
   530  						},
   531  					},
   532  				},
   533  			},
   534  			&configschema.Block{
   535  				BlockTypes: map[string]*configschema.NestedBlock{
   536  					"single": &configschema.NestedBlock{
   537  						Nesting: configschema.NestingSingle,
   538  						Block: configschema.Block{
   539  							BlockTypes: map[string]*configschema.NestedBlock{
   540  								"list": &configschema.NestedBlock{
   541  									Nesting: configschema.NestingList,
   542  									Block: configschema.Block{
   543  										BlockTypes: map[string]*configschema.NestedBlock{
   544  											"set": &configschema.NestedBlock{
   545  												Nesting: configschema.NestingSet,
   546  											},
   547  										},
   548  									},
   549  								},
   550  							},
   551  						},
   552  					},
   553  				},
   554  			},
   555  		},
   556  	}
   557  
   558  	for name, tc := range tests {
   559  		t.Run(name, func(t *testing.T) {
   560  			converted := ConfigSchemaToProto(tc.Block)
   561  			if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported) {
   562  				t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported))
   563  			}
   564  		})
   565  	}
   566  }