github.com/hashicorp/hcl/v2@v2.20.0/hcltest/mock_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hcltest
     5  
     6  import (
     7  	"strings"
     8  	"testing"
     9  
    10  	"reflect"
    11  
    12  	"github.com/hashicorp/hcl/v2"
    13  	"github.com/zclconf/go-cty/cty"
    14  )
    15  
    16  var mockBodyIsBody hcl.Body = mockBody{}
    17  var mockExprLiteralIsExpr hcl.Expression = mockExprLiteral{}
    18  var mockExprVariableIsExpr hcl.Expression = mockExprVariable("")
    19  
    20  func TestMockBodyPartialContent(t *testing.T) {
    21  	tests := map[string]struct {
    22  		In        *hcl.BodyContent
    23  		Schema    *hcl.BodySchema
    24  		Want      *hcl.BodyContent
    25  		Remain    *hcl.BodyContent
    26  		DiagCount int
    27  	}{
    28  		"empty": {
    29  			&hcl.BodyContent{},
    30  			&hcl.BodySchema{},
    31  			&hcl.BodyContent{
    32  				Attributes: hcl.Attributes{},
    33  				Blocks:     hcl.Blocks{},
    34  			},
    35  			&hcl.BodyContent{
    36  				Attributes: hcl.Attributes{},
    37  				Blocks:     hcl.Blocks{},
    38  			},
    39  			0,
    40  		},
    41  		"attribute requested": {
    42  			&hcl.BodyContent{
    43  				Attributes: MockAttrs(map[string]hcl.Expression{
    44  					"name": MockExprLiteral(cty.StringVal("Ermintrude")),
    45  				}),
    46  			},
    47  			&hcl.BodySchema{
    48  				Attributes: []hcl.AttributeSchema{
    49  					{
    50  						Name: "name",
    51  					},
    52  				},
    53  			},
    54  			&hcl.BodyContent{
    55  				Attributes: MockAttrs(map[string]hcl.Expression{
    56  					"name": MockExprLiteral(cty.StringVal("Ermintrude")),
    57  				}),
    58  				Blocks: hcl.Blocks{},
    59  			},
    60  			&hcl.BodyContent{
    61  				Attributes: hcl.Attributes{},
    62  				Blocks:     hcl.Blocks{},
    63  			},
    64  			0,
    65  		},
    66  		"attribute remains": {
    67  			&hcl.BodyContent{
    68  				Attributes: MockAttrs(map[string]hcl.Expression{
    69  					"name": MockExprLiteral(cty.StringVal("Ermintrude")),
    70  				}),
    71  			},
    72  			&hcl.BodySchema{},
    73  			&hcl.BodyContent{
    74  				Attributes: hcl.Attributes{},
    75  				Blocks:     hcl.Blocks{},
    76  			},
    77  			&hcl.BodyContent{
    78  				Attributes: MockAttrs(map[string]hcl.Expression{
    79  					"name": MockExprLiteral(cty.StringVal("Ermintrude")),
    80  				}),
    81  				Blocks: hcl.Blocks{},
    82  			},
    83  			0,
    84  		},
    85  		"attribute missing": {
    86  			&hcl.BodyContent{
    87  				Attributes: hcl.Attributes{},
    88  			},
    89  			&hcl.BodySchema{
    90  				Attributes: []hcl.AttributeSchema{
    91  					{
    92  						Name:     "name",
    93  						Required: true,
    94  					},
    95  				},
    96  			},
    97  			&hcl.BodyContent{
    98  				Attributes: hcl.Attributes{},
    99  				Blocks:     hcl.Blocks{},
   100  			},
   101  			&hcl.BodyContent{
   102  				Attributes: hcl.Attributes{},
   103  				Blocks:     hcl.Blocks{},
   104  			},
   105  			1, // missing attribute "name"
   106  		},
   107  		"block requested, no labels": {
   108  			&hcl.BodyContent{
   109  				Blocks: hcl.Blocks{
   110  					{
   111  						Type: "baz",
   112  					},
   113  				},
   114  			},
   115  			&hcl.BodySchema{
   116  				Blocks: []hcl.BlockHeaderSchema{
   117  					{
   118  						Type: "baz",
   119  					},
   120  				},
   121  			},
   122  			&hcl.BodyContent{
   123  				Attributes: hcl.Attributes{},
   124  				Blocks: hcl.Blocks{
   125  					{
   126  						Type: "baz",
   127  					},
   128  				},
   129  			},
   130  			&hcl.BodyContent{
   131  				Attributes: hcl.Attributes{},
   132  				Blocks:     hcl.Blocks{},
   133  			},
   134  			0,
   135  		},
   136  		"block requested, wrong labels": {
   137  			&hcl.BodyContent{
   138  				Blocks: hcl.Blocks{
   139  					{
   140  						Type: "baz",
   141  					},
   142  				},
   143  			},
   144  			&hcl.BodySchema{
   145  				Blocks: []hcl.BlockHeaderSchema{
   146  					{
   147  						Type:       "baz",
   148  						LabelNames: []string{"foo"},
   149  					},
   150  				},
   151  			},
   152  			&hcl.BodyContent{
   153  				Attributes: hcl.Attributes{},
   154  				Blocks: hcl.Blocks{
   155  					{
   156  						Type: "baz",
   157  					},
   158  				},
   159  			},
   160  			&hcl.BodyContent{
   161  				Attributes: hcl.Attributes{},
   162  				Blocks:     hcl.Blocks{},
   163  			},
   164  			1, // "baz" requires 1 label
   165  		},
   166  		"block remains": {
   167  			&hcl.BodyContent{
   168  				Blocks: hcl.Blocks{
   169  					{
   170  						Type: "baz",
   171  					},
   172  				},
   173  			},
   174  			&hcl.BodySchema{},
   175  			&hcl.BodyContent{
   176  				Attributes: hcl.Attributes{},
   177  				Blocks:     hcl.Blocks{},
   178  			},
   179  			&hcl.BodyContent{
   180  				Attributes: hcl.Attributes{},
   181  				Blocks: hcl.Blocks{
   182  					{
   183  						Type: "baz",
   184  					},
   185  				},
   186  			},
   187  			0,
   188  		},
   189  		"various": {
   190  			&hcl.BodyContent{
   191  				Attributes: MockAttrs(map[string]hcl.Expression{
   192  					"name": MockExprLiteral(cty.StringVal("Ermintrude")),
   193  					"age":  MockExprLiteral(cty.NumberIntVal(32)),
   194  				}),
   195  				Blocks: hcl.Blocks{
   196  					{
   197  						Type: "baz",
   198  					},
   199  					{
   200  						Type:   "bar",
   201  						Labels: []string{"foo1"},
   202  					},
   203  					{
   204  						Type:   "bar",
   205  						Labels: []string{"foo2"},
   206  					},
   207  				},
   208  			},
   209  			&hcl.BodySchema{
   210  				Attributes: []hcl.AttributeSchema{
   211  					{
   212  						Name: "name",
   213  					},
   214  				},
   215  				Blocks: []hcl.BlockHeaderSchema{
   216  					{
   217  						Type:       "bar",
   218  						LabelNames: []string{"name"},
   219  					},
   220  				},
   221  			},
   222  			&hcl.BodyContent{
   223  				Attributes: MockAttrs(map[string]hcl.Expression{
   224  					"name": MockExprLiteral(cty.StringVal("Ermintrude")),
   225  				}),
   226  				Blocks: hcl.Blocks{
   227  					{
   228  						Type:   "bar",
   229  						Labels: []string{"foo1"},
   230  					},
   231  					{
   232  						Type:   "bar",
   233  						Labels: []string{"foo2"},
   234  					},
   235  				},
   236  			},
   237  			&hcl.BodyContent{
   238  				Attributes: MockAttrs(map[string]hcl.Expression{
   239  					"age": MockExprLiteral(cty.NumberIntVal(32)),
   240  				}),
   241  				Blocks: hcl.Blocks{
   242  					{
   243  						Type: "baz",
   244  					},
   245  				},
   246  			},
   247  			0,
   248  		},
   249  	}
   250  
   251  	for name, test := range tests {
   252  		t.Run(name, func(t *testing.T) {
   253  			inBody := MockBody(test.In)
   254  			got, remainBody, diags := inBody.PartialContent(test.Schema)
   255  			if len(diags) != test.DiagCount {
   256  				t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
   257  				for _, diag := range diags {
   258  					t.Logf("- %s", diag)
   259  				}
   260  			}
   261  
   262  			if !reflect.DeepEqual(got, test.Want) {
   263  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   264  			}
   265  
   266  			gotRemain := remainBody.(mockBody).C
   267  			if !reflect.DeepEqual(gotRemain, test.Remain) {
   268  				t.Errorf("wrong remain\ngot:  %#v\nwant: %#v", gotRemain, test.Remain)
   269  			}
   270  		})
   271  	}
   272  }
   273  
   274  func TestExprList(t *testing.T) {
   275  	tests := map[string]struct {
   276  		In    hcl.Expression
   277  		Want  []hcl.Expression
   278  		Diags string
   279  	}{
   280  		"as list": {
   281  			In: MockExprLiteral(cty.ListVal([]cty.Value{
   282  				cty.StringVal("foo"),
   283  				cty.StringVal("bar"),
   284  			})),
   285  			Want: []hcl.Expression{
   286  				MockExprLiteral(cty.StringVal("foo")),
   287  				MockExprLiteral(cty.StringVal("bar")),
   288  			},
   289  		},
   290  		"as tuple": {
   291  			In: MockExprLiteral(cty.TupleVal([]cty.Value{
   292  				cty.StringVal("foo"),
   293  				cty.StringVal("bar"),
   294  			})),
   295  			Want: []hcl.Expression{
   296  				MockExprLiteral(cty.StringVal("foo")),
   297  				MockExprLiteral(cty.StringVal("bar")),
   298  			},
   299  		},
   300  		"not list": {
   301  			In: MockExprLiteral(cty.ObjectVal(map[string]cty.Value{
   302  				"a": cty.StringVal("foo"),
   303  				"b": cty.StringVal("bar"),
   304  			})),
   305  			Want:  nil,
   306  			Diags: "list expression is required",
   307  		},
   308  	}
   309  
   310  	for name, tc := range tests {
   311  		t.Run(name, func(t *testing.T) {
   312  			got, diags := hcl.ExprList(tc.In)
   313  			if tc.Diags != "" {
   314  				if diags.HasErrors() && !strings.Contains(diags.Error(), tc.Diags) {
   315  					t.Errorf("expected error %q, got %q", tc.Diags, diags)
   316  				}
   317  				if !diags.HasErrors() {
   318  					t.Errorf("expected diagnostic message %q", tc.Diags)
   319  				}
   320  			} else if diags.HasErrors() {
   321  				t.Error(diags)
   322  			}
   323  
   324  			if !reflect.DeepEqual(got, tc.Want) {
   325  				t.Errorf("incorrect expression,\ngot:  %#v\nwant: %#v", got, tc.Want)
   326  			}
   327  		})
   328  	}
   329  }
   330  
   331  func TestExprMap(t *testing.T) {
   332  	tests := map[string]struct {
   333  		In    hcl.Expression
   334  		Want  []hcl.KeyValuePair
   335  		Diags string
   336  	}{
   337  		"as object": {
   338  			In: MockExprLiteral(cty.ObjectVal(map[string]cty.Value{
   339  				"name":  cty.StringVal("test"),
   340  				"count": cty.NumberIntVal(2),
   341  			})),
   342  			Want: []hcl.KeyValuePair{
   343  				{
   344  					Key:   MockExprLiteral(cty.StringVal("count")),
   345  					Value: MockExprLiteral(cty.NumberIntVal(2)),
   346  				},
   347  				{
   348  					Key:   MockExprLiteral(cty.StringVal("name")),
   349  					Value: MockExprLiteral(cty.StringVal("test")),
   350  				},
   351  			},
   352  		},
   353  		"as map": {
   354  			In: MockExprLiteral(cty.MapVal(map[string]cty.Value{
   355  				"name":    cty.StringVal("test"),
   356  				"version": cty.StringVal("2.0.0"),
   357  			})),
   358  			Want: []hcl.KeyValuePair{
   359  				{
   360  					Key:   MockExprLiteral(cty.StringVal("name")),
   361  					Value: MockExprLiteral(cty.StringVal("test")),
   362  				},
   363  				{
   364  					Key:   MockExprLiteral(cty.StringVal("version")),
   365  					Value: MockExprLiteral(cty.StringVal("2.0.0")),
   366  				},
   367  			},
   368  		},
   369  		"not map": {
   370  			In: MockExprLiteral(cty.ListVal([]cty.Value{
   371  				cty.StringVal("foo"),
   372  				cty.StringVal("bar"),
   373  			})),
   374  			Want:  nil,
   375  			Diags: "map expression is required",
   376  		},
   377  	}
   378  
   379  	for name, tc := range tests {
   380  		t.Run(name, func(t *testing.T) {
   381  			got, diags := hcl.ExprMap(tc.In)
   382  			if tc.Diags != "" {
   383  				if diags.HasErrors() && !strings.Contains(diags.Error(), tc.Diags) {
   384  					t.Errorf("expected error %q, got %q", tc.Diags, diags)
   385  				}
   386  				if !diags.HasErrors() {
   387  					t.Errorf("expected diagnostic message %q", tc.Diags)
   388  				}
   389  			} else if diags.HasErrors() {
   390  				t.Error(diags)
   391  			}
   392  
   393  			if !reflect.DeepEqual(got, tc.Want) {
   394  				t.Errorf("incorrect expression,\ngot:  %#v\nwant: %#v", got, tc.Want)
   395  			}
   396  		})
   397  	}
   398  }