github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/addrs/parse_target_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package addrs 5 6 import ( 7 "testing" 8 9 "github.com/go-test/deep" 10 "github.com/hashicorp/hcl/v2" 11 "github.com/hashicorp/hcl/v2/hclsyntax" 12 "github.com/terramate-io/tf/tfdiags" 13 ) 14 15 func TestParseTarget(t *testing.T) { 16 tests := []struct { 17 Input string 18 Want *Target 19 WantErr string 20 }{ 21 { 22 `module.foo`, 23 &Target{ 24 Subject: ModuleInstance{ 25 { 26 Name: "foo", 27 }, 28 }, 29 SourceRange: tfdiags.SourceRange{ 30 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 31 End: tfdiags.SourcePos{Line: 1, Column: 11, Byte: 10}, 32 }, 33 }, 34 ``, 35 }, 36 { 37 `module.foo[2]`, 38 &Target{ 39 Subject: ModuleInstance{ 40 { 41 Name: "foo", 42 InstanceKey: IntKey(2), 43 }, 44 }, 45 SourceRange: tfdiags.SourceRange{ 46 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 47 End: tfdiags.SourcePos{Line: 1, Column: 14, Byte: 13}, 48 }, 49 }, 50 ``, 51 }, 52 { 53 `module.foo[2].module.bar`, 54 &Target{ 55 Subject: ModuleInstance{ 56 { 57 Name: "foo", 58 InstanceKey: IntKey(2), 59 }, 60 { 61 Name: "bar", 62 }, 63 }, 64 SourceRange: tfdiags.SourceRange{ 65 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 66 End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24}, 67 }, 68 }, 69 ``, 70 }, 71 { 72 `aws_instance.foo`, 73 &Target{ 74 Subject: AbsResource{ 75 Resource: Resource{ 76 Mode: ManagedResourceMode, 77 Type: "aws_instance", 78 Name: "foo", 79 }, 80 Module: RootModuleInstance, 81 }, 82 SourceRange: tfdiags.SourceRange{ 83 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 84 End: tfdiags.SourcePos{Line: 1, Column: 17, Byte: 16}, 85 }, 86 }, 87 ``, 88 }, 89 { 90 `aws_instance.foo[1]`, 91 &Target{ 92 Subject: AbsResourceInstance{ 93 Resource: ResourceInstance{ 94 Resource: Resource{ 95 Mode: ManagedResourceMode, 96 Type: "aws_instance", 97 Name: "foo", 98 }, 99 Key: IntKey(1), 100 }, 101 Module: RootModuleInstance, 102 }, 103 SourceRange: tfdiags.SourceRange{ 104 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 105 End: tfdiags.SourcePos{Line: 1, Column: 20, Byte: 19}, 106 }, 107 }, 108 ``, 109 }, 110 { 111 `data.aws_instance.foo`, 112 &Target{ 113 Subject: AbsResource{ 114 Resource: Resource{ 115 Mode: DataResourceMode, 116 Type: "aws_instance", 117 Name: "foo", 118 }, 119 Module: RootModuleInstance, 120 }, 121 SourceRange: tfdiags.SourceRange{ 122 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 123 End: tfdiags.SourcePos{Line: 1, Column: 22, Byte: 21}, 124 }, 125 }, 126 ``, 127 }, 128 { 129 `data.aws_instance.foo[1]`, 130 &Target{ 131 Subject: AbsResourceInstance{ 132 Resource: ResourceInstance{ 133 Resource: Resource{ 134 Mode: DataResourceMode, 135 Type: "aws_instance", 136 Name: "foo", 137 }, 138 Key: IntKey(1), 139 }, 140 Module: RootModuleInstance, 141 }, 142 SourceRange: tfdiags.SourceRange{ 143 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 144 End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24}, 145 }, 146 }, 147 ``, 148 }, 149 { 150 `module.foo.aws_instance.bar`, 151 &Target{ 152 Subject: AbsResource{ 153 Resource: Resource{ 154 Mode: ManagedResourceMode, 155 Type: "aws_instance", 156 Name: "bar", 157 }, 158 Module: ModuleInstance{ 159 {Name: "foo"}, 160 }, 161 }, 162 SourceRange: tfdiags.SourceRange{ 163 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 164 End: tfdiags.SourcePos{Line: 1, Column: 28, Byte: 27}, 165 }, 166 }, 167 ``, 168 }, 169 { 170 `module.foo.module.bar.aws_instance.baz`, 171 &Target{ 172 Subject: AbsResource{ 173 Resource: Resource{ 174 Mode: ManagedResourceMode, 175 Type: "aws_instance", 176 Name: "baz", 177 }, 178 Module: ModuleInstance{ 179 {Name: "foo"}, 180 {Name: "bar"}, 181 }, 182 }, 183 SourceRange: tfdiags.SourceRange{ 184 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 185 End: tfdiags.SourcePos{Line: 1, Column: 39, Byte: 38}, 186 }, 187 }, 188 ``, 189 }, 190 { 191 `module.foo.module.bar.aws_instance.baz["hello"]`, 192 &Target{ 193 Subject: AbsResourceInstance{ 194 Resource: ResourceInstance{ 195 Resource: Resource{ 196 Mode: ManagedResourceMode, 197 Type: "aws_instance", 198 Name: "baz", 199 }, 200 Key: StringKey("hello"), 201 }, 202 Module: ModuleInstance{ 203 {Name: "foo"}, 204 {Name: "bar"}, 205 }, 206 }, 207 SourceRange: tfdiags.SourceRange{ 208 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 209 End: tfdiags.SourcePos{Line: 1, Column: 48, Byte: 47}, 210 }, 211 }, 212 ``, 213 }, 214 { 215 `module.foo.data.aws_instance.bar`, 216 &Target{ 217 Subject: AbsResource{ 218 Resource: Resource{ 219 Mode: DataResourceMode, 220 Type: "aws_instance", 221 Name: "bar", 222 }, 223 Module: ModuleInstance{ 224 {Name: "foo"}, 225 }, 226 }, 227 SourceRange: tfdiags.SourceRange{ 228 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 229 End: tfdiags.SourcePos{Line: 1, Column: 33, Byte: 32}, 230 }, 231 }, 232 ``, 233 }, 234 { 235 `module.foo.module.bar.data.aws_instance.baz`, 236 &Target{ 237 Subject: AbsResource{ 238 Resource: Resource{ 239 Mode: DataResourceMode, 240 Type: "aws_instance", 241 Name: "baz", 242 }, 243 Module: ModuleInstance{ 244 {Name: "foo"}, 245 {Name: "bar"}, 246 }, 247 }, 248 SourceRange: tfdiags.SourceRange{ 249 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 250 End: tfdiags.SourcePos{Line: 1, Column: 44, Byte: 43}, 251 }, 252 }, 253 ``, 254 }, 255 { 256 `module.foo.module.bar[0].data.aws_instance.baz`, 257 &Target{ 258 Subject: AbsResource{ 259 Resource: Resource{ 260 Mode: DataResourceMode, 261 Type: "aws_instance", 262 Name: "baz", 263 }, 264 Module: ModuleInstance{ 265 {Name: "foo", InstanceKey: NoKey}, 266 {Name: "bar", InstanceKey: IntKey(0)}, 267 }, 268 }, 269 SourceRange: tfdiags.SourceRange{ 270 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 271 End: tfdiags.SourcePos{Line: 1, Column: 47, Byte: 46}, 272 }, 273 }, 274 ``, 275 }, 276 { 277 `module.foo.module.bar["a"].data.aws_instance.baz["hello"]`, 278 &Target{ 279 Subject: AbsResourceInstance{ 280 Resource: ResourceInstance{ 281 Resource: Resource{ 282 Mode: DataResourceMode, 283 Type: "aws_instance", 284 Name: "baz", 285 }, 286 Key: StringKey("hello"), 287 }, 288 Module: ModuleInstance{ 289 {Name: "foo", InstanceKey: NoKey}, 290 {Name: "bar", InstanceKey: StringKey("a")}, 291 }, 292 }, 293 SourceRange: tfdiags.SourceRange{ 294 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 295 End: tfdiags.SourcePos{Line: 1, Column: 58, Byte: 57}, 296 }, 297 }, 298 ``, 299 }, 300 { 301 `module.foo.module.bar.data.aws_instance.baz["hello"]`, 302 &Target{ 303 Subject: AbsResourceInstance{ 304 Resource: ResourceInstance{ 305 Resource: Resource{ 306 Mode: DataResourceMode, 307 Type: "aws_instance", 308 Name: "baz", 309 }, 310 Key: StringKey("hello"), 311 }, 312 Module: ModuleInstance{ 313 {Name: "foo"}, 314 {Name: "bar"}, 315 }, 316 }, 317 SourceRange: tfdiags.SourceRange{ 318 Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0}, 319 End: tfdiags.SourcePos{Line: 1, Column: 53, Byte: 52}, 320 }, 321 }, 322 ``, 323 }, 324 325 { 326 `aws_instance`, 327 nil, 328 `Resource specification must include a resource type and name.`, 329 }, 330 { 331 `module`, 332 nil, 333 `Prefix "module." must be followed by a module name.`, 334 }, 335 { 336 `module["baz"]`, 337 nil, 338 `Prefix "module." must be followed by a module name.`, 339 }, 340 { 341 `module.baz.bar`, 342 nil, 343 `Resource specification must include a resource type and name.`, 344 }, 345 { 346 `aws_instance.foo.bar`, 347 nil, 348 `Resource instance key must be given in square brackets.`, 349 }, 350 { 351 `aws_instance.foo[1].baz`, 352 nil, 353 `Unexpected extra operators after address.`, 354 }, 355 } 356 357 for _, test := range tests { 358 t.Run(test.Input, func(t *testing.T) { 359 traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(test.Input), "", hcl.Pos{Line: 1, Column: 1}) 360 if travDiags.HasErrors() { 361 t.Fatal(travDiags.Error()) 362 } 363 364 got, diags := ParseTarget(traversal) 365 366 switch len(diags) { 367 case 0: 368 if test.WantErr != "" { 369 t.Fatalf("succeeded; want error: %s", test.WantErr) 370 } 371 case 1: 372 if test.WantErr == "" { 373 t.Fatalf("unexpected diagnostics: %s", diags.Err()) 374 } 375 if got, want := diags[0].Description().Detail, test.WantErr; got != want { 376 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 377 } 378 default: 379 t.Fatalf("too many diagnostics: %s", diags.Err()) 380 } 381 382 if diags.HasErrors() { 383 return 384 } 385 386 for _, problem := range deep.Equal(got, test.Want) { 387 t.Errorf(problem) 388 } 389 }) 390 } 391 }