kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/configs/configschema/validate_traversal_test.go (about)

     1  package configschema
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/hashicorp/hcl/v2/hclsyntax"
     8  	"github.com/zclconf/go-cty/cty"
     9  )
    10  
    11  func TestStaticValidateTraversal(t *testing.T) {
    12  	attrs := map[string]*Attribute{
    13  		"str":  {Type: cty.String, Optional: true},
    14  		"list": {Type: cty.List(cty.String), Optional: true},
    15  		"dyn":  {Type: cty.DynamicPseudoType, Optional: true},
    16  	}
    17  	schema := &Block{
    18  		Attributes: attrs,
    19  		BlockTypes: map[string]*NestedBlock{
    20  			"single_block": {
    21  				Nesting: NestingSingle,
    22  				Block: Block{
    23  					Attributes: attrs,
    24  				},
    25  			},
    26  			"list_block": {
    27  				Nesting: NestingList,
    28  				Block: Block{
    29  					Attributes: attrs,
    30  				},
    31  			},
    32  			"set_block": {
    33  				Nesting: NestingSet,
    34  				Block: Block{
    35  					Attributes: attrs,
    36  				},
    37  			},
    38  			"map_block": {
    39  				Nesting: NestingMap,
    40  				Block: Block{
    41  					Attributes: attrs,
    42  				},
    43  			},
    44  		},
    45  	}
    46  
    47  	tests := []struct {
    48  		Traversal string
    49  		WantError string
    50  	}{
    51  		{
    52  			`obj`,
    53  			``,
    54  		},
    55  		{
    56  			`obj.str`,
    57  			``,
    58  		},
    59  		{
    60  			`obj.str.nonexist`,
    61  			`Unsupported attribute: Can't access attributes on a primitive-typed value (string).`,
    62  		},
    63  		{
    64  			`obj.list`,
    65  			``,
    66  		},
    67  		{
    68  			`obj.list[0]`,
    69  			``,
    70  		},
    71  		{
    72  			`obj.list.nonexist`,
    73  			`Unsupported attribute: This value does not have any attributes.`,
    74  		},
    75  		{
    76  			`obj.dyn`,
    77  			``,
    78  		},
    79  		{
    80  			`obj.dyn.anything_goes`,
    81  			``,
    82  		},
    83  		{
    84  			`obj.dyn[0]`,
    85  			``,
    86  		},
    87  		{
    88  			`obj.nonexist`,
    89  			`Unsupported attribute: This object has no argument, nested block, or exported attribute named "nonexist".`,
    90  		},
    91  		{
    92  			`obj[1]`,
    93  			`Invalid index operation: Only attribute access is allowed here, using the dot operator.`,
    94  		},
    95  		{
    96  			`obj["str"]`, // we require attribute access for the first step to avoid ambiguity with resource instance indices
    97  			`Invalid index operation: Only attribute access is allowed here. Did you mean to access attribute "str" using the dot operator?`,
    98  		},
    99  		{
   100  			`obj.atr`,
   101  			`Unsupported attribute: This object has no argument, nested block, or exported attribute named "atr". Did you mean "str"?`,
   102  		},
   103  		{
   104  			`obj.single_block`,
   105  			``,
   106  		},
   107  		{
   108  			`obj.single_block.str`,
   109  			``,
   110  		},
   111  		{
   112  			`obj.single_block.nonexist`,
   113  			`Unsupported attribute: This object has no argument, nested block, or exported attribute named "nonexist".`,
   114  		},
   115  		{
   116  			`obj.list_block`,
   117  			``,
   118  		},
   119  		{
   120  			`obj.list_block[0]`,
   121  			``,
   122  		},
   123  		{
   124  			`obj.list_block[0].str`,
   125  			``,
   126  		},
   127  		{
   128  			`obj.list_block[0].nonexist`,
   129  			`Unsupported attribute: This object has no argument, nested block, or exported attribute named "nonexist".`,
   130  		},
   131  		{
   132  			`obj.list_block.str`,
   133  			`Invalid operation: Block type "list_block" is represented by a list of objects, so it must be indexed using a numeric key, like .list_block[0].`,
   134  		},
   135  		{
   136  			`obj.set_block`,
   137  			``,
   138  		},
   139  		{
   140  			`obj.set_block[0]`,
   141  			`Cannot index a set value: Block type "set_block" is represented by a set of objects, and set elements do not have addressable keys. To find elements matching specific criteria, use a "for" expression with an "if" clause.`,
   142  		},
   143  		{
   144  			`obj.set_block.str`,
   145  			`Cannot index a set value: Block type "set_block" is represented by a set of objects, and set elements do not have addressable keys. To find elements matching specific criteria, use a "for" expression with an "if" clause.`,
   146  		},
   147  		{
   148  			`obj.map_block`,
   149  			``,
   150  		},
   151  		{
   152  			`obj.map_block.anything`,
   153  			``,
   154  		},
   155  		{
   156  			`obj.map_block["anything"]`,
   157  			``,
   158  		},
   159  		{
   160  			`obj.map_block.anything.str`,
   161  			``,
   162  		},
   163  		{
   164  			`obj.map_block["anything"].str`,
   165  			``,
   166  		},
   167  		{
   168  			`obj.map_block.anything.nonexist`,
   169  			`Unsupported attribute: This object has no argument, nested block, or exported attribute named "nonexist".`,
   170  		},
   171  	}
   172  
   173  	for _, test := range tests {
   174  		t.Run(test.Traversal, func(t *testing.T) {
   175  			traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(test.Traversal), "", hcl.Pos{Line: 1, Column: 1})
   176  			for _, diag := range parseDiags {
   177  				t.Error(diag.Error())
   178  			}
   179  
   180  			// We trim the "obj." portion from the front since StaticValidateTraversal
   181  			// only works with relative traversals.
   182  			traversal = traversal[1:]
   183  
   184  			diags := schema.StaticValidateTraversal(traversal)
   185  			if test.WantError == "" {
   186  				if diags.HasErrors() {
   187  					t.Errorf("unexpected error: %s", diags.Err().Error())
   188  				}
   189  			} else {
   190  				if diags.HasErrors() {
   191  					if got := diags.Err().Error(); got != test.WantError {
   192  						t.Errorf("wrong error\ngot:  %s\nwant: %s", got, test.WantError)
   193  					}
   194  				} else {
   195  					t.Errorf("wrong error\ngot:  <no error>\nwant: %s", test.WantError)
   196  				}
   197  			}
   198  		})
   199  	}
   200  }