github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/lang/blocktoattr/variables_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package blocktoattr
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/google/go-cmp/cmp/cmpopts"
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/hashicorp/hcl/v2/hclsyntax"
    13  	hcljson "github.com/hashicorp/hcl/v2/json"
    14  	"github.com/terramate-io/tf/configs/configschema"
    15  	"github.com/zclconf/go-cty/cty"
    16  )
    17  
    18  func TestExpandedVariables(t *testing.T) {
    19  	fooSchema := &configschema.Block{
    20  		Attributes: map[string]*configschema.Attribute{
    21  			"foo": {
    22  				Type: cty.List(cty.Object(map[string]cty.Type{
    23  					"bar": cty.String,
    24  				})),
    25  				Optional: true,
    26  			},
    27  			"bar": {
    28  				Type:     cty.Map(cty.String),
    29  				Optional: true,
    30  			},
    31  		},
    32  	}
    33  
    34  	tests := map[string]struct {
    35  		src    string
    36  		json   bool
    37  		schema *configschema.Block
    38  		want   []hcl.Traversal
    39  	}{
    40  		"empty": {
    41  			src:    ``,
    42  			schema: &configschema.Block{},
    43  			want:   nil,
    44  		},
    45  		"attribute syntax": {
    46  			src: `
    47  foo = [
    48    {
    49      bar = baz
    50    },
    51  ]
    52  `,
    53  			schema: fooSchema,
    54  			want: []hcl.Traversal{
    55  				{
    56  					hcl.TraverseRoot{
    57  						Name: "baz",
    58  						SrcRange: hcl.Range{
    59  							Filename: "test.tf",
    60  							Start:    hcl.Pos{Line: 4, Column: 11, Byte: 23},
    61  							End:      hcl.Pos{Line: 4, Column: 14, Byte: 26},
    62  						},
    63  					},
    64  				},
    65  			},
    66  		},
    67  		"block syntax": {
    68  			src: `
    69  foo {
    70    bar = baz
    71  }
    72  `,
    73  			schema: fooSchema,
    74  			want: []hcl.Traversal{
    75  				{
    76  					hcl.TraverseRoot{
    77  						Name: "baz",
    78  						SrcRange: hcl.Range{
    79  							Filename: "test.tf",
    80  							Start:    hcl.Pos{Line: 3, Column: 9, Byte: 15},
    81  							End:      hcl.Pos{Line: 3, Column: 12, Byte: 18},
    82  						},
    83  					},
    84  				},
    85  			},
    86  		},
    87  		"block syntax with nested blocks": {
    88  			src: `
    89  foo {
    90    bar {
    91      boop = baz
    92    }
    93  }
    94  `,
    95  			schema: &configschema.Block{
    96  				Attributes: map[string]*configschema.Attribute{
    97  					"foo": {
    98  						Type: cty.List(cty.Object(map[string]cty.Type{
    99  							"bar": cty.List(cty.Object(map[string]cty.Type{
   100  								"boop": cty.String,
   101  							})),
   102  						})),
   103  						Optional: true,
   104  					},
   105  				},
   106  			},
   107  			want: []hcl.Traversal{
   108  				{
   109  					hcl.TraverseRoot{
   110  						Name: "baz",
   111  						SrcRange: hcl.Range{
   112  							Filename: "test.tf",
   113  							Start:    hcl.Pos{Line: 4, Column: 12, Byte: 26},
   114  							End:      hcl.Pos{Line: 4, Column: 15, Byte: 29},
   115  						},
   116  					},
   117  				},
   118  			},
   119  		},
   120  		"dynamic block syntax": {
   121  			src: `
   122  dynamic "foo" {
   123    for_each = beep
   124    content {
   125      bar = baz
   126    }
   127  }
   128  `,
   129  			schema: fooSchema,
   130  			want: []hcl.Traversal{
   131  				{
   132  					hcl.TraverseRoot{
   133  						Name: "beep",
   134  						SrcRange: hcl.Range{
   135  							Filename: "test.tf",
   136  							Start:    hcl.Pos{Line: 3, Column: 14, Byte: 30},
   137  							End:      hcl.Pos{Line: 3, Column: 18, Byte: 34},
   138  						},
   139  					},
   140  				},
   141  				{
   142  					hcl.TraverseRoot{
   143  						Name: "baz",
   144  						SrcRange: hcl.Range{
   145  							Filename: "test.tf",
   146  							Start:    hcl.Pos{Line: 5, Column: 11, Byte: 57},
   147  							End:      hcl.Pos{Line: 5, Column: 14, Byte: 60},
   148  						},
   149  					},
   150  				},
   151  			},
   152  		},
   153  		"misplaced dynamic block": {
   154  			src: `
   155  dynamic "bar" {
   156    for_each = beep
   157    content {
   158      key = val
   159    }
   160  }
   161  `,
   162  			schema: fooSchema,
   163  			want: []hcl.Traversal{
   164  				{
   165  					hcl.TraverseRoot{
   166  						Name: "beep",
   167  						SrcRange: hcl.Range{
   168  							Filename: "test.tf",
   169  							Start:    hcl.Pos{Line: 3, Column: 14, Byte: 30},
   170  							End:      hcl.Pos{Line: 3, Column: 18, Byte: 34},
   171  						},
   172  					},
   173  				},
   174  			},
   175  		},
   176  	}
   177  
   178  	for name, test := range tests {
   179  		t.Run(name, func(t *testing.T) {
   180  			var f *hcl.File
   181  			var diags hcl.Diagnostics
   182  			if test.json {
   183  				f, diags = hcljson.Parse([]byte(test.src), "test.tf.json")
   184  			} else {
   185  				f, diags = hclsyntax.ParseConfig([]byte(test.src), "test.tf", hcl.Pos{Line: 1, Column: 1})
   186  			}
   187  			if diags.HasErrors() {
   188  				for _, diag := range diags {
   189  					t.Errorf("unexpected diagnostic: %s", diag)
   190  				}
   191  				t.FailNow()
   192  			}
   193  
   194  			got := ExpandedVariables(f.Body, test.schema)
   195  
   196  			co := cmpopts.IgnoreUnexported(hcl.TraverseRoot{})
   197  			if !cmp.Equal(got, test.want, co) {
   198  				t.Errorf("wrong result\n%s", cmp.Diff(test.want, got, co))
   199  			}
   200  		})
   201  	}
   202  
   203  }