github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/configs/hcl2shim/flatmap_test.go (about)

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