github.com/kevinklinger/open_terraform@v1.3.6/noninternal/configs/configschema/implied_type_test.go (about)

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