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 }