github.com/hashicorp/hcl/v2@v2.20.0/hcldec/public_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hcldec
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/hashicorp/hcl/v2/hclsyntax"
    13  	"github.com/zclconf/go-cty/cty"
    14  )
    15  
    16  func TestDecode(t *testing.T) {
    17  	tests := []struct {
    18  		config    string
    19  		spec      Spec
    20  		ctx       *hcl.EvalContext
    21  		want      cty.Value
    22  		diagCount int
    23  	}{
    24  		{
    25  			``,
    26  			&ObjectSpec{},
    27  			nil,
    28  			cty.EmptyObjectVal,
    29  			0,
    30  		},
    31  		{
    32  			"a = 1\n",
    33  			&ObjectSpec{},
    34  			nil,
    35  			cty.EmptyObjectVal,
    36  			1, // attribute named "a" is not expected here
    37  		},
    38  		{
    39  			"a = 1\n",
    40  			&ObjectSpec{
    41  				"a": &AttrSpec{
    42  					Name: "a",
    43  					Type: cty.Number,
    44  				},
    45  			},
    46  			nil,
    47  			cty.ObjectVal(map[string]cty.Value{
    48  				"a": cty.NumberIntVal(1),
    49  			}),
    50  			0,
    51  		},
    52  		{
    53  			"a = 1\n",
    54  			&AttrSpec{
    55  				Name: "a",
    56  				Type: cty.Number,
    57  			},
    58  			nil,
    59  			cty.NumberIntVal(1),
    60  			0,
    61  		},
    62  		{
    63  			"a = 1\n",
    64  			&DefaultSpec{
    65  				Primary: &AttrSpec{
    66  					Name: "a",
    67  					Type: cty.Number,
    68  				},
    69  				Default: &LiteralSpec{
    70  					Value: cty.NumberIntVal(10),
    71  				},
    72  			},
    73  			nil,
    74  			cty.NumberIntVal(1),
    75  			0,
    76  		},
    77  		{
    78  			"",
    79  			&DefaultSpec{
    80  				Primary: &AttrSpec{
    81  					Name: "a",
    82  					Type: cty.Number,
    83  				},
    84  				Default: &LiteralSpec{
    85  					Value: cty.NumberIntVal(10),
    86  				},
    87  			},
    88  			nil,
    89  			cty.NumberIntVal(10),
    90  			0,
    91  		},
    92  		{
    93  			"a = 1\n",
    94  			ObjectSpec{
    95  				"foo": &DefaultSpec{
    96  					Primary: &AttrSpec{
    97  						Name: "a",
    98  						Type: cty.Number,
    99  					},
   100  					Default: &LiteralSpec{
   101  						Value: cty.NumberIntVal(10),
   102  					},
   103  				},
   104  			},
   105  			nil,
   106  			cty.ObjectVal(map[string]cty.Value{"foo": cty.NumberIntVal(1)}),
   107  			0,
   108  		},
   109  		{
   110  			"a = \"1\"\n",
   111  			&AttrSpec{
   112  				Name: "a",
   113  				Type: cty.Number,
   114  			},
   115  			nil,
   116  			cty.NumberIntVal(1),
   117  			0,
   118  		},
   119  		{
   120  			"a = true\n",
   121  			&AttrSpec{
   122  				Name: "a",
   123  				Type: cty.Number,
   124  			},
   125  			nil,
   126  			cty.UnknownVal(cty.Number),
   127  			1, // incorrect type - number required.
   128  		},
   129  		{
   130  			``,
   131  			&AttrSpec{
   132  				Name:     "a",
   133  				Type:     cty.Number,
   134  				Required: true,
   135  			},
   136  			nil,
   137  			cty.NullVal(cty.Number),
   138  			1, // attribute "a" is required
   139  		},
   140  		{
   141  			``,
   142  			&AttrSpec{
   143  				Name: "a",
   144  				Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{
   145  					"attr": cty.String,
   146  				}, []string{"attr"}),
   147  			},
   148  			nil,
   149  			cty.NullVal(cty.Object(map[string]cty.Type{
   150  				"attr": cty.String,
   151  			})),
   152  			0,
   153  		},
   154  
   155  		{
   156  			`
   157  b {
   158  }
   159  `,
   160  			&BlockSpec{
   161  				TypeName: "b",
   162  				Nested:   ObjectSpec{},
   163  			},
   164  			nil,
   165  			cty.EmptyObjectVal,
   166  			0,
   167  		},
   168  		{
   169  			`
   170  b "baz" {
   171  }
   172  `,
   173  			&BlockSpec{
   174  				TypeName: "b",
   175  				Nested: &BlockLabelSpec{
   176  					Index: 0,
   177  					Name:  "name",
   178  				},
   179  			},
   180  			nil,
   181  			cty.StringVal("baz"),
   182  			0,
   183  		},
   184  		{
   185  			`
   186  b "baz" {}
   187  b "foo" {}
   188  `,
   189  			&BlockSpec{
   190  				TypeName: "b",
   191  				Nested: &BlockLabelSpec{
   192  					Index: 0,
   193  					Name:  "name",
   194  				},
   195  			},
   196  			nil,
   197  			cty.StringVal("baz"),
   198  			1, // duplicate "b" block
   199  		},
   200  		{
   201  			`
   202  b {
   203  }
   204  `,
   205  			&BlockSpec{
   206  				TypeName: "b",
   207  				Nested: &BlockLabelSpec{
   208  					Index: 0,
   209  					Name:  "name",
   210  				},
   211  			},
   212  			nil,
   213  			cty.NullVal(cty.String),
   214  			1, // missing name label
   215  		},
   216  		{
   217  			``,
   218  			&BlockSpec{
   219  				TypeName: "b",
   220  				Nested:   ObjectSpec{},
   221  			},
   222  			nil,
   223  			cty.NullVal(cty.EmptyObject),
   224  			0,
   225  		},
   226  		{
   227  			"a {}\n",
   228  			&BlockSpec{
   229  				TypeName: "b",
   230  				Nested:   ObjectSpec{},
   231  			},
   232  			nil,
   233  			cty.NullVal(cty.EmptyObject),
   234  			1, // blocks of type "a" are not supported
   235  		},
   236  		{
   237  			``,
   238  			&BlockSpec{
   239  				TypeName: "b",
   240  				Nested:   ObjectSpec{},
   241  				Required: true,
   242  			},
   243  			nil,
   244  			cty.NullVal(cty.EmptyObject),
   245  			1, // a block of type "b" is required
   246  		},
   247  		{
   248  			`
   249  b {}
   250  b {}
   251  `,
   252  			&BlockSpec{
   253  				TypeName: "b",
   254  				Nested:   ObjectSpec{},
   255  				Required: true,
   256  			},
   257  			nil,
   258  			cty.EmptyObjectVal,
   259  			1, // only one "b" block is allowed
   260  		},
   261  		{
   262  			`
   263  b {
   264  }
   265  `,
   266  			&BlockAttrsSpec{
   267  				TypeName:    "b",
   268  				ElementType: cty.String,
   269  			},
   270  			nil,
   271  			cty.MapValEmpty(cty.String),
   272  			0,
   273  		},
   274  		{
   275  			`
   276  b {
   277    hello = "world"
   278  }
   279  `,
   280  			&BlockAttrsSpec{
   281  				TypeName:    "b",
   282  				ElementType: cty.String,
   283  			},
   284  			nil,
   285  			cty.MapVal(map[string]cty.Value{
   286  				"hello": cty.StringVal("world"),
   287  			}),
   288  			0,
   289  		},
   290  		{
   291  			`
   292  b {
   293    hello = true
   294  }
   295  `,
   296  			&BlockAttrsSpec{
   297  				TypeName:    "b",
   298  				ElementType: cty.String,
   299  			},
   300  			nil,
   301  			cty.MapVal(map[string]cty.Value{
   302  				"hello": cty.StringVal("true"),
   303  			}),
   304  			0,
   305  		},
   306  		{
   307  			`
   308  b {
   309    hello   = true
   310    goodbye = 5
   311  }
   312  `,
   313  			&BlockAttrsSpec{
   314  				TypeName:    "b",
   315  				ElementType: cty.String,
   316  			},
   317  			nil,
   318  			cty.MapVal(map[string]cty.Value{
   319  				"hello":   cty.StringVal("true"),
   320  				"goodbye": cty.StringVal("5"),
   321  			}),
   322  			0,
   323  		},
   324  		{
   325  			``,
   326  			&BlockAttrsSpec{
   327  				TypeName:    "b",
   328  				ElementType: cty.String,
   329  			},
   330  			nil,
   331  			cty.NullVal(cty.Map(cty.String)),
   332  			0,
   333  		},
   334  		{
   335  			``,
   336  			&BlockAttrsSpec{
   337  				TypeName:    "b",
   338  				ElementType: cty.String,
   339  				Required:    true,
   340  			},
   341  			nil,
   342  			cty.NullVal(cty.Map(cty.String)),
   343  			1, // missing b block
   344  		},
   345  		{
   346  			``,
   347  			&BlockAttrsSpec{
   348  				TypeName: "b",
   349  				ElementType: cty.ObjectWithOptionalAttrs(map[string]cty.Type{
   350  					"attr": cty.String,
   351  				}, []string{"attr"}),
   352  			},
   353  			nil,
   354  			cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
   355  				"attr": cty.String,
   356  			}))),
   357  			0,
   358  		},
   359  		{
   360  			`
   361  b {
   362  }
   363  b {
   364  }
   365  			`,
   366  			&BlockAttrsSpec{
   367  				TypeName:    "b",
   368  				ElementType: cty.String,
   369  			},
   370  			nil,
   371  			cty.MapValEmpty(cty.String),
   372  			1, // duplicate b block
   373  		},
   374  		{
   375  			`
   376  b {
   377  }
   378  b {
   379  }
   380  			`,
   381  			&BlockAttrsSpec{
   382  				TypeName:    "b",
   383  				ElementType: cty.String,
   384  				Required:    true,
   385  			},
   386  			nil,
   387  			cty.MapValEmpty(cty.String),
   388  			1, // duplicate b block
   389  		},
   390  		{
   391  			`
   392  b {}
   393  b {}
   394  `,
   395  			&BlockListSpec{
   396  				TypeName: "b",
   397  				Nested:   ObjectSpec{},
   398  			},
   399  			nil,
   400  			cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
   401  			0,
   402  		},
   403  		{
   404  			``,
   405  			&BlockListSpec{
   406  				TypeName: "b",
   407  				Nested:   ObjectSpec{},
   408  			},
   409  			nil,
   410  			cty.ListValEmpty(cty.EmptyObject),
   411  			0,
   412  		},
   413  		{
   414  			`
   415  b "foo" {}
   416  b "bar" {}
   417  `,
   418  			&BlockListSpec{
   419  				TypeName: "b",
   420  				Nested: &BlockLabelSpec{
   421  					Name:  "name",
   422  					Index: 0,
   423  				},
   424  			},
   425  			nil,
   426  			cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
   427  			0,
   428  		},
   429  		{
   430  			`
   431  b {}
   432  b {}
   433  b {}
   434  `,
   435  			&BlockListSpec{
   436  				TypeName: "b",
   437  				Nested:   ObjectSpec{},
   438  				MaxItems: 2,
   439  			},
   440  			nil,
   441  			cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal, cty.EmptyObjectVal}),
   442  			1, // too many b blocks
   443  		},
   444  		{
   445  			`
   446  b {}
   447  b {}
   448  `,
   449  			&BlockListSpec{
   450  				TypeName: "b",
   451  				Nested:   ObjectSpec{},
   452  				MinItems: 10,
   453  			},
   454  			nil,
   455  			cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
   456  			1, // insufficient b blocks
   457  		},
   458  		{
   459  			`
   460  b {
   461  	a = true
   462  }
   463  b {
   464  	a = 1
   465  }
   466  `,
   467  			&BlockListSpec{
   468  				TypeName: "b",
   469  				Nested: &AttrSpec{
   470  					Name: "a",
   471  					Type: cty.DynamicPseudoType,
   472  				},
   473  			},
   474  			nil,
   475  			cty.DynamicVal,
   476  			1, // Unconsistent argument types in b blocks
   477  		},
   478  		{
   479  			`
   480  b {
   481  	a = true
   482  }
   483  b {
   484  	a = "not a bool"
   485  }
   486  `,
   487  			&BlockListSpec{
   488  				TypeName: "b",
   489  				Nested: &AttrSpec{
   490  					Name: "a",
   491  					Type: cty.DynamicPseudoType,
   492  				},
   493  			},
   494  			nil,
   495  			cty.ListVal([]cty.Value{
   496  				cty.StringVal("true"), // type unification generalizes all the values to strings
   497  				cty.StringVal("not a bool"),
   498  			}),
   499  			0,
   500  		},
   501  		{
   502  			`
   503  b {}
   504  b {}
   505  `,
   506  			&BlockSetSpec{
   507  				TypeName: "b",
   508  				Nested:   ObjectSpec{},
   509  				MaxItems: 2,
   510  			},
   511  			nil,
   512  			cty.SetVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
   513  			0,
   514  		},
   515  		{
   516  			`
   517  b "foo" "bar" {}
   518  b "bar" "baz" {}
   519  `,
   520  			&BlockSetSpec{
   521  				TypeName: "b",
   522  				Nested: TupleSpec{
   523  					&BlockLabelSpec{
   524  						Name:  "name",
   525  						Index: 1,
   526  					},
   527  					&BlockLabelSpec{
   528  						Name:  "type",
   529  						Index: 0,
   530  					},
   531  				},
   532  			},
   533  			nil,
   534  			cty.SetVal([]cty.Value{
   535  				cty.TupleVal([]cty.Value{cty.StringVal("bar"), cty.StringVal("foo")}),
   536  				cty.TupleVal([]cty.Value{cty.StringVal("baz"), cty.StringVal("bar")}),
   537  			}),
   538  			0,
   539  		},
   540  		{
   541  			`
   542  b {
   543  	a = true
   544  }
   545  b {
   546  	a = 1
   547  }
   548  `,
   549  			&BlockSetSpec{
   550  				TypeName: "b",
   551  				Nested: &AttrSpec{
   552  					Name: "a",
   553  					Type: cty.DynamicPseudoType,
   554  				},
   555  			},
   556  			nil,
   557  			cty.DynamicVal,
   558  			1, // Unconsistent argument types in b blocks
   559  		},
   560  		{
   561  			`
   562  b {
   563  	a = true
   564  }
   565  b {
   566  	a = "not a bool"
   567  }
   568  `,
   569  			&BlockSetSpec{
   570  				TypeName: "b",
   571  				Nested: &AttrSpec{
   572  					Name: "a",
   573  					Type: cty.DynamicPseudoType,
   574  				},
   575  			},
   576  			nil,
   577  			cty.SetVal([]cty.Value{
   578  				cty.StringVal("true"), // type unification generalizes all the values to strings
   579  				cty.StringVal("not a bool"),
   580  			}),
   581  			0,
   582  		},
   583  		{
   584  			`
   585  b "foo" {}
   586  b "bar" {}
   587  `,
   588  			&BlockMapSpec{
   589  				TypeName:   "b",
   590  				LabelNames: []string{"key"},
   591  				Nested:     ObjectSpec{},
   592  			},
   593  			nil,
   594  			cty.MapVal(map[string]cty.Value{"foo": cty.EmptyObjectVal, "bar": cty.EmptyObjectVal}),
   595  			0,
   596  		},
   597  		{
   598  			`
   599  b "foo" "bar" {}
   600  b "bar" "baz" {}
   601  `,
   602  			&BlockMapSpec{
   603  				TypeName:   "b",
   604  				LabelNames: []string{"key1", "key2"},
   605  				Nested:     ObjectSpec{},
   606  			},
   607  			nil,
   608  			cty.MapVal(map[string]cty.Value{
   609  				"foo": cty.MapVal(map[string]cty.Value{
   610  					"bar": cty.EmptyObjectVal,
   611  				}),
   612  				"bar": cty.MapVal(map[string]cty.Value{
   613  					"baz": cty.EmptyObjectVal,
   614  				}),
   615  			}),
   616  			0,
   617  		},
   618  		{
   619  			`
   620  b "foo" "bar" {}
   621  b "bar" "bar" {}
   622  `,
   623  			&BlockMapSpec{
   624  				TypeName:   "b",
   625  				LabelNames: []string{"key1", "key2"},
   626  				Nested:     ObjectSpec{},
   627  			},
   628  			nil,
   629  			cty.MapVal(map[string]cty.Value{
   630  				"foo": cty.MapVal(map[string]cty.Value{
   631  					"bar": cty.EmptyObjectVal,
   632  				}),
   633  				"bar": cty.MapVal(map[string]cty.Value{
   634  					"bar": cty.EmptyObjectVal,
   635  				}),
   636  			}),
   637  			0,
   638  		},
   639  		{
   640  			`
   641  b "foo" "bar" {}
   642  b "foo" "baz" {}
   643  `,
   644  			&BlockMapSpec{
   645  				TypeName:   "b",
   646  				LabelNames: []string{"key1", "key2"},
   647  				Nested:     ObjectSpec{},
   648  			},
   649  			nil,
   650  			cty.MapVal(map[string]cty.Value{
   651  				"foo": cty.MapVal(map[string]cty.Value{
   652  					"bar": cty.EmptyObjectVal,
   653  					"baz": cty.EmptyObjectVal,
   654  				}),
   655  			}),
   656  			0,
   657  		},
   658  		{
   659  			`
   660  b "foo" "bar" {}
   661  `,
   662  			&BlockMapSpec{
   663  				TypeName:   "b",
   664  				LabelNames: []string{"key"},
   665  				Nested:     ObjectSpec{},
   666  			},
   667  			nil,
   668  			cty.MapValEmpty(cty.EmptyObject),
   669  			1, // too many labels
   670  		},
   671  		{
   672  			`
   673  b "bar" {}
   674  `,
   675  			&BlockMapSpec{
   676  				TypeName:   "b",
   677  				LabelNames: []string{"key1", "key2"},
   678  				Nested:     ObjectSpec{},
   679  			},
   680  			nil,
   681  			cty.MapValEmpty(cty.EmptyObject),
   682  			1, // not enough labels
   683  		},
   684  		{
   685  			`
   686  b "foo" {}
   687  b "foo" {}
   688  `,
   689  			&BlockMapSpec{
   690  				TypeName:   "b",
   691  				LabelNames: []string{"key"},
   692  				Nested:     ObjectSpec{},
   693  			},
   694  			nil,
   695  			cty.MapVal(map[string]cty.Value{"foo": cty.EmptyObjectVal}),
   696  			1, // duplicate b block
   697  		},
   698  		{
   699  			`
   700  b "foo" "bar" {}
   701  b "foo" "bar" {}
   702  `,
   703  			&BlockMapSpec{
   704  				TypeName:   "b",
   705  				LabelNames: []string{"key1", "key2"},
   706  				Nested:     ObjectSpec{},
   707  			},
   708  			nil,
   709  			cty.MapVal(map[string]cty.Value{"foo": cty.MapVal(map[string]cty.Value{"bar": cty.EmptyObjectVal})}),
   710  			1, // duplicate b block
   711  		},
   712  		{
   713  			`
   714  b "foo" "bar" {}
   715  b "bar" "baz" {}
   716  `,
   717  			&BlockMapSpec{
   718  				TypeName:   "b",
   719  				LabelNames: []string{"type"},
   720  				Nested: &BlockLabelSpec{
   721  					Name:  "name",
   722  					Index: 0,
   723  				},
   724  			},
   725  			nil,
   726  			cty.MapVal(map[string]cty.Value{
   727  				"foo": cty.StringVal("bar"),
   728  				"bar": cty.StringVal("baz"),
   729  			}),
   730  			0,
   731  		},
   732  		{
   733  			`
   734  b "foo" {}
   735  `,
   736  			&BlockMapSpec{
   737  				TypeName:   "b",
   738  				LabelNames: []string{"type"},
   739  				Nested: &BlockLabelSpec{
   740  					Name:  "name",
   741  					Index: 0,
   742  				},
   743  			},
   744  			nil,
   745  			cty.MapValEmpty(cty.String),
   746  			1, // missing name
   747  		},
   748  		{
   749  			`
   750  b {}
   751  b {}
   752  `,
   753  			&BlockTupleSpec{
   754  				TypeName: "b",
   755  				Nested:   ObjectSpec{},
   756  			},
   757  			nil,
   758  			cty.TupleVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
   759  			0,
   760  		},
   761  		{
   762  			``,
   763  			&BlockTupleSpec{
   764  				TypeName: "b",
   765  				Nested:   ObjectSpec{},
   766  			},
   767  			nil,
   768  			cty.EmptyTupleVal,
   769  			0,
   770  		},
   771  		{
   772  			`
   773  b "foo" {}
   774  b "bar" {}
   775  `,
   776  			&BlockTupleSpec{
   777  				TypeName: "b",
   778  				Nested: &BlockLabelSpec{
   779  					Name:  "name",
   780  					Index: 0,
   781  				},
   782  			},
   783  			nil,
   784  			cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}),
   785  			0,
   786  		},
   787  		{
   788  			`
   789  b {}
   790  b {}
   791  b {}
   792  `,
   793  			&BlockTupleSpec{
   794  				TypeName: "b",
   795  				Nested:   ObjectSpec{},
   796  				MaxItems: 2,
   797  			},
   798  			nil,
   799  			cty.TupleVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal, cty.EmptyObjectVal}),
   800  			1, // too many b blocks
   801  		},
   802  		{
   803  			`
   804  b {}
   805  b {}
   806  `,
   807  			&BlockTupleSpec{
   808  				TypeName: "b",
   809  				Nested:   ObjectSpec{},
   810  				MinItems: 10,
   811  			},
   812  			nil,
   813  			cty.TupleVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
   814  			1, // insufficient b blocks
   815  		},
   816  		{
   817  			`
   818  b {
   819  	a = true
   820  }
   821  b {
   822  	a = 1
   823  }
   824  `,
   825  			&BlockTupleSpec{
   826  				TypeName: "b",
   827  				Nested: &AttrSpec{
   828  					Name: "a",
   829  					Type: cty.DynamicPseudoType,
   830  				},
   831  			},
   832  			nil,
   833  			cty.TupleVal([]cty.Value{
   834  				cty.True,
   835  				cty.NumberIntVal(1),
   836  			}),
   837  			0,
   838  		},
   839  		{
   840  			`
   841  b {
   842  	a = true
   843  }
   844  b {
   845  	a = "not a bool"
   846  }
   847  `,
   848  			&BlockTupleSpec{
   849  				TypeName: "b",
   850  				Nested: &AttrSpec{
   851  					Name: "a",
   852  					Type: cty.DynamicPseudoType,
   853  				},
   854  			},
   855  			nil,
   856  			cty.TupleVal([]cty.Value{
   857  				cty.True,
   858  				cty.StringVal("not a bool"),
   859  			}),
   860  			0,
   861  		},
   862  		{
   863  			`
   864  b "foo" {}
   865  b "bar" {}
   866  `,
   867  			&BlockObjectSpec{
   868  				TypeName:   "b",
   869  				LabelNames: []string{"key"},
   870  				Nested:     ObjectSpec{},
   871  			},
   872  			nil,
   873  			cty.ObjectVal(map[string]cty.Value{"foo": cty.EmptyObjectVal, "bar": cty.EmptyObjectVal}),
   874  			0,
   875  		},
   876  		{
   877  			`
   878  b "foo" "bar" {}
   879  b "bar" "baz" {}
   880  `,
   881  			&BlockObjectSpec{
   882  				TypeName:   "b",
   883  				LabelNames: []string{"key1", "key2"},
   884  				Nested:     ObjectSpec{},
   885  			},
   886  			nil,
   887  			cty.ObjectVal(map[string]cty.Value{
   888  				"foo": cty.ObjectVal(map[string]cty.Value{
   889  					"bar": cty.EmptyObjectVal,
   890  				}),
   891  				"bar": cty.ObjectVal(map[string]cty.Value{
   892  					"baz": cty.EmptyObjectVal,
   893  				}),
   894  			}),
   895  			0,
   896  		},
   897  		{
   898  			`
   899  b "foo" "bar" {}
   900  b "bar" "bar" {}
   901  `,
   902  			&BlockObjectSpec{
   903  				TypeName:   "b",
   904  				LabelNames: []string{"key1", "key2"},
   905  				Nested:     ObjectSpec{},
   906  			},
   907  			nil,
   908  			cty.ObjectVal(map[string]cty.Value{
   909  				"foo": cty.ObjectVal(map[string]cty.Value{
   910  					"bar": cty.EmptyObjectVal,
   911  				}),
   912  				"bar": cty.ObjectVal(map[string]cty.Value{
   913  					"bar": cty.EmptyObjectVal,
   914  				}),
   915  			}),
   916  			0,
   917  		},
   918  		{
   919  			`
   920  b "foo" "bar" {}
   921  b "foo" "baz" {}
   922  `,
   923  			&BlockObjectSpec{
   924  				TypeName:   "b",
   925  				LabelNames: []string{"key1", "key2"},
   926  				Nested:     ObjectSpec{},
   927  			},
   928  			nil,
   929  			cty.ObjectVal(map[string]cty.Value{
   930  				"foo": cty.ObjectVal(map[string]cty.Value{
   931  					"bar": cty.EmptyObjectVal,
   932  					"baz": cty.EmptyObjectVal,
   933  				}),
   934  			}),
   935  			0,
   936  		},
   937  		{
   938  			`
   939  b "foo" "bar" {}
   940  `,
   941  			&BlockObjectSpec{
   942  				TypeName:   "b",
   943  				LabelNames: []string{"key"},
   944  				Nested:     ObjectSpec{},
   945  			},
   946  			nil,
   947  			cty.EmptyObjectVal,
   948  			1, // too many labels
   949  		},
   950  		{
   951  			`
   952  b "bar" {}
   953  `,
   954  			&BlockObjectSpec{
   955  				TypeName:   "b",
   956  				LabelNames: []string{"key1", "key2"},
   957  				Nested:     ObjectSpec{},
   958  			},
   959  			nil,
   960  			cty.EmptyObjectVal,
   961  			1, // not enough labels
   962  		},
   963  		{
   964  			`
   965  b "foo" {}
   966  b "foo" {}
   967  `,
   968  			&BlockObjectSpec{
   969  				TypeName:   "b",
   970  				LabelNames: []string{"key"},
   971  				Nested:     ObjectSpec{},
   972  			},
   973  			nil,
   974  			cty.ObjectVal(map[string]cty.Value{"foo": cty.EmptyObjectVal}),
   975  			1, // duplicate b block
   976  		},
   977  		{
   978  			`
   979  b "foo" "bar" {}
   980  b "foo" "bar" {}
   981  `,
   982  			&BlockObjectSpec{
   983  				TypeName:   "b",
   984  				LabelNames: []string{"key1", "key2"},
   985  				Nested:     ObjectSpec{},
   986  			},
   987  			nil,
   988  			cty.ObjectVal(map[string]cty.Value{"foo": cty.ObjectVal(map[string]cty.Value{"bar": cty.EmptyObjectVal})}),
   989  			1, // duplicate b block
   990  		},
   991  		{
   992  			`
   993  b "foo" "bar" {}
   994  b "bar" "baz" {}
   995  `,
   996  			&BlockObjectSpec{
   997  				TypeName:   "b",
   998  				LabelNames: []string{"type"},
   999  				Nested: &BlockLabelSpec{
  1000  					Name:  "name",
  1001  					Index: 0,
  1002  				},
  1003  			},
  1004  			nil,
  1005  			cty.ObjectVal(map[string]cty.Value{
  1006  				"foo": cty.StringVal("bar"),
  1007  				"bar": cty.StringVal("baz"),
  1008  			}),
  1009  			0,
  1010  		},
  1011  		{
  1012  			`
  1013  b "foo" {}
  1014  `,
  1015  			&BlockObjectSpec{
  1016  				TypeName:   "b",
  1017  				LabelNames: []string{"type"},
  1018  				Nested: &BlockLabelSpec{
  1019  					Name:  "name",
  1020  					Index: 0,
  1021  				},
  1022  			},
  1023  			nil,
  1024  			cty.EmptyObjectVal,
  1025  			1, // missing name
  1026  		},
  1027  		{
  1028  			`
  1029  b "foo" {
  1030  	arg = true
  1031  }
  1032  b "bar" {
  1033  	arg = 1
  1034  }
  1035  `,
  1036  			&BlockObjectSpec{
  1037  				TypeName:   "b",
  1038  				LabelNames: []string{"type"},
  1039  				Nested: &AttrSpec{
  1040  					Name: "arg",
  1041  					Type: cty.DynamicPseudoType,
  1042  				},
  1043  			},
  1044  			nil,
  1045  			cty.ObjectVal(map[string]cty.Value{
  1046  				"foo": cty.True,
  1047  				"bar": cty.NumberIntVal(1),
  1048  			}),
  1049  			0,
  1050  		},
  1051  	}
  1052  
  1053  	for i, test := range tests {
  1054  		t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) {
  1055  			file, parseDiags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0})
  1056  			body := file.Body
  1057  			got, valDiags := Decode(body, test.spec, test.ctx)
  1058  
  1059  			var diags hcl.Diagnostics
  1060  			diags = append(diags, parseDiags...)
  1061  			diags = append(diags, valDiags...)
  1062  
  1063  			if len(diags) != test.diagCount {
  1064  				t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount)
  1065  				for _, diag := range diags {
  1066  					t.Logf(" - %s", diag.Error())
  1067  				}
  1068  			}
  1069  
  1070  			if !got.RawEquals(test.want) {
  1071  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.want)
  1072  			}
  1073  		})
  1074  	}
  1075  }
  1076  
  1077  func TestSourceRange(t *testing.T) {
  1078  	tests := []struct {
  1079  		config string
  1080  		spec   Spec
  1081  		want   hcl.Range
  1082  	}{
  1083  		{
  1084  			"a = 1\n",
  1085  			&AttrSpec{
  1086  				Name: "a",
  1087  			},
  1088  			hcl.Range{
  1089  				Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
  1090  				End:   hcl.Pos{Line: 1, Column: 6, Byte: 5},
  1091  			},
  1092  		},
  1093  		{
  1094  			`
  1095  b {
  1096    a = 1
  1097  }
  1098  `,
  1099  			&BlockSpec{
  1100  				TypeName: "b",
  1101  				Nested: &AttrSpec{
  1102  					Name: "a",
  1103  				},
  1104  			},
  1105  			hcl.Range{
  1106  				Start: hcl.Pos{Line: 3, Column: 7, Byte: 11},
  1107  				End:   hcl.Pos{Line: 3, Column: 8, Byte: 12},
  1108  			},
  1109  		},
  1110  		{
  1111  			`
  1112  b {
  1113    c {
  1114      a = 1
  1115    }
  1116  }
  1117  `,
  1118  			&BlockSpec{
  1119  				TypeName: "b",
  1120  				Nested: &BlockSpec{
  1121  					TypeName: "c",
  1122  					Nested: &AttrSpec{
  1123  						Name: "a",
  1124  					},
  1125  				},
  1126  			},
  1127  			hcl.Range{
  1128  				Start: hcl.Pos{Line: 4, Column: 9, Byte: 19},
  1129  				End:   hcl.Pos{Line: 4, Column: 10, Byte: 20},
  1130  			},
  1131  		},
  1132  	}
  1133  
  1134  	for i, test := range tests {
  1135  		t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) {
  1136  			file, diags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0})
  1137  			if len(diags) != 0 {
  1138  				t.Errorf("wrong number of diagnostics %d; want %d", len(diags), 0)
  1139  				for _, diag := range diags {
  1140  					t.Logf(" - %s", diag.Error())
  1141  				}
  1142  			}
  1143  			body := file.Body
  1144  
  1145  			got := SourceRange(body, test.spec)
  1146  
  1147  			if !reflect.DeepEqual(got, test.want) {
  1148  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.want)
  1149  			}
  1150  		})
  1151  	}
  1152  
  1153  }