github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/configs/hcl2shim/flatmap_test.go (about)

     1  package hcl2shim
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/go-test/deep"
     8  
     9  	"github.com/zclconf/go-cty/cty"
    10  )
    11  
    12  func TestFlatmapValueFromHCL2(t *testing.T) {
    13  	tests := []struct {
    14  		Value cty.Value
    15  		Want  map[string]string
    16  	}{
    17  		{
    18  			cty.EmptyObjectVal,
    19  			map[string]string{},
    20  		},
    21  		{
    22  			cty.ObjectVal(map[string]cty.Value{
    23  				"foo": cty.StringVal("hello"),
    24  			}),
    25  			map[string]string{
    26  				"foo": "hello",
    27  			},
    28  		},
    29  		{
    30  			cty.ObjectVal(map[string]cty.Value{
    31  				"foo": cty.UnknownVal(cty.Bool),
    32  			}),
    33  			map[string]string{
    34  				"foo": UnknownVariableValue,
    35  			},
    36  		},
    37  		{
    38  			cty.ObjectVal(map[string]cty.Value{
    39  				"foo": cty.NumberIntVal(12),
    40  			}),
    41  			map[string]string{
    42  				"foo": "12",
    43  			},
    44  		},
    45  		{
    46  			cty.ObjectVal(map[string]cty.Value{
    47  				"foo": cty.True,
    48  				"bar": cty.False,
    49  			}),
    50  			map[string]string{
    51  				"foo": "true",
    52  				"bar": "false",
    53  			},
    54  		},
    55  		{
    56  			cty.ObjectVal(map[string]cty.Value{
    57  				"foo": cty.StringVal("hello"),
    58  				"bar": cty.StringVal("world"),
    59  				"baz": cty.StringVal("whelp"),
    60  			}),
    61  			map[string]string{
    62  				"foo": "hello",
    63  				"bar": "world",
    64  				"baz": "whelp",
    65  			},
    66  		},
    67  		{
    68  			cty.ObjectVal(map[string]cty.Value{
    69  				"foo": cty.ListValEmpty(cty.String),
    70  			}),
    71  			map[string]string{
    72  				"foo.#": "0",
    73  			},
    74  		},
    75  		{
    76  			cty.ObjectVal(map[string]cty.Value{
    77  				"foo": cty.UnknownVal(cty.List(cty.String)),
    78  			}),
    79  			map[string]string{
    80  				"foo.#": UnknownVariableValue,
    81  			},
    82  		},
    83  		{
    84  			cty.ObjectVal(map[string]cty.Value{
    85  				"foo": cty.ListVal([]cty.Value{
    86  					cty.StringVal("hello"),
    87  				}),
    88  			}),
    89  			map[string]string{
    90  				"foo.#": "1",
    91  				"foo.0": "hello",
    92  			},
    93  		},
    94  		{
    95  			cty.ObjectVal(map[string]cty.Value{
    96  				"foo": cty.ListVal([]cty.Value{
    97  					cty.StringVal("hello"),
    98  					cty.StringVal("world"),
    99  				}),
   100  			}),
   101  			map[string]string{
   102  				"foo.#": "2",
   103  				"foo.0": "hello",
   104  				"foo.1": "world",
   105  			},
   106  		},
   107  		{
   108  			cty.ObjectVal(map[string]cty.Value{
   109  				"foo": cty.MapVal(map[string]cty.Value{
   110  					"hello":       cty.NumberIntVal(12),
   111  					"hello.world": cty.NumberIntVal(10),
   112  				}),
   113  			}),
   114  			map[string]string{
   115  				"foo.%":           "2",
   116  				"foo.hello":       "12",
   117  				"foo.hello.world": "10",
   118  			},
   119  		},
   120  		{
   121  			cty.ObjectVal(map[string]cty.Value{
   122  				"foo": cty.UnknownVal(cty.Map(cty.String)),
   123  			}),
   124  			map[string]string{
   125  				"foo.%": UnknownVariableValue,
   126  			},
   127  		},
   128  		{
   129  			cty.ObjectVal(map[string]cty.Value{
   130  				"foo": cty.MapVal(map[string]cty.Value{
   131  					"hello":       cty.NumberIntVal(12),
   132  					"hello.world": cty.NumberIntVal(10),
   133  				}),
   134  			}),
   135  			map[string]string{
   136  				"foo.%":           "2",
   137  				"foo.hello":       "12",
   138  				"foo.hello.world": "10",
   139  			},
   140  		},
   141  		{
   142  			cty.ObjectVal(map[string]cty.Value{
   143  				"foo": cty.SetVal([]cty.Value{
   144  					cty.StringVal("hello"),
   145  					cty.StringVal("world"),
   146  				}),
   147  			}),
   148  			map[string]string{
   149  				"foo.#": "2",
   150  				"foo.0": "hello",
   151  				"foo.1": "world",
   152  			},
   153  		},
   154  		{
   155  			cty.ObjectVal(map[string]cty.Value{
   156  				"foo": cty.UnknownVal(cty.Set(cty.Number)),
   157  			}),
   158  			map[string]string{
   159  				"foo.#": UnknownVariableValue,
   160  			},
   161  		},
   162  		{
   163  			cty.ObjectVal(map[string]cty.Value{
   164  				"foo": cty.ListVal([]cty.Value{
   165  					cty.ObjectVal(map[string]cty.Value{
   166  						"bar": cty.StringVal("hello"),
   167  						"baz": cty.StringVal("world"),
   168  					}),
   169  					cty.ObjectVal(map[string]cty.Value{
   170  						"bar": cty.StringVal("bloo"),
   171  						"baz": cty.StringVal("blaa"),
   172  					}),
   173  				}),
   174  			}),
   175  			map[string]string{
   176  				"foo.#":     "2",
   177  				"foo.0.bar": "hello",
   178  				"foo.0.baz": "world",
   179  				"foo.1.bar": "bloo",
   180  				"foo.1.baz": "blaa",
   181  			},
   182  		},
   183  		{
   184  			cty.ObjectVal(map[string]cty.Value{
   185  				"foo": cty.ListVal([]cty.Value{
   186  					cty.ObjectVal(map[string]cty.Value{
   187  						"bar": cty.StringVal("hello"),
   188  						"baz": cty.ListVal([]cty.Value{
   189  							cty.True,
   190  							cty.True,
   191  						}),
   192  					}),
   193  					cty.ObjectVal(map[string]cty.Value{
   194  						"bar": cty.StringVal("bloo"),
   195  						"baz": cty.ListVal([]cty.Value{
   196  							cty.False,
   197  							cty.True,
   198  						}),
   199  					}),
   200  				}),
   201  			}),
   202  			map[string]string{
   203  				"foo.#":       "2",
   204  				"foo.0.bar":   "hello",
   205  				"foo.0.baz.#": "2",
   206  				"foo.0.baz.0": "true",
   207  				"foo.0.baz.1": "true",
   208  				"foo.1.bar":   "bloo",
   209  				"foo.1.baz.#": "2",
   210  				"foo.1.baz.0": "false",
   211  				"foo.1.baz.1": "true",
   212  			},
   213  		},
   214  		{
   215  			cty.ObjectVal(map[string]cty.Value{
   216  				"foo": cty.ListVal([]cty.Value{
   217  					cty.UnknownVal(cty.Object(map[string]cty.Type{
   218  						"bar": cty.String,
   219  						"baz": cty.List(cty.Bool),
   220  						"bap": cty.Map(cty.Number),
   221  					})),
   222  				}),
   223  			}),
   224  			map[string]string{
   225  				"foo.#":       "1",
   226  				"foo.0.bar":   UnknownVariableValue,
   227  				"foo.0.baz.#": UnknownVariableValue,
   228  				"foo.0.bap.%": UnknownVariableValue,
   229  			},
   230  		},
   231  		{
   232  			cty.NullVal(cty.Object(map[string]cty.Type{
   233  				"foo": cty.Set(cty.Object(map[string]cty.Type{
   234  					"bar": cty.String,
   235  				})),
   236  			})),
   237  			nil,
   238  		},
   239  	}
   240  
   241  	for _, test := range tests {
   242  		t.Run(test.Value.GoString(), func(t *testing.T) {
   243  			got := FlatmapValueFromHCL2(test.Value)
   244  
   245  			for _, problem := range deep.Equal(got, test.Want) {
   246  				t.Error(problem)
   247  			}
   248  		})
   249  	}
   250  }
   251  
   252  func TestFlatmapValueFromHCL2FromFlatmap(t *testing.T) {
   253  	tests := []struct {
   254  		Name string
   255  		Map  map[string]string
   256  		Type cty.Type
   257  	}{
   258  		{
   259  			"empty flatmap with collections",
   260  			map[string]string{},
   261  			cty.Object(map[string]cty.Type{
   262  				"foo": cty.Map(cty.String),
   263  				"bar": cty.Set(cty.String),
   264  			}),
   265  		},
   266  		{
   267  			"nil flatmap with collections",
   268  			nil,
   269  			cty.Object(map[string]cty.Type{
   270  				"foo": cty.Map(cty.String),
   271  				"bar": cty.Set(cty.String),
   272  			}),
   273  		},
   274  		{
   275  			"empty flatmap with nested collections",
   276  			map[string]string{},
   277  			cty.Object(map[string]cty.Type{
   278  				"foo": cty.Object(
   279  					map[string]cty.Type{
   280  						"baz": cty.Map(cty.String),
   281  					},
   282  				),
   283  				"bar": cty.Set(cty.String),
   284  			}),
   285  		},
   286  		{
   287  			"partial flatmap with nested collections",
   288  			map[string]string{
   289  				"foo.baz.%":   "1",
   290  				"foo.baz.key": "val",
   291  			},
   292  			cty.Object(map[string]cty.Type{
   293  				"foo": cty.Object(
   294  					map[string]cty.Type{
   295  						"baz": cty.Map(cty.String),
   296  						"biz": cty.Map(cty.String),
   297  					},
   298  				),
   299  				"bar": cty.Set(cty.String),
   300  			}),
   301  		},
   302  	}
   303  
   304  	for _, test := range tests {
   305  		t.Run(test.Name, func(t *testing.T) {
   306  			val, err := HCL2ValueFromFlatmap(test.Map, test.Type)
   307  			if err != nil {
   308  				t.Fatal(err)
   309  			}
   310  
   311  			got := FlatmapValueFromHCL2(val)
   312  
   313  			for _, problem := range deep.Equal(got, test.Map) {
   314  				t.Error(problem)
   315  			}
   316  		})
   317  	}
   318  }
   319  func TestHCL2ValueFromFlatmap(t *testing.T) {
   320  	tests := []struct {
   321  		Flatmap map[string]string
   322  		Type    cty.Type
   323  		Want    cty.Value
   324  		WantErr string
   325  	}{
   326  		{
   327  			Flatmap: map[string]string{},
   328  			Type:    cty.EmptyObject,
   329  			Want:    cty.EmptyObjectVal,
   330  		},
   331  		{
   332  			Flatmap: map[string]string{
   333  				"ignored": "foo",
   334  			},
   335  			Type: cty.EmptyObject,
   336  			Want: cty.EmptyObjectVal,
   337  		},
   338  		{
   339  			Flatmap: map[string]string{
   340  				"foo": "blah",
   341  				"bar": "true",
   342  				"baz": "12.5",
   343  				"unk": UnknownVariableValue,
   344  			},
   345  			Type: cty.Object(map[string]cty.Type{
   346  				"foo": cty.String,
   347  				"bar": cty.Bool,
   348  				"baz": cty.Number,
   349  				"unk": cty.Bool,
   350  			}),
   351  			Want: cty.ObjectVal(map[string]cty.Value{
   352  				"foo": cty.StringVal("blah"),
   353  				"bar": cty.True,
   354  				"baz": cty.NumberFloatVal(12.5),
   355  				"unk": cty.UnknownVal(cty.Bool),
   356  			}),
   357  		},
   358  		{
   359  			Flatmap: map[string]string{
   360  				"foo.#": "0",
   361  			},
   362  			Type: cty.Object(map[string]cty.Type{
   363  				"foo": cty.List(cty.String),
   364  			}),
   365  			Want: cty.ObjectVal(map[string]cty.Value{
   366  				"foo": cty.ListValEmpty(cty.String),
   367  			}),
   368  		},
   369  		{
   370  			Flatmap: map[string]string{
   371  				"foo.#": UnknownVariableValue,
   372  			},
   373  			Type: cty.Object(map[string]cty.Type{
   374  				"foo": cty.List(cty.String),
   375  			}),
   376  			Want: cty.ObjectVal(map[string]cty.Value{
   377  				"foo": cty.UnknownVal(cty.List(cty.String)),
   378  			}),
   379  		},
   380  		{
   381  			Flatmap: map[string]string{
   382  				"foo.#": "1",
   383  				"foo.0": "hello",
   384  			},
   385  			Type: cty.Object(map[string]cty.Type{
   386  				"foo": cty.List(cty.String),
   387  			}),
   388  			Want: cty.ObjectVal(map[string]cty.Value{
   389  				"foo": cty.ListVal([]cty.Value{
   390  					cty.StringVal("hello"),
   391  				}),
   392  			}),
   393  		},
   394  		{
   395  			Flatmap: map[string]string{
   396  				"foo.#": "2",
   397  				"foo.0": "true",
   398  				"foo.1": "false",
   399  				"foo.2": "ignored", // (because the count is 2, so this is out of range)
   400  			},
   401  			Type: cty.Object(map[string]cty.Type{
   402  				"foo": cty.List(cty.Bool),
   403  			}),
   404  			Want: cty.ObjectVal(map[string]cty.Value{
   405  				"foo": cty.ListVal([]cty.Value{
   406  					cty.True,
   407  					cty.False,
   408  				}),
   409  			}),
   410  		},
   411  		{
   412  			Flatmap: map[string]string{
   413  				"foo.#": "2",
   414  				"foo.0": "hello",
   415  			},
   416  			Type: cty.Object(map[string]cty.Type{
   417  				"foo": cty.Tuple([]cty.Type{
   418  					cty.String,
   419  					cty.Bool,
   420  				}),
   421  			}),
   422  			Want: cty.ObjectVal(map[string]cty.Value{
   423  				"foo": cty.TupleVal([]cty.Value{
   424  					cty.StringVal("hello"),
   425  					cty.NullVal(cty.Bool),
   426  				}),
   427  			}),
   428  		},
   429  		{
   430  			Flatmap: map[string]string{
   431  				"foo.#": UnknownVariableValue,
   432  			},
   433  			Type: cty.Object(map[string]cty.Type{
   434  				"foo": cty.Tuple([]cty.Type{
   435  					cty.String,
   436  					cty.Bool,
   437  				}),
   438  			}),
   439  			Want: cty.ObjectVal(map[string]cty.Value{
   440  				"foo": cty.UnknownVal(cty.Tuple([]cty.Type{
   441  					cty.String,
   442  					cty.Bool,
   443  				})),
   444  			}),
   445  		},
   446  		{
   447  			Flatmap: map[string]string{
   448  				"foo.#": "0",
   449  			},
   450  			Type: cty.Object(map[string]cty.Type{
   451  				"foo": cty.Set(cty.String),
   452  			}),
   453  			Want: cty.ObjectVal(map[string]cty.Value{
   454  				"foo": cty.SetValEmpty(cty.String),
   455  			}),
   456  		},
   457  		{
   458  			Flatmap: map[string]string{
   459  				"foo.#": UnknownVariableValue,
   460  			},
   461  			Type: cty.Object(map[string]cty.Type{
   462  				"foo": cty.Set(cty.String),
   463  			}),
   464  			Want: cty.ObjectVal(map[string]cty.Value{
   465  				"foo": cty.UnknownVal(cty.Set(cty.String)),
   466  			}),
   467  		},
   468  		{
   469  			Flatmap: map[string]string{
   470  				"foo.#":        "1",
   471  				"foo.24534534": "hello",
   472  			},
   473  			Type: cty.Object(map[string]cty.Type{
   474  				"foo": cty.Set(cty.String),
   475  			}),
   476  			Want: cty.ObjectVal(map[string]cty.Value{
   477  				"foo": cty.SetVal([]cty.Value{
   478  					cty.StringVal("hello"),
   479  				}),
   480  			}),
   481  		},
   482  		{
   483  			Flatmap: map[string]string{
   484  				"foo.#":        "1",
   485  				"foo.24534534": "true",
   486  				"foo.95645644": "true",
   487  				"foo.34533452": "false",
   488  			},
   489  			Type: cty.Object(map[string]cty.Type{
   490  				"foo": cty.Set(cty.Bool),
   491  			}),
   492  			Want: cty.ObjectVal(map[string]cty.Value{
   493  				"foo": cty.SetVal([]cty.Value{
   494  					cty.True,
   495  					cty.False,
   496  				}),
   497  			}),
   498  		},
   499  		{
   500  			Flatmap: map[string]string{
   501  				"foo.%": "0",
   502  			},
   503  			Type: cty.Object(map[string]cty.Type{
   504  				"foo": cty.Map(cty.String),
   505  			}),
   506  			Want: cty.ObjectVal(map[string]cty.Value{
   507  				"foo": cty.MapValEmpty(cty.String),
   508  			}),
   509  		},
   510  		{
   511  			Flatmap: map[string]string{
   512  				"foo.%":       "2",
   513  				"foo.baz":     "true",
   514  				"foo.bar.baz": "false",
   515  			},
   516  			Type: cty.Object(map[string]cty.Type{
   517  				"foo": cty.Map(cty.Bool),
   518  			}),
   519  			Want: cty.ObjectVal(map[string]cty.Value{
   520  				"foo": cty.MapVal(map[string]cty.Value{
   521  					"baz":     cty.True,
   522  					"bar.baz": cty.False,
   523  				}),
   524  			}),
   525  		},
   526  		{
   527  			Flatmap: map[string]string{
   528  				"foo.%": UnknownVariableValue,
   529  			},
   530  			Type: cty.Object(map[string]cty.Type{
   531  				"foo": cty.Map(cty.Bool),
   532  			}),
   533  			Want: cty.ObjectVal(map[string]cty.Value{
   534  				"foo": cty.UnknownVal(cty.Map(cty.Bool)),
   535  			}),
   536  		},
   537  		{
   538  			Flatmap: map[string]string{
   539  				"foo.#":     "2",
   540  				"foo.0.bar": "hello",
   541  				"foo.0.baz": "1",
   542  				"foo.1.bar": "world",
   543  				"foo.1.baz": "false",
   544  			},
   545  			Type: cty.Object(map[string]cty.Type{
   546  				"foo": cty.List(cty.Object(map[string]cty.Type{
   547  					"bar": cty.String,
   548  					"baz": cty.Bool,
   549  				})),
   550  			}),
   551  			Want: cty.ObjectVal(map[string]cty.Value{
   552  				"foo": cty.ListVal([]cty.Value{
   553  					cty.ObjectVal(map[string]cty.Value{
   554  						"bar": cty.StringVal("hello"),
   555  						"baz": cty.True,
   556  					}),
   557  					cty.ObjectVal(map[string]cty.Value{
   558  						"bar": cty.StringVal("world"),
   559  						"baz": cty.False,
   560  					}),
   561  				}),
   562  			}),
   563  		},
   564  		{
   565  			Flatmap: map[string]string{
   566  				"foo.#":            "2",
   567  				"foo.34534534.bar": "hello",
   568  				"foo.34534534.baz": "1",
   569  				"foo.93453345.bar": "world",
   570  				"foo.93453345.baz": "false",
   571  			},
   572  			Type: cty.Object(map[string]cty.Type{
   573  				"foo": cty.Set(cty.Object(map[string]cty.Type{
   574  					"bar": cty.String,
   575  					"baz": cty.Bool,
   576  				})),
   577  			}),
   578  			Want: cty.ObjectVal(map[string]cty.Value{
   579  				"foo": cty.SetVal([]cty.Value{
   580  					cty.ObjectVal(map[string]cty.Value{
   581  						"bar": cty.StringVal("hello"),
   582  						"baz": cty.True,
   583  					}),
   584  					cty.ObjectVal(map[string]cty.Value{
   585  						"bar": cty.StringVal("world"),
   586  						"baz": cty.False,
   587  					}),
   588  				}),
   589  			}),
   590  		},
   591  		{
   592  			Flatmap: map[string]string{
   593  				"foo.#": "not-valid",
   594  			},
   595  			Type: cty.Object(map[string]cty.Type{
   596  				"foo": cty.List(cty.String),
   597  			}),
   598  			WantErr: `invalid count value for "foo." in state: strconv.Atoi: parsing "not-valid": invalid syntax`,
   599  		},
   600  		{
   601  			Flatmap: nil,
   602  			Type: cty.Object(map[string]cty.Type{
   603  				"foo": cty.Set(cty.Object(map[string]cty.Type{
   604  					"bar": cty.String,
   605  				})),
   606  			}),
   607  			Want: cty.NullVal(cty.Object(map[string]cty.Type{
   608  				"foo": cty.Set(cty.Object(map[string]cty.Type{
   609  					"bar": cty.String,
   610  				})),
   611  			})),
   612  		},
   613  		{
   614  			Flatmap: map[string]string{
   615  				"foo.#":   "2",
   616  				"foo.0.%": "2",
   617  				"foo.0.a": "a",
   618  				"foo.0.b": "b",
   619  				"foo.1.%": "1",
   620  				"foo.1.a": "a",
   621  			},
   622  			Type: cty.Object(map[string]cty.Type{
   623  				"foo": cty.List(cty.Map(cty.String)),
   624  			}),
   625  
   626  			Want: cty.ObjectVal(map[string]cty.Value{
   627  				"foo": cty.ListVal([]cty.Value{
   628  					cty.MapVal(map[string]cty.Value{
   629  						"a": cty.StringVal("a"),
   630  						"b": cty.StringVal("b"),
   631  					}),
   632  					cty.MapVal(map[string]cty.Value{
   633  						"a": cty.StringVal("a"),
   634  					}),
   635  				}),
   636  			}),
   637  		},
   638  		{
   639  			Flatmap: map[string]string{
   640  				"single.#":                 "1",
   641  				"single.~1.value":          "a",
   642  				"single.~1.optional":       UnknownVariableValue,
   643  				"two.#":                    "2",
   644  				"two.~2381914684.value":    "a",
   645  				"two.~2381914684.optional": UnknownVariableValue,
   646  				"two.~2798940671.value":    "b",
   647  				"two.~2798940671.optional": UnknownVariableValue,
   648  			},
   649  			Type: cty.Object(map[string]cty.Type{
   650  				"single": cty.Set(
   651  					cty.Object(map[string]cty.Type{
   652  						"value":    cty.String,
   653  						"optional": cty.String,
   654  					}),
   655  				),
   656  				"two": cty.Set(
   657  					cty.Object(map[string]cty.Type{
   658  						"optional": cty.String,
   659  						"value":    cty.String,
   660  					}),
   661  				),
   662  			}),
   663  			Want: cty.ObjectVal(map[string]cty.Value{
   664  				"single": cty.SetVal([]cty.Value{
   665  					cty.ObjectVal(map[string]cty.Value{
   666  						"value":    cty.StringVal("a"),
   667  						"optional": cty.UnknownVal(cty.String),
   668  					}),
   669  				}),
   670  				"two": cty.SetVal([]cty.Value{
   671  					cty.ObjectVal(map[string]cty.Value{
   672  						"value":    cty.StringVal("a"),
   673  						"optional": cty.UnknownVal(cty.String),
   674  					}),
   675  					cty.ObjectVal(map[string]cty.Value{
   676  						"value":    cty.StringVal("b"),
   677  						"optional": cty.UnknownVal(cty.String),
   678  					}),
   679  				}),
   680  			}),
   681  		},
   682  		{
   683  			Flatmap: map[string]string{
   684  				"foo.#": "1",
   685  			},
   686  			Type: cty.Object(map[string]cty.Type{
   687  				"foo": cty.Set(cty.Object(map[string]cty.Type{
   688  					"bar": cty.String,
   689  				})),
   690  			}),
   691  			Want: cty.ObjectVal(map[string]cty.Value{
   692  				"foo": cty.SetVal([]cty.Value{
   693  					cty.ObjectVal(map[string]cty.Value{
   694  						"bar": cty.NullVal(cty.String),
   695  					}),
   696  				}),
   697  			}),
   698  		},
   699  		{
   700  			Flatmap: map[string]string{
   701  				"multi.#":                "1",
   702  				"multi.2.set.#":          "1",
   703  				"multi.2.set.3.required": "val",
   704  			},
   705  			Type: cty.Object(map[string]cty.Type{
   706  				"multi": cty.Set(cty.Object(map[string]cty.Type{
   707  					"set": cty.Set(cty.Object(map[string]cty.Type{
   708  						"required": cty.String,
   709  					})),
   710  				})),
   711  			}),
   712  			Want: cty.ObjectVal(map[string]cty.Value{
   713  				"multi": cty.SetVal([]cty.Value{
   714  					cty.ObjectVal(map[string]cty.Value{
   715  						"set": cty.SetVal([]cty.Value{
   716  							cty.ObjectVal(map[string]cty.Value{
   717  								"required": cty.StringVal("val"),
   718  							}),
   719  						}),
   720  					}),
   721  				}),
   722  			}),
   723  		},
   724  	}
   725  
   726  	for i, test := range tests {
   727  		t.Run(fmt.Sprintf("%d %#v as %#v", i, test.Flatmap, test.Type), func(t *testing.T) {
   728  			got, err := HCL2ValueFromFlatmap(test.Flatmap, test.Type)
   729  
   730  			if test.WantErr != "" {
   731  				if err == nil {
   732  					t.Fatalf("succeeded; want error: %s", test.WantErr)
   733  				}
   734  				if got, want := err.Error(), test.WantErr; got != want {
   735  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
   736  				}
   737  				if got == cty.NilVal {
   738  					t.Fatalf("result is cty.NilVal; want valid placeholder value")
   739  				}
   740  				return
   741  			} else {
   742  				if err != nil {
   743  					t.Fatalf("unexpected error: %s", err.Error())
   744  				}
   745  			}
   746  
   747  			if !got.RawEquals(test.Want) {
   748  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   749  			}
   750  		})
   751  	}
   752  }