github.com/kcburge/terraform@v0.11.12-beta1/config/configschema/decoder_spec_test.go (about)

     1  package configschema
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/davecgh/go-spew/spew"
     7  
     8  	"github.com/hashicorp/hcl2/hcl"
     9  	"github.com/hashicorp/hcl2/hcldec"
    10  	"github.com/hashicorp/hcl2/hcltest"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestBlockDecoderSpec(t *testing.T) {
    15  	tests := map[string]struct {
    16  		Schema    *Block
    17  		TestBody  hcl.Body
    18  		Want      cty.Value
    19  		DiagCount int
    20  	}{
    21  		"empty": {
    22  			&Block{},
    23  			hcl.EmptyBody(),
    24  			cty.EmptyObjectVal,
    25  			0,
    26  		},
    27  		"nil": {
    28  			nil,
    29  			hcl.EmptyBody(),
    30  			cty.EmptyObjectVal,
    31  			0,
    32  		},
    33  		"attributes": {
    34  			&Block{
    35  				Attributes: map[string]*Attribute{
    36  					"optional": {
    37  						Type:     cty.Number,
    38  						Optional: true,
    39  					},
    40  					"required": {
    41  						Type:     cty.String,
    42  						Required: true,
    43  					},
    44  					"computed": {
    45  						Type:     cty.List(cty.Bool),
    46  						Computed: true,
    47  					},
    48  					"optional_computed": {
    49  						Type:     cty.Map(cty.Bool),
    50  						Optional: true,
    51  						Computed: true,
    52  					},
    53  					"optional_computed_overridden": {
    54  						Type:     cty.Bool,
    55  						Optional: true,
    56  						Computed: true,
    57  					},
    58  				},
    59  			},
    60  			hcltest.MockBody(&hcl.BodyContent{
    61  				Attributes: hcl.Attributes{
    62  					"required": {
    63  						Name: "required",
    64  						Expr: hcltest.MockExprLiteral(cty.NumberIntVal(5)),
    65  					},
    66  					"optional_computed_overridden": {
    67  						Name: "optional_computed_overridden",
    68  						Expr: hcltest.MockExprLiteral(cty.True),
    69  					},
    70  				},
    71  			}),
    72  			cty.ObjectVal(map[string]cty.Value{
    73  				"optional":                     cty.NullVal(cty.Number),
    74  				"required":                     cty.StringVal("5"), // converted from number to string
    75  				"computed":                     cty.UnknownVal(cty.List(cty.Bool)),
    76  				"optional_computed":            cty.UnknownVal(cty.Map(cty.Bool)),
    77  				"optional_computed_overridden": cty.True,
    78  			}),
    79  			0,
    80  		},
    81  		"dynamically-typed attribute": {
    82  			&Block{
    83  				Attributes: map[string]*Attribute{
    84  					"foo": {
    85  						Type:     cty.DynamicPseudoType, // any type is permitted
    86  						Required: true,
    87  					},
    88  				},
    89  			},
    90  			hcltest.MockBody(&hcl.BodyContent{
    91  				Attributes: hcl.Attributes{
    92  					"foo": {
    93  						Name: "foo",
    94  						Expr: hcltest.MockExprLiteral(cty.True),
    95  					},
    96  				},
    97  			}),
    98  			cty.ObjectVal(map[string]cty.Value{
    99  				"foo": cty.True,
   100  			}),
   101  			0,
   102  		},
   103  		"dynamically-typed attribute omitted": {
   104  			&Block{
   105  				Attributes: map[string]*Attribute{
   106  					"foo": {
   107  						Type:     cty.DynamicPseudoType, // any type is permitted
   108  						Optional: true,
   109  					},
   110  				},
   111  			},
   112  			hcltest.MockBody(&hcl.BodyContent{}),
   113  			cty.ObjectVal(map[string]cty.Value{
   114  				"foo": cty.NullVal(cty.DynamicPseudoType),
   115  			}),
   116  			0,
   117  		},
   118  		"required attribute omitted": {
   119  			&Block{
   120  				Attributes: map[string]*Attribute{
   121  					"foo": {
   122  						Type:     cty.Bool,
   123  						Required: true,
   124  					},
   125  				},
   126  			},
   127  			hcltest.MockBody(&hcl.BodyContent{}),
   128  			cty.ObjectVal(map[string]cty.Value{
   129  				"foo": cty.NullVal(cty.Bool),
   130  			}),
   131  			1, // missing required attribute
   132  		},
   133  		"wrong attribute type": {
   134  			&Block{
   135  				Attributes: map[string]*Attribute{
   136  					"optional": {
   137  						Type:     cty.Number,
   138  						Optional: true,
   139  					},
   140  				},
   141  			},
   142  			hcltest.MockBody(&hcl.BodyContent{
   143  				Attributes: hcl.Attributes{
   144  					"optional": {
   145  						Name: "optional",
   146  						Expr: hcltest.MockExprLiteral(cty.True),
   147  					},
   148  				},
   149  			}),
   150  			cty.ObjectVal(map[string]cty.Value{
   151  				"optional": cty.UnknownVal(cty.Number),
   152  			}),
   153  			1, // incorrect type; number required
   154  		},
   155  		"blocks": {
   156  			&Block{
   157  				BlockTypes: map[string]*NestedBlock{
   158  					"single": {
   159  						Nesting: NestingSingle,
   160  						Block:   Block{},
   161  					},
   162  					"list": {
   163  						Nesting: NestingList,
   164  						Block:   Block{},
   165  					},
   166  					"set": {
   167  						Nesting: NestingSet,
   168  						Block:   Block{},
   169  					},
   170  					"map": {
   171  						Nesting: NestingMap,
   172  						Block:   Block{},
   173  					},
   174  				},
   175  			},
   176  			hcltest.MockBody(&hcl.BodyContent{
   177  				Blocks: hcl.Blocks{
   178  					&hcl.Block{
   179  						Type: "list",
   180  						Body: hcl.EmptyBody(),
   181  					},
   182  					&hcl.Block{
   183  						Type: "single",
   184  						Body: hcl.EmptyBody(),
   185  					},
   186  					&hcl.Block{
   187  						Type: "list",
   188  						Body: hcl.EmptyBody(),
   189  					},
   190  					&hcl.Block{
   191  						Type: "set",
   192  						Body: hcl.EmptyBody(),
   193  					},
   194  					&hcl.Block{
   195  						Type:        "map",
   196  						Labels:      []string{"foo"},
   197  						LabelRanges: []hcl.Range{hcl.Range{}},
   198  						Body:        hcl.EmptyBody(),
   199  					},
   200  					&hcl.Block{
   201  						Type:        "map",
   202  						Labels:      []string{"bar"},
   203  						LabelRanges: []hcl.Range{hcl.Range{}},
   204  						Body:        hcl.EmptyBody(),
   205  					},
   206  					&hcl.Block{
   207  						Type: "set",
   208  						Body: hcl.EmptyBody(),
   209  					},
   210  				},
   211  			}),
   212  			cty.ObjectVal(map[string]cty.Value{
   213  				"single": cty.EmptyObjectVal,
   214  				"list": cty.ListVal([]cty.Value{
   215  					cty.EmptyObjectVal,
   216  					cty.EmptyObjectVal,
   217  				}),
   218  				"set": cty.SetVal([]cty.Value{
   219  					cty.EmptyObjectVal,
   220  					cty.EmptyObjectVal,
   221  				}),
   222  				"map": cty.MapVal(map[string]cty.Value{
   223  					"foo": cty.EmptyObjectVal,
   224  					"bar": cty.EmptyObjectVal,
   225  				}),
   226  			}),
   227  			0,
   228  		},
   229  		"too many list items": {
   230  			&Block{
   231  				BlockTypes: map[string]*NestedBlock{
   232  					"foo": {
   233  						Nesting:  NestingList,
   234  						Block:    Block{},
   235  						MaxItems: 1,
   236  					},
   237  				},
   238  			},
   239  			hcltest.MockBody(&hcl.BodyContent{
   240  				Blocks: hcl.Blocks{
   241  					&hcl.Block{
   242  						Type: "foo",
   243  						Body: hcl.EmptyBody(),
   244  					},
   245  					&hcl.Block{
   246  						Type: "foo",
   247  						Body: hcl.EmptyBody(),
   248  					},
   249  				},
   250  			}),
   251  			cty.ObjectVal(map[string]cty.Value{
   252  				"foo": cty.ListVal([]cty.Value{
   253  					cty.EmptyObjectVal,
   254  					cty.EmptyObjectVal,
   255  				}),
   256  			}),
   257  			1, // too many "foo" blocks
   258  		},
   259  		"extraneous attribute": {
   260  			&Block{},
   261  			hcltest.MockBody(&hcl.BodyContent{
   262  				Attributes: hcl.Attributes{
   263  					"extra": {
   264  						Name: "extra",
   265  						Expr: hcltest.MockExprLiteral(cty.StringVal("hello")),
   266  					},
   267  				},
   268  			}),
   269  			cty.EmptyObjectVal,
   270  			1, // extraneous attribute
   271  		},
   272  	}
   273  
   274  	for name, test := range tests {
   275  		t.Run(name, func(t *testing.T) {
   276  			spec := test.Schema.DecoderSpec()
   277  			got, diags := hcldec.Decode(test.TestBody, spec, nil)
   278  			if len(diags) != test.DiagCount {
   279  				t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
   280  				for _, diag := range diags {
   281  					t.Logf("- %s", diag.Error())
   282  				}
   283  			}
   284  
   285  			if !got.RawEquals(test.Want) {
   286  				t.Logf("[INFO] implied schema is %s", spew.Sdump(hcldec.ImpliedSchema(spec)))
   287  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   288  			}
   289  
   290  			// Double-check that we're producing consistent results for DecoderSpec
   291  			// and ImpliedType.
   292  			impliedType := test.Schema.ImpliedType()
   293  			if errs := got.Type().TestConformance(impliedType); len(errs) != 0 {
   294  				t.Errorf("result does not conform to the schema's implied type")
   295  				for _, err := range errs {
   296  					t.Logf("- %s", err.Error())
   297  				}
   298  			}
   299  		})
   300  	}
   301  }