github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/legacy/terraform/resource_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/hashicorp/terraform/internal/configs/configschema"
     9  	"github.com/zclconf/go-cty/cty"
    10  
    11  	"github.com/hashicorp/terraform/internal/configs/hcl2shim"
    12  	"github.com/mitchellh/reflectwalk"
    13  )
    14  
    15  func TestResourceConfigGet(t *testing.T) {
    16  	fooStringSchema := &configschema.Block{
    17  		Attributes: map[string]*configschema.Attribute{
    18  			"foo": {Type: cty.String, Optional: true},
    19  		},
    20  	}
    21  	fooListSchema := &configschema.Block{
    22  		Attributes: map[string]*configschema.Attribute{
    23  			"foo": {Type: cty.List(cty.Number), Optional: true},
    24  		},
    25  	}
    26  
    27  	cases := []struct {
    28  		Config cty.Value
    29  		Schema *configschema.Block
    30  		Key    string
    31  		Value  interface{}
    32  	}{
    33  		{
    34  			Config: cty.ObjectVal(map[string]cty.Value{
    35  				"foo": cty.StringVal("bar"),
    36  			}),
    37  			Schema: fooStringSchema,
    38  			Key:    "foo",
    39  			Value:  "bar",
    40  		},
    41  
    42  		{
    43  			Config: cty.ObjectVal(map[string]cty.Value{
    44  				"foo": cty.UnknownVal(cty.String),
    45  			}),
    46  			Schema: fooStringSchema,
    47  			Key:    "foo",
    48  			Value:  hcl2shim.UnknownVariableValue,
    49  		},
    50  
    51  		{
    52  			Config: cty.ObjectVal(map[string]cty.Value{
    53  				"foo": cty.ListVal([]cty.Value{
    54  					cty.NumberIntVal(1),
    55  					cty.NumberIntVal(2),
    56  					cty.NumberIntVal(5),
    57  				}),
    58  			}),
    59  			Schema: fooListSchema,
    60  			Key:    "foo.0",
    61  			Value:  1,
    62  		},
    63  
    64  		{
    65  			Config: cty.ObjectVal(map[string]cty.Value{
    66  				"foo": cty.ListVal([]cty.Value{
    67  					cty.NumberIntVal(1),
    68  					cty.NumberIntVal(2),
    69  					cty.NumberIntVal(5),
    70  				}),
    71  			}),
    72  			Schema: fooListSchema,
    73  			Key:    "foo.5",
    74  			Value:  nil,
    75  		},
    76  
    77  		{
    78  			Config: cty.ObjectVal(map[string]cty.Value{
    79  				"foo": cty.ListVal([]cty.Value{
    80  					cty.NumberIntVal(1),
    81  					cty.NumberIntVal(2),
    82  					cty.NumberIntVal(5),
    83  				}),
    84  			}),
    85  			Schema: fooListSchema,
    86  			Key:    "foo.-1",
    87  			Value:  nil,
    88  		},
    89  
    90  		// get from map
    91  		{
    92  			Config: cty.ObjectVal(map[string]cty.Value{
    93  				"mapname": cty.ListVal([]cty.Value{
    94  					cty.MapVal(map[string]cty.Value{
    95  						"key": cty.NumberIntVal(1),
    96  					}),
    97  				}),
    98  			}),
    99  			Schema: &configschema.Block{
   100  				Attributes: map[string]*configschema.Attribute{
   101  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   102  				},
   103  			},
   104  			Key:   "mapname.0.key",
   105  			Value: 1,
   106  		},
   107  
   108  		// get from map with dot in key
   109  		{
   110  			Config: cty.ObjectVal(map[string]cty.Value{
   111  				"mapname": cty.ListVal([]cty.Value{
   112  					cty.MapVal(map[string]cty.Value{
   113  						"key.name": cty.NumberIntVal(1),
   114  					}),
   115  				}),
   116  			}),
   117  			Schema: &configschema.Block{
   118  				Attributes: map[string]*configschema.Attribute{
   119  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   120  				},
   121  			},
   122  			Key:   "mapname.0.key.name",
   123  			Value: 1,
   124  		},
   125  
   126  		// get from map with overlapping key names
   127  		{
   128  			Config: cty.ObjectVal(map[string]cty.Value{
   129  				"mapname": cty.ListVal([]cty.Value{
   130  					cty.MapVal(map[string]cty.Value{
   131  						"key.name":   cty.NumberIntVal(1),
   132  						"key.name.2": cty.NumberIntVal(2),
   133  					}),
   134  				}),
   135  			}),
   136  			Schema: &configschema.Block{
   137  				Attributes: map[string]*configschema.Attribute{
   138  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   139  				},
   140  			},
   141  			Key:   "mapname.0.key.name.2",
   142  			Value: 2,
   143  		},
   144  		{
   145  			Config: cty.ObjectVal(map[string]cty.Value{
   146  				"mapname": cty.ListVal([]cty.Value{
   147  					cty.MapVal(map[string]cty.Value{
   148  						"key.name":     cty.NumberIntVal(1),
   149  						"key.name.foo": cty.NumberIntVal(2),
   150  					}),
   151  				}),
   152  			}),
   153  			Schema: &configschema.Block{
   154  				Attributes: map[string]*configschema.Attribute{
   155  					"mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true},
   156  				},
   157  			},
   158  			Key:   "mapname.0.key.name",
   159  			Value: 1,
   160  		},
   161  		{
   162  			Config: cty.ObjectVal(map[string]cty.Value{
   163  				"mapname": cty.ListVal([]cty.Value{
   164  					cty.MapVal(map[string]cty.Value{
   165  						"listkey": cty.ListVal([]cty.Value{
   166  							cty.MapVal(map[string]cty.Value{
   167  								"key": cty.NumberIntVal(3),
   168  							}),
   169  						}),
   170  					}),
   171  				}),
   172  			}),
   173  			Schema: &configschema.Block{
   174  				Attributes: map[string]*configschema.Attribute{
   175  					"mapname": {Type: cty.List(cty.Map(cty.List(cty.Map(cty.Number)))), Optional: true},
   176  				},
   177  			},
   178  			Key:   "mapname.0.listkey.0.key",
   179  			Value: 3,
   180  		},
   181  	}
   182  
   183  	for i, tc := range cases {
   184  		rc := NewResourceConfigShimmed(tc.Config, tc.Schema)
   185  
   186  		// Test getting a key
   187  		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
   188  			v, ok := rc.Get(tc.Key)
   189  			if ok && v == nil {
   190  				t.Fatal("(nil, true) returned from Get")
   191  			}
   192  
   193  			if !reflect.DeepEqual(v, tc.Value) {
   194  				t.Fatalf("%d bad: %#v", i, v)
   195  			}
   196  		})
   197  
   198  		// Test copying and equality
   199  		t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
   200  			copy := rc.DeepCopy()
   201  			if !reflect.DeepEqual(copy, rc) {
   202  				t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
   203  			}
   204  
   205  			if !copy.Equal(rc) {
   206  				t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
   207  			}
   208  			if !rc.Equal(copy) {
   209  				t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
   210  			}
   211  		})
   212  	}
   213  }
   214  
   215  func TestResourceConfigDeepCopy_nil(t *testing.T) {
   216  	var nilRc *ResourceConfig
   217  	actual := nilRc.DeepCopy()
   218  	if actual != nil {
   219  		t.Fatalf("bad: %#v", actual)
   220  	}
   221  }
   222  
   223  func TestResourceConfigDeepCopy_nilComputed(t *testing.T) {
   224  	rc := &ResourceConfig{}
   225  	actual := rc.DeepCopy()
   226  	if actual.ComputedKeys != nil {
   227  		t.Fatalf("bad: %#v", actual)
   228  	}
   229  }
   230  
   231  func TestResourceConfigEqual_nil(t *testing.T) {
   232  	var nilRc *ResourceConfig
   233  	notNil := NewResourceConfigShimmed(cty.EmptyObjectVal, &configschema.Block{})
   234  
   235  	if nilRc.Equal(notNil) {
   236  		t.Fatal("should not be equal")
   237  	}
   238  
   239  	if notNil.Equal(nilRc) {
   240  		t.Fatal("should not be equal")
   241  	}
   242  }
   243  
   244  func TestResourceConfigEqual_computedKeyOrder(t *testing.T) {
   245  	v := cty.ObjectVal(map[string]cty.Value{
   246  		"foo": cty.UnknownVal(cty.String),
   247  	})
   248  	schema := &configschema.Block{
   249  		Attributes: map[string]*configschema.Attribute{
   250  			"foo": {Type: cty.String, Optional: true},
   251  		},
   252  	}
   253  	rc := NewResourceConfigShimmed(v, schema)
   254  	rc2 := NewResourceConfigShimmed(v, schema)
   255  
   256  	// Set the computed keys manually to force ordering to differ
   257  	rc.ComputedKeys = []string{"foo", "bar"}
   258  	rc2.ComputedKeys = []string{"bar", "foo"}
   259  
   260  	if !rc.Equal(rc2) {
   261  		t.Fatal("should be equal")
   262  	}
   263  }
   264  
   265  func TestUnknownCheckWalker(t *testing.T) {
   266  	cases := []struct {
   267  		Name   string
   268  		Input  interface{}
   269  		Result bool
   270  	}{
   271  		{
   272  			"primitive",
   273  			42,
   274  			false,
   275  		},
   276  
   277  		{
   278  			"primitive computed",
   279  			hcl2shim.UnknownVariableValue,
   280  			true,
   281  		},
   282  
   283  		{
   284  			"list",
   285  			[]interface{}{"foo", hcl2shim.UnknownVariableValue},
   286  			true,
   287  		},
   288  
   289  		{
   290  			"nested list",
   291  			[]interface{}{
   292  				"foo",
   293  				[]interface{}{hcl2shim.UnknownVariableValue},
   294  			},
   295  			true,
   296  		},
   297  	}
   298  
   299  	for i, tc := range cases {
   300  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   301  			var w unknownCheckWalker
   302  			if err := reflectwalk.Walk(tc.Input, &w); err != nil {
   303  				t.Fatalf("err: %s", err)
   304  			}
   305  
   306  			if w.Unknown != tc.Result {
   307  				t.Fatalf("bad: %v", w.Unknown)
   308  			}
   309  		})
   310  	}
   311  }
   312  
   313  func TestNewResourceConfigShimmed(t *testing.T) {
   314  	for _, tc := range []struct {
   315  		Name     string
   316  		Val      cty.Value
   317  		Schema   *configschema.Block
   318  		Expected *ResourceConfig
   319  	}{
   320  		{
   321  			Name: "empty object",
   322  			Val:  cty.NullVal(cty.EmptyObject),
   323  			Schema: &configschema.Block{
   324  				Attributes: map[string]*configschema.Attribute{
   325  					"foo": {
   326  						Type:     cty.String,
   327  						Optional: true,
   328  					},
   329  				},
   330  			},
   331  			Expected: &ResourceConfig{
   332  				Raw:    map[string]interface{}{},
   333  				Config: map[string]interface{}{},
   334  			},
   335  		},
   336  		{
   337  			Name: "basic",
   338  			Val: cty.ObjectVal(map[string]cty.Value{
   339  				"foo": cty.StringVal("bar"),
   340  			}),
   341  			Schema: &configschema.Block{
   342  				Attributes: map[string]*configschema.Attribute{
   343  					"foo": {
   344  						Type:     cty.String,
   345  						Optional: true,
   346  					},
   347  				},
   348  			},
   349  			Expected: &ResourceConfig{
   350  				Raw: map[string]interface{}{
   351  					"foo": "bar",
   352  				},
   353  				Config: map[string]interface{}{
   354  					"foo": "bar",
   355  				},
   356  			},
   357  		},
   358  		{
   359  			Name: "null string",
   360  			Val: cty.ObjectVal(map[string]cty.Value{
   361  				"foo": cty.NullVal(cty.String),
   362  			}),
   363  			Schema: &configschema.Block{
   364  				Attributes: map[string]*configschema.Attribute{
   365  					"foo": {
   366  						Type:     cty.String,
   367  						Optional: true,
   368  					},
   369  				},
   370  			},
   371  			Expected: &ResourceConfig{
   372  				Raw:    map[string]interface{}{},
   373  				Config: map[string]interface{}{},
   374  			},
   375  		},
   376  		{
   377  			Name: "unknown string",
   378  			Val: cty.ObjectVal(map[string]cty.Value{
   379  				"foo": cty.UnknownVal(cty.String),
   380  			}),
   381  			Schema: &configschema.Block{
   382  				Attributes: map[string]*configschema.Attribute{
   383  					"foo": {
   384  						Type:     cty.String,
   385  						Optional: true,
   386  					},
   387  				},
   388  			},
   389  			Expected: &ResourceConfig{
   390  				ComputedKeys: []string{"foo"},
   391  				Raw: map[string]interface{}{
   392  					"foo": hcl2shim.UnknownVariableValue,
   393  				},
   394  				Config: map[string]interface{}{
   395  					"foo": hcl2shim.UnknownVariableValue,
   396  				},
   397  			},
   398  		},
   399  		{
   400  			Name: "unknown collections",
   401  			Val: cty.ObjectVal(map[string]cty.Value{
   402  				"bar": cty.UnknownVal(cty.Map(cty.String)),
   403  				"baz": cty.UnknownVal(cty.List(cty.String)),
   404  			}),
   405  			Schema: &configschema.Block{
   406  				Attributes: map[string]*configschema.Attribute{
   407  					"bar": {
   408  						Type:     cty.Map(cty.String),
   409  						Required: true,
   410  					},
   411  					"baz": {
   412  						Type:     cty.List(cty.String),
   413  						Optional: true,
   414  					},
   415  				},
   416  			},
   417  			Expected: &ResourceConfig{
   418  				ComputedKeys: []string{"bar", "baz"},
   419  				Raw: map[string]interface{}{
   420  					"bar": hcl2shim.UnknownVariableValue,
   421  					"baz": hcl2shim.UnknownVariableValue,
   422  				},
   423  				Config: map[string]interface{}{
   424  					"bar": hcl2shim.UnknownVariableValue,
   425  					"baz": hcl2shim.UnknownVariableValue,
   426  				},
   427  			},
   428  		},
   429  		{
   430  			Name: "null collections",
   431  			Val: cty.ObjectVal(map[string]cty.Value{
   432  				"bar": cty.NullVal(cty.Map(cty.String)),
   433  				"baz": cty.NullVal(cty.List(cty.String)),
   434  			}),
   435  			Schema: &configschema.Block{
   436  				Attributes: map[string]*configschema.Attribute{
   437  					"bar": {
   438  						Type:     cty.Map(cty.String),
   439  						Required: true,
   440  					},
   441  					"baz": {
   442  						Type:     cty.List(cty.String),
   443  						Optional: true,
   444  					},
   445  				},
   446  			},
   447  			Expected: &ResourceConfig{
   448  				Raw:    map[string]interface{}{},
   449  				Config: map[string]interface{}{},
   450  			},
   451  		},
   452  		{
   453  			Name: "unknown blocks",
   454  			Val: cty.ObjectVal(map[string]cty.Value{
   455  				"bar": cty.UnknownVal(cty.Map(cty.String)),
   456  				"baz": cty.UnknownVal(cty.List(cty.String)),
   457  			}),
   458  			Schema: &configschema.Block{
   459  				BlockTypes: map[string]*configschema.NestedBlock{
   460  					"bar": {
   461  						Block:   configschema.Block{},
   462  						Nesting: configschema.NestingList,
   463  					},
   464  					"baz": {
   465  						Block:   configschema.Block{},
   466  						Nesting: configschema.NestingSet,
   467  					},
   468  				},
   469  			},
   470  			Expected: &ResourceConfig{
   471  				ComputedKeys: []string{"bar", "baz"},
   472  				Raw: map[string]interface{}{
   473  					"bar": hcl2shim.UnknownVariableValue,
   474  					"baz": hcl2shim.UnknownVariableValue,
   475  				},
   476  				Config: map[string]interface{}{
   477  					"bar": hcl2shim.UnknownVariableValue,
   478  					"baz": hcl2shim.UnknownVariableValue,
   479  				},
   480  			},
   481  		},
   482  		{
   483  			Name: "unknown in nested blocks",
   484  			Val: cty.ObjectVal(map[string]cty.Value{
   485  				"bar": cty.ListVal([]cty.Value{
   486  					cty.ObjectVal(map[string]cty.Value{
   487  						"baz": cty.ListVal([]cty.Value{
   488  							cty.ObjectVal(map[string]cty.Value{
   489  								"list": cty.UnknownVal(cty.List(cty.String)),
   490  							}),
   491  						}),
   492  					}),
   493  				}),
   494  			}),
   495  			Schema: &configschema.Block{
   496  				BlockTypes: map[string]*configschema.NestedBlock{
   497  					"bar": {
   498  						Block: configschema.Block{
   499  							BlockTypes: map[string]*configschema.NestedBlock{
   500  								"baz": {
   501  									Block: configschema.Block{
   502  										Attributes: map[string]*configschema.Attribute{
   503  											"list": {Type: cty.List(cty.String),
   504  												Optional: true,
   505  											},
   506  										},
   507  									},
   508  									Nesting: configschema.NestingList,
   509  								},
   510  							},
   511  						},
   512  						Nesting: configschema.NestingList,
   513  					},
   514  				},
   515  			},
   516  			Expected: &ResourceConfig{
   517  				ComputedKeys: []string{"bar.0.baz.0.list"},
   518  				Raw: map[string]interface{}{
   519  					"bar": []interface{}{map[string]interface{}{
   520  						"baz": []interface{}{map[string]interface{}{
   521  							"list": "74D93920-ED26-11E3-AC10-0800200C9A66",
   522  						}},
   523  					}},
   524  				},
   525  				Config: map[string]interface{}{
   526  					"bar": []interface{}{map[string]interface{}{
   527  						"baz": []interface{}{map[string]interface{}{
   528  							"list": "74D93920-ED26-11E3-AC10-0800200C9A66",
   529  						}},
   530  					}},
   531  				},
   532  			},
   533  		},
   534  		{
   535  			Name: "unknown in set",
   536  			Val: cty.ObjectVal(map[string]cty.Value{
   537  				"bar": cty.SetVal([]cty.Value{
   538  					cty.ObjectVal(map[string]cty.Value{
   539  						"val": cty.UnknownVal(cty.String),
   540  					}),
   541  				}),
   542  			}),
   543  			Schema: &configschema.Block{
   544  				BlockTypes: map[string]*configschema.NestedBlock{
   545  					"bar": {
   546  						Block: configschema.Block{
   547  							Attributes: map[string]*configschema.Attribute{
   548  								"val": {
   549  									Type:     cty.String,
   550  									Optional: true,
   551  								},
   552  							},
   553  						},
   554  						Nesting: configschema.NestingSet,
   555  					},
   556  				},
   557  			},
   558  			Expected: &ResourceConfig{
   559  				ComputedKeys: []string{"bar.0.val"},
   560  				Raw: map[string]interface{}{
   561  					"bar": []interface{}{map[string]interface{}{
   562  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   563  					}},
   564  				},
   565  				Config: map[string]interface{}{
   566  					"bar": []interface{}{map[string]interface{}{
   567  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   568  					}},
   569  				},
   570  			},
   571  		},
   572  		{
   573  			Name: "unknown in attribute sets",
   574  			Val: cty.ObjectVal(map[string]cty.Value{
   575  				"bar": cty.SetVal([]cty.Value{
   576  					cty.ObjectVal(map[string]cty.Value{
   577  						"val": cty.UnknownVal(cty.String),
   578  					}),
   579  				}),
   580  				"baz": cty.SetVal([]cty.Value{
   581  					cty.ObjectVal(map[string]cty.Value{
   582  						"obj": cty.UnknownVal(cty.Object(map[string]cty.Type{
   583  							"attr": cty.List(cty.String),
   584  						})),
   585  					}),
   586  					cty.ObjectVal(map[string]cty.Value{
   587  						"obj": cty.ObjectVal(map[string]cty.Value{
   588  							"attr": cty.UnknownVal(cty.List(cty.String)),
   589  						}),
   590  					}),
   591  				}),
   592  			}),
   593  			Schema: &configschema.Block{
   594  				Attributes: map[string]*configschema.Attribute{
   595  					"bar": &configschema.Attribute{
   596  						Type: cty.Set(cty.Object(map[string]cty.Type{
   597  							"val": cty.String,
   598  						})),
   599  					},
   600  					"baz": &configschema.Attribute{
   601  						Type: cty.Set(cty.Object(map[string]cty.Type{
   602  							"obj": cty.Object(map[string]cty.Type{
   603  								"attr": cty.List(cty.String),
   604  							}),
   605  						})),
   606  					},
   607  				},
   608  			},
   609  			Expected: &ResourceConfig{
   610  				ComputedKeys: []string{"bar.0.val", "baz.0.obj.attr", "baz.1.obj"},
   611  				Raw: map[string]interface{}{
   612  					"bar": []interface{}{map[string]interface{}{
   613  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   614  					}},
   615  					"baz": []interface{}{
   616  						map[string]interface{}{
   617  							"obj": map[string]interface{}{
   618  								"attr": "74D93920-ED26-11E3-AC10-0800200C9A66",
   619  							},
   620  						},
   621  						map[string]interface{}{
   622  							"obj": "74D93920-ED26-11E3-AC10-0800200C9A66",
   623  						},
   624  					},
   625  				},
   626  				Config: map[string]interface{}{
   627  					"bar": []interface{}{map[string]interface{}{
   628  						"val": "74D93920-ED26-11E3-AC10-0800200C9A66",
   629  					}},
   630  					"baz": []interface{}{
   631  						map[string]interface{}{
   632  							"obj": map[string]interface{}{
   633  								"attr": "74D93920-ED26-11E3-AC10-0800200C9A66",
   634  							},
   635  						},
   636  						map[string]interface{}{
   637  							"obj": "74D93920-ED26-11E3-AC10-0800200C9A66",
   638  						},
   639  					},
   640  				},
   641  			},
   642  		},
   643  		{
   644  			Name: "null blocks",
   645  			Val: cty.ObjectVal(map[string]cty.Value{
   646  				"bar": cty.NullVal(cty.Map(cty.String)),
   647  				"baz": cty.NullVal(cty.List(cty.String)),
   648  			}),
   649  			Schema: &configschema.Block{
   650  				BlockTypes: map[string]*configschema.NestedBlock{
   651  					"bar": {
   652  						Block:   configschema.Block{},
   653  						Nesting: configschema.NestingMap,
   654  					},
   655  					"baz": {
   656  						Block:   configschema.Block{},
   657  						Nesting: configschema.NestingSingle,
   658  					},
   659  				},
   660  			},
   661  			Expected: &ResourceConfig{
   662  				Raw:    map[string]interface{}{},
   663  				Config: map[string]interface{}{},
   664  			},
   665  		},
   666  	} {
   667  		t.Run(tc.Name, func(*testing.T) {
   668  			cfg := NewResourceConfigShimmed(tc.Val, tc.Schema)
   669  			if !tc.Expected.Equal(cfg) {
   670  				t.Fatalf("expected:\n%#v\ngot:\n%#v", tc.Expected, cfg)
   671  			}
   672  		})
   673  	}
   674  }