github.com/opentofu/opentofu@v1.7.1/internal/configs/configschema/path_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package configschema
     7  
     8  import (
     9  	"testing"
    10  
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestAttributeByPath(t *testing.T) {
    15  	schema := &Block{
    16  		Attributes: map[string]*Attribute{
    17  			"a1": {Description: "a1"},
    18  			"a2": {Description: "a2"},
    19  			"a3": {
    20  				Description: "a3",
    21  				NestedType: &Object{
    22  					Nesting: NestingList,
    23  					Attributes: map[string]*Attribute{
    24  						"nt1": {Description: "nt1"},
    25  						"nt2": {
    26  							Description: "nt2",
    27  							NestedType: &Object{
    28  								Nesting: NestingSingle,
    29  								Attributes: map[string]*Attribute{
    30  									"deeply_nested": {Description: "deeply_nested"},
    31  								},
    32  							},
    33  						},
    34  					},
    35  				},
    36  			},
    37  		},
    38  		BlockTypes: map[string]*NestedBlock{
    39  			"b1": {
    40  				Nesting: NestingList,
    41  				Block: Block{
    42  					Attributes: map[string]*Attribute{
    43  						"a3": {Description: "a3"},
    44  						"a4": {Description: "a4"},
    45  					},
    46  					BlockTypes: map[string]*NestedBlock{
    47  						"b2": {
    48  							Nesting: NestingMap,
    49  							Block: Block{
    50  								Attributes: map[string]*Attribute{
    51  									"a5": {Description: "a5"},
    52  									"a6": {Description: "a6"},
    53  								},
    54  							},
    55  						},
    56  					},
    57  				},
    58  			},
    59  			"b3": {
    60  				Nesting: NestingMap,
    61  				Block: Block{
    62  					Attributes: map[string]*Attribute{
    63  						"a7": {Description: "a7"},
    64  						"a8": {Description: "a8"},
    65  					},
    66  					BlockTypes: map[string]*NestedBlock{
    67  						"b4": {
    68  							Nesting: NestingSet,
    69  							Block: Block{
    70  								Attributes: map[string]*Attribute{
    71  									"a9":  {Description: "a9"},
    72  									"a10": {Description: "a10"},
    73  								},
    74  							},
    75  						},
    76  					},
    77  				},
    78  			},
    79  		},
    80  	}
    81  
    82  	for _, tc := range []struct {
    83  		path            cty.Path
    84  		attrDescription string
    85  		exists          bool
    86  	}{
    87  		{
    88  			cty.GetAttrPath("a2"),
    89  			"a2",
    90  			true,
    91  		},
    92  		{
    93  			cty.GetAttrPath("a3").IndexInt(1).GetAttr("nt2"),
    94  			"nt2",
    95  			true,
    96  		},
    97  		{
    98  			cty.GetAttrPath("a3").IndexInt(1).GetAttr("b2").IndexString("foo").GetAttr("no"),
    99  			"missing",
   100  			false,
   101  		},
   102  		{
   103  			cty.GetAttrPath("b1"),
   104  			"block",
   105  			false,
   106  		},
   107  		{
   108  			cty.GetAttrPath("b1").IndexInt(1).GetAttr("a3"),
   109  			"a3",
   110  			true,
   111  		},
   112  		{
   113  			cty.GetAttrPath("b1").IndexInt(1).GetAttr("b2").IndexString("foo").GetAttr("a7"),
   114  			"missing",
   115  			false,
   116  		},
   117  		{
   118  			cty.GetAttrPath("b1").IndexInt(1).GetAttr("b2").IndexString("foo").GetAttr("a6"),
   119  			"a6",
   120  			true,
   121  		},
   122  		{
   123  			cty.GetAttrPath("b3").IndexString("foo").GetAttr("b2").IndexString("foo").GetAttr("a7"),
   124  			"missing_block",
   125  			false,
   126  		},
   127  		{
   128  			cty.GetAttrPath("b3").IndexString("foo").GetAttr("a7"),
   129  			"a7",
   130  			true,
   131  		},
   132  		{
   133  			// Index steps don't apply to the schema, so the set Index value doesn't matter.
   134  			cty.GetAttrPath("b3").IndexString("foo").GetAttr("b4").Index(cty.EmptyObjectVal).GetAttr("a9"),
   135  			"a9",
   136  			true,
   137  		},
   138  	} {
   139  		t.Run(tc.attrDescription, func(t *testing.T) {
   140  			attr := schema.AttributeByPath(tc.path)
   141  			if !tc.exists && attr == nil {
   142  				return
   143  			}
   144  
   145  			if attr == nil {
   146  				t.Fatalf("missing attribute from path %#v\n", tc.path)
   147  			}
   148  
   149  			if attr.Description != tc.attrDescription {
   150  				t.Fatalf("expected Attribute for %q, got %#v\n", tc.attrDescription, attr)
   151  			}
   152  		})
   153  	}
   154  }
   155  
   156  func TestObject_AttributeByPath(t *testing.T) {
   157  	obj := &Object{
   158  		Nesting: NestingList,
   159  		Attributes: map[string]*Attribute{
   160  			"a1": {Description: "a1"},
   161  			"a2": {
   162  				Description: "a2",
   163  				NestedType: &Object{
   164  					Nesting: NestingSingle,
   165  					Attributes: map[string]*Attribute{
   166  						"n1": {Description: "n1"},
   167  						"n2": {
   168  							Description: "n2",
   169  							NestedType: &Object{
   170  								Attributes: map[string]*Attribute{
   171  									"dn1": {Description: "dn1"},
   172  								},
   173  							},
   174  						},
   175  					},
   176  				},
   177  			},
   178  		},
   179  	}
   180  
   181  	tests := []struct {
   182  		path            cty.Path
   183  		attrDescription string
   184  		exists          bool
   185  	}{
   186  		{
   187  			cty.GetAttrPath("a2"),
   188  			"a2",
   189  			true,
   190  		},
   191  		{
   192  			cty.GetAttrPath("a3"),
   193  			"missing",
   194  			false,
   195  		},
   196  		{
   197  			cty.GetAttrPath("a2").IndexString("foo").GetAttr("n1"),
   198  			"n1",
   199  			true,
   200  		},
   201  		{
   202  			cty.GetAttrPath("a2").IndexString("foo").GetAttr("n2").IndexInt(11).GetAttr("dn1"),
   203  			"dn1",
   204  			true,
   205  		},
   206  		{
   207  			cty.GetAttrPath("a2").IndexString("foo").GetAttr("n2").IndexInt(11).GetAttr("dn1").IndexString("hello").GetAttr("nope"),
   208  			"missing_nested",
   209  			false,
   210  		},
   211  	}
   212  
   213  	for _, tc := range tests {
   214  		t.Run(tc.attrDescription, func(t *testing.T) {
   215  			attr := obj.AttributeByPath(tc.path)
   216  			if !tc.exists && attr == nil {
   217  				return
   218  			}
   219  
   220  			if !tc.exists && attr != nil {
   221  				t.Fatalf("found Attribute, expected nil from path %#v\n", tc.path)
   222  			}
   223  
   224  			if attr == nil {
   225  				t.Fatalf("missing attribute from path %#v\n", tc.path)
   226  			}
   227  
   228  			if attr.Description != tc.attrDescription {
   229  				t.Fatalf("expected Attribute for %q, got %#v\n", tc.attrDescription, attr)
   230  			}
   231  		})
   232  	}
   233  
   234  }