github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/lang/eval_test.go (about) 1 package lang 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "testing" 7 8 "github.com/muratcelep/terraform/not-internal/addrs" 9 "github.com/muratcelep/terraform/not-internal/configs/configschema" 10 "github.com/muratcelep/terraform/not-internal/instances" 11 12 "github.com/hashicorp/hcl/v2" 13 "github.com/hashicorp/hcl/v2/hclsyntax" 14 15 "github.com/zclconf/go-cty/cty" 16 ctyjson "github.com/zclconf/go-cty/cty/json" 17 ) 18 19 func TestScopeEvalContext(t *testing.T) { 20 data := &dataForTests{ 21 CountAttrs: map[string]cty.Value{ 22 "index": cty.NumberIntVal(0), 23 }, 24 ForEachAttrs: map[string]cty.Value{ 25 "key": cty.StringVal("a"), 26 "value": cty.NumberIntVal(1), 27 }, 28 Resources: map[string]cty.Value{ 29 "null_resource.foo": cty.ObjectVal(map[string]cty.Value{ 30 "attr": cty.StringVal("bar"), 31 }), 32 "data.null_data_source.foo": cty.ObjectVal(map[string]cty.Value{ 33 "attr": cty.StringVal("bar"), 34 }), 35 "null_resource.multi": cty.TupleVal([]cty.Value{ 36 cty.ObjectVal(map[string]cty.Value{ 37 "attr": cty.StringVal("multi0"), 38 }), 39 cty.ObjectVal(map[string]cty.Value{ 40 "attr": cty.StringVal("multi1"), 41 }), 42 }), 43 "null_resource.each": cty.ObjectVal(map[string]cty.Value{ 44 "each0": cty.ObjectVal(map[string]cty.Value{ 45 "attr": cty.StringVal("each0"), 46 }), 47 "each1": cty.ObjectVal(map[string]cty.Value{ 48 "attr": cty.StringVal("each1"), 49 }), 50 }), 51 "null_resource.multi[1]": cty.ObjectVal(map[string]cty.Value{ 52 "attr": cty.StringVal("multi1"), 53 }), 54 }, 55 LocalValues: map[string]cty.Value{ 56 "foo": cty.StringVal("bar"), 57 }, 58 Modules: map[string]cty.Value{ 59 "module.foo": cty.ObjectVal(map[string]cty.Value{ 60 "output0": cty.StringVal("bar0"), 61 "output1": cty.StringVal("bar1"), 62 }), 63 }, 64 PathAttrs: map[string]cty.Value{ 65 "module": cty.StringVal("foo/bar"), 66 }, 67 TerraformAttrs: map[string]cty.Value{ 68 "workspace": cty.StringVal("default"), 69 }, 70 InputVariables: map[string]cty.Value{ 71 "baz": cty.StringVal("boop"), 72 }, 73 } 74 75 tests := []struct { 76 Expr string 77 Want map[string]cty.Value 78 }{ 79 { 80 `12`, 81 map[string]cty.Value{}, 82 }, 83 { 84 `count.index`, 85 map[string]cty.Value{ 86 "count": cty.ObjectVal(map[string]cty.Value{ 87 "index": cty.NumberIntVal(0), 88 }), 89 }, 90 }, 91 { 92 `each.key`, 93 map[string]cty.Value{ 94 "each": cty.ObjectVal(map[string]cty.Value{ 95 "key": cty.StringVal("a"), 96 }), 97 }, 98 }, 99 { 100 `each.value`, 101 map[string]cty.Value{ 102 "each": cty.ObjectVal(map[string]cty.Value{ 103 "value": cty.NumberIntVal(1), 104 }), 105 }, 106 }, 107 { 108 `local.foo`, 109 map[string]cty.Value{ 110 "local": cty.ObjectVal(map[string]cty.Value{ 111 "foo": cty.StringVal("bar"), 112 }), 113 }, 114 }, 115 { 116 `null_resource.foo`, 117 map[string]cty.Value{ 118 "null_resource": cty.ObjectVal(map[string]cty.Value{ 119 "foo": cty.ObjectVal(map[string]cty.Value{ 120 "attr": cty.StringVal("bar"), 121 }), 122 }), 123 "resource": cty.ObjectVal(map[string]cty.Value{ 124 "null_resource": cty.ObjectVal(map[string]cty.Value{ 125 "foo": cty.ObjectVal(map[string]cty.Value{ 126 "attr": cty.StringVal("bar"), 127 }), 128 }), 129 }), 130 }, 131 }, 132 { 133 `null_resource.foo.attr`, 134 map[string]cty.Value{ 135 "null_resource": cty.ObjectVal(map[string]cty.Value{ 136 "foo": cty.ObjectVal(map[string]cty.Value{ 137 "attr": cty.StringVal("bar"), 138 }), 139 }), 140 "resource": cty.ObjectVal(map[string]cty.Value{ 141 "null_resource": cty.ObjectVal(map[string]cty.Value{ 142 "foo": cty.ObjectVal(map[string]cty.Value{ 143 "attr": cty.StringVal("bar"), 144 }), 145 }), 146 }), 147 }, 148 }, 149 { 150 `null_resource.multi`, 151 map[string]cty.Value{ 152 "null_resource": cty.ObjectVal(map[string]cty.Value{ 153 "multi": cty.TupleVal([]cty.Value{ 154 cty.ObjectVal(map[string]cty.Value{ 155 "attr": cty.StringVal("multi0"), 156 }), 157 cty.ObjectVal(map[string]cty.Value{ 158 "attr": cty.StringVal("multi1"), 159 }), 160 }), 161 }), 162 "resource": cty.ObjectVal(map[string]cty.Value{ 163 "null_resource": cty.ObjectVal(map[string]cty.Value{ 164 "multi": cty.TupleVal([]cty.Value{ 165 cty.ObjectVal(map[string]cty.Value{ 166 "attr": cty.StringVal("multi0"), 167 }), 168 cty.ObjectVal(map[string]cty.Value{ 169 "attr": cty.StringVal("multi1"), 170 }), 171 }), 172 }), 173 }), 174 }, 175 }, 176 { 177 // at this level, all instance references return the entire resource 178 `null_resource.multi[1]`, 179 map[string]cty.Value{ 180 "null_resource": cty.ObjectVal(map[string]cty.Value{ 181 "multi": cty.TupleVal([]cty.Value{ 182 cty.ObjectVal(map[string]cty.Value{ 183 "attr": cty.StringVal("multi0"), 184 }), 185 cty.ObjectVal(map[string]cty.Value{ 186 "attr": cty.StringVal("multi1"), 187 }), 188 }), 189 }), 190 "resource": cty.ObjectVal(map[string]cty.Value{ 191 "null_resource": cty.ObjectVal(map[string]cty.Value{ 192 "multi": cty.TupleVal([]cty.Value{ 193 cty.ObjectVal(map[string]cty.Value{ 194 "attr": cty.StringVal("multi0"), 195 }), 196 cty.ObjectVal(map[string]cty.Value{ 197 "attr": cty.StringVal("multi1"), 198 }), 199 }), 200 }), 201 }), 202 }, 203 }, 204 { 205 // at this level, all instance references return the entire resource 206 `null_resource.each["each1"]`, 207 map[string]cty.Value{ 208 "null_resource": cty.ObjectVal(map[string]cty.Value{ 209 "each": cty.ObjectVal(map[string]cty.Value{ 210 "each0": cty.ObjectVal(map[string]cty.Value{ 211 "attr": cty.StringVal("each0"), 212 }), 213 "each1": cty.ObjectVal(map[string]cty.Value{ 214 "attr": cty.StringVal("each1"), 215 }), 216 }), 217 }), 218 "resource": cty.ObjectVal(map[string]cty.Value{ 219 "null_resource": cty.ObjectVal(map[string]cty.Value{ 220 "each": cty.ObjectVal(map[string]cty.Value{ 221 "each0": cty.ObjectVal(map[string]cty.Value{ 222 "attr": cty.StringVal("each0"), 223 }), 224 "each1": cty.ObjectVal(map[string]cty.Value{ 225 "attr": cty.StringVal("each1"), 226 }), 227 }), 228 }), 229 }), 230 }, 231 }, 232 { 233 // at this level, all instance references return the entire resource 234 `null_resource.each["each1"].attr`, 235 map[string]cty.Value{ 236 "null_resource": cty.ObjectVal(map[string]cty.Value{ 237 "each": cty.ObjectVal(map[string]cty.Value{ 238 "each0": cty.ObjectVal(map[string]cty.Value{ 239 "attr": cty.StringVal("each0"), 240 }), 241 "each1": cty.ObjectVal(map[string]cty.Value{ 242 "attr": cty.StringVal("each1"), 243 }), 244 }), 245 }), 246 "resource": cty.ObjectVal(map[string]cty.Value{ 247 "null_resource": cty.ObjectVal(map[string]cty.Value{ 248 "each": cty.ObjectVal(map[string]cty.Value{ 249 "each0": cty.ObjectVal(map[string]cty.Value{ 250 "attr": cty.StringVal("each0"), 251 }), 252 "each1": cty.ObjectVal(map[string]cty.Value{ 253 "attr": cty.StringVal("each1"), 254 }), 255 }), 256 }), 257 }), 258 }, 259 }, 260 { 261 `foo(null_resource.multi, null_resource.multi[1])`, 262 map[string]cty.Value{ 263 "null_resource": cty.ObjectVal(map[string]cty.Value{ 264 "multi": cty.TupleVal([]cty.Value{ 265 cty.ObjectVal(map[string]cty.Value{ 266 "attr": cty.StringVal("multi0"), 267 }), 268 cty.ObjectVal(map[string]cty.Value{ 269 "attr": cty.StringVal("multi1"), 270 }), 271 }), 272 }), 273 "resource": cty.ObjectVal(map[string]cty.Value{ 274 "null_resource": cty.ObjectVal(map[string]cty.Value{ 275 "multi": cty.TupleVal([]cty.Value{ 276 cty.ObjectVal(map[string]cty.Value{ 277 "attr": cty.StringVal("multi0"), 278 }), 279 cty.ObjectVal(map[string]cty.Value{ 280 "attr": cty.StringVal("multi1"), 281 }), 282 }), 283 }), 284 }), 285 }, 286 }, 287 { 288 `data.null_data_source.foo`, 289 map[string]cty.Value{ 290 "data": cty.ObjectVal(map[string]cty.Value{ 291 "null_data_source": cty.ObjectVal(map[string]cty.Value{ 292 "foo": cty.ObjectVal(map[string]cty.Value{ 293 "attr": cty.StringVal("bar"), 294 }), 295 }), 296 }), 297 }, 298 }, 299 { 300 `module.foo`, 301 map[string]cty.Value{ 302 "module": cty.ObjectVal(map[string]cty.Value{ 303 "foo": cty.ObjectVal(map[string]cty.Value{ 304 "output0": cty.StringVal("bar0"), 305 "output1": cty.StringVal("bar1"), 306 }), 307 }), 308 }, 309 }, 310 // any module reference returns the entire module 311 { 312 `module.foo.output1`, 313 map[string]cty.Value{ 314 "module": cty.ObjectVal(map[string]cty.Value{ 315 "foo": cty.ObjectVal(map[string]cty.Value{ 316 "output0": cty.StringVal("bar0"), 317 "output1": cty.StringVal("bar1"), 318 }), 319 }), 320 }, 321 }, 322 { 323 `path.module`, 324 map[string]cty.Value{ 325 "path": cty.ObjectVal(map[string]cty.Value{ 326 "module": cty.StringVal("foo/bar"), 327 }), 328 }, 329 }, 330 { 331 `self.baz`, 332 map[string]cty.Value{ 333 "self": cty.ObjectVal(map[string]cty.Value{ 334 "attr": cty.StringVal("multi1"), 335 }), 336 }, 337 }, 338 { 339 `terraform.workspace`, 340 map[string]cty.Value{ 341 "terraform": cty.ObjectVal(map[string]cty.Value{ 342 "workspace": cty.StringVal("default"), 343 }), 344 }, 345 }, 346 { 347 `var.baz`, 348 map[string]cty.Value{ 349 "var": cty.ObjectVal(map[string]cty.Value{ 350 "baz": cty.StringVal("boop"), 351 }), 352 }, 353 }, 354 } 355 356 for _, test := range tests { 357 t.Run(test.Expr, func(t *testing.T) { 358 expr, parseDiags := hclsyntax.ParseExpression([]byte(test.Expr), "", hcl.Pos{Line: 1, Column: 1}) 359 if len(parseDiags) != 0 { 360 t.Errorf("unexpected diagnostics during parse") 361 for _, diag := range parseDiags { 362 t.Errorf("- %s", diag) 363 } 364 return 365 } 366 367 refs, refsDiags := ReferencesInExpr(expr) 368 if refsDiags.HasErrors() { 369 t.Fatal(refsDiags.Err()) 370 } 371 372 scope := &Scope{ 373 Data: data, 374 375 // "self" will just be an arbitrary one of the several resource 376 // instances we have in our test dataset. 377 SelfAddr: addrs.ResourceInstance{ 378 Resource: addrs.Resource{ 379 Mode: addrs.ManagedResourceMode, 380 Type: "null_resource", 381 Name: "multi", 382 }, 383 Key: addrs.IntKey(1), 384 }, 385 } 386 ctx, ctxDiags := scope.EvalContext(refs) 387 if ctxDiags.HasErrors() { 388 t.Fatal(ctxDiags.Err()) 389 } 390 391 // For easier test assertions we'll just remove any top-level 392 // empty objects from our variables map. 393 for k, v := range ctx.Variables { 394 if v.RawEquals(cty.EmptyObjectVal) { 395 delete(ctx.Variables, k) 396 } 397 } 398 399 gotVal := cty.ObjectVal(ctx.Variables) 400 wantVal := cty.ObjectVal(test.Want) 401 402 if !gotVal.RawEquals(wantVal) { 403 // We'll JSON-ize our values here just so it's easier to 404 // read them in the assertion output. 405 gotJSON := formattedJSONValue(gotVal) 406 wantJSON := formattedJSONValue(wantVal) 407 408 t.Errorf( 409 "wrong result\nexpr: %s\ngot: %s\nwant: %s", 410 test.Expr, gotJSON, wantJSON, 411 ) 412 } 413 }) 414 } 415 } 416 417 func TestScopeExpandEvalBlock(t *testing.T) { 418 nestedObjTy := cty.Object(map[string]cty.Type{ 419 "boop": cty.String, 420 }) 421 schema := &configschema.Block{ 422 Attributes: map[string]*configschema.Attribute{ 423 "foo": {Type: cty.String, Optional: true}, 424 "list_of_obj": {Type: cty.List(nestedObjTy), Optional: true}, 425 }, 426 BlockTypes: map[string]*configschema.NestedBlock{ 427 "bar": { 428 Nesting: configschema.NestingMap, 429 Block: configschema.Block{ 430 Attributes: map[string]*configschema.Attribute{ 431 "baz": {Type: cty.String, Optional: true}, 432 }, 433 }, 434 }, 435 }, 436 } 437 data := &dataForTests{ 438 LocalValues: map[string]cty.Value{ 439 "greeting": cty.StringVal("howdy"), 440 "list": cty.ListVal([]cty.Value{ 441 cty.StringVal("elem0"), 442 cty.StringVal("elem1"), 443 }), 444 "map": cty.MapVal(map[string]cty.Value{ 445 "key1": cty.StringVal("val1"), 446 "key2": cty.StringVal("val2"), 447 }), 448 }, 449 } 450 451 tests := map[string]struct { 452 Config string 453 Want cty.Value 454 }{ 455 "empty": { 456 ` 457 `, 458 cty.ObjectVal(map[string]cty.Value{ 459 "foo": cty.NullVal(cty.String), 460 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 461 "bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 462 "baz": cty.String, 463 })), 464 }), 465 }, 466 "literal attribute": { 467 ` 468 foo = "hello" 469 `, 470 cty.ObjectVal(map[string]cty.Value{ 471 "foo": cty.StringVal("hello"), 472 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 473 "bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 474 "baz": cty.String, 475 })), 476 }), 477 }, 478 "variable attribute": { 479 ` 480 foo = local.greeting 481 `, 482 cty.ObjectVal(map[string]cty.Value{ 483 "foo": cty.StringVal("howdy"), 484 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 485 "bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 486 "baz": cty.String, 487 })), 488 }), 489 }, 490 "one static block": { 491 ` 492 bar "static" {} 493 `, 494 cty.ObjectVal(map[string]cty.Value{ 495 "foo": cty.NullVal(cty.String), 496 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 497 "bar": cty.MapVal(map[string]cty.Value{ 498 "static": cty.ObjectVal(map[string]cty.Value{ 499 "baz": cty.NullVal(cty.String), 500 }), 501 }), 502 }), 503 }, 504 "two static blocks": { 505 ` 506 bar "static0" { 507 baz = 0 508 } 509 bar "static1" { 510 baz = 1 511 } 512 `, 513 cty.ObjectVal(map[string]cty.Value{ 514 "foo": cty.NullVal(cty.String), 515 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 516 "bar": cty.MapVal(map[string]cty.Value{ 517 "static0": cty.ObjectVal(map[string]cty.Value{ 518 "baz": cty.StringVal("0"), 519 }), 520 "static1": cty.ObjectVal(map[string]cty.Value{ 521 "baz": cty.StringVal("1"), 522 }), 523 }), 524 }), 525 }, 526 "dynamic blocks from list": { 527 ` 528 dynamic "bar" { 529 for_each = local.list 530 labels = [bar.value] 531 content { 532 baz = bar.key 533 } 534 } 535 `, 536 cty.ObjectVal(map[string]cty.Value{ 537 "foo": cty.NullVal(cty.String), 538 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 539 "bar": cty.MapVal(map[string]cty.Value{ 540 "elem0": cty.ObjectVal(map[string]cty.Value{ 541 "baz": cty.StringVal("0"), 542 }), 543 "elem1": cty.ObjectVal(map[string]cty.Value{ 544 "baz": cty.StringVal("1"), 545 }), 546 }), 547 }), 548 }, 549 "dynamic blocks from map": { 550 ` 551 dynamic "bar" { 552 for_each = local.map 553 labels = [bar.key] 554 content { 555 baz = bar.value 556 } 557 } 558 `, 559 cty.ObjectVal(map[string]cty.Value{ 560 "foo": cty.NullVal(cty.String), 561 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 562 "bar": cty.MapVal(map[string]cty.Value{ 563 "key1": cty.ObjectVal(map[string]cty.Value{ 564 "baz": cty.StringVal("val1"), 565 }), 566 "key2": cty.ObjectVal(map[string]cty.Value{ 567 "baz": cty.StringVal("val2"), 568 }), 569 }), 570 }), 571 }, 572 "list-of-object attribute": { 573 ` 574 list_of_obj = [ 575 { 576 boop = local.greeting 577 }, 578 ] 579 `, 580 cty.ObjectVal(map[string]cty.Value{ 581 "foo": cty.NullVal(cty.String), 582 "list_of_obj": cty.ListVal([]cty.Value{ 583 cty.ObjectVal(map[string]cty.Value{ 584 "boop": cty.StringVal("howdy"), 585 }), 586 }), 587 "bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 588 "baz": cty.String, 589 })), 590 }), 591 }, 592 "list-of-object attribute as blocks": { 593 ` 594 list_of_obj { 595 boop = local.greeting 596 } 597 `, 598 cty.ObjectVal(map[string]cty.Value{ 599 "foo": cty.NullVal(cty.String), 600 "list_of_obj": cty.ListVal([]cty.Value{ 601 cty.ObjectVal(map[string]cty.Value{ 602 "boop": cty.StringVal("howdy"), 603 }), 604 }), 605 "bar": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 606 "baz": cty.String, 607 })), 608 }), 609 }, 610 "lots of things at once": { 611 ` 612 foo = "whoop" 613 bar "static0" { 614 baz = "s0" 615 } 616 dynamic "bar" { 617 for_each = local.list 618 labels = [bar.value] 619 content { 620 baz = bar.key 621 } 622 } 623 bar "static1" { 624 baz = "s1" 625 } 626 dynamic "bar" { 627 for_each = local.map 628 labels = [bar.key] 629 content { 630 baz = bar.value 631 } 632 } 633 bar "static2" { 634 baz = "s2" 635 } 636 `, 637 cty.ObjectVal(map[string]cty.Value{ 638 "foo": cty.StringVal("whoop"), 639 "list_of_obj": cty.NullVal(cty.List(nestedObjTy)), 640 "bar": cty.MapVal(map[string]cty.Value{ 641 "key1": cty.ObjectVal(map[string]cty.Value{ 642 "baz": cty.StringVal("val1"), 643 }), 644 "key2": cty.ObjectVal(map[string]cty.Value{ 645 "baz": cty.StringVal("val2"), 646 }), 647 "elem0": cty.ObjectVal(map[string]cty.Value{ 648 "baz": cty.StringVal("0"), 649 }), 650 "elem1": cty.ObjectVal(map[string]cty.Value{ 651 "baz": cty.StringVal("1"), 652 }), 653 "static0": cty.ObjectVal(map[string]cty.Value{ 654 "baz": cty.StringVal("s0"), 655 }), 656 "static1": cty.ObjectVal(map[string]cty.Value{ 657 "baz": cty.StringVal("s1"), 658 }), 659 "static2": cty.ObjectVal(map[string]cty.Value{ 660 "baz": cty.StringVal("s2"), 661 }), 662 }), 663 }), 664 }, 665 } 666 667 for name, test := range tests { 668 t.Run(name, func(t *testing.T) { 669 file, parseDiags := hclsyntax.ParseConfig([]byte(test.Config), "", hcl.Pos{Line: 1, Column: 1}) 670 if len(parseDiags) != 0 { 671 t.Errorf("unexpected diagnostics during parse") 672 for _, diag := range parseDiags { 673 t.Errorf("- %s", diag) 674 } 675 return 676 } 677 678 body := file.Body 679 scope := &Scope{ 680 Data: data, 681 } 682 683 body, expandDiags := scope.ExpandBlock(body, schema) 684 if expandDiags.HasErrors() { 685 t.Fatal(expandDiags.Err()) 686 } 687 688 got, valDiags := scope.EvalBlock(body, schema) 689 if valDiags.HasErrors() { 690 t.Fatal(valDiags.Err()) 691 } 692 693 if !got.RawEquals(test.Want) { 694 // We'll JSON-ize our values here just so it's easier to 695 // read them in the assertion output. 696 gotJSON := formattedJSONValue(got) 697 wantJSON := formattedJSONValue(test.Want) 698 699 t.Errorf( 700 "wrong result\nconfig: %s\ngot: %s\nwant: %s", 701 test.Config, gotJSON, wantJSON, 702 ) 703 } 704 705 }) 706 } 707 708 } 709 710 func formattedJSONValue(val cty.Value) string { 711 val = cty.UnknownAsNull(val) // since JSON can't represent unknowns 712 j, err := ctyjson.Marshal(val, val.Type()) 713 if err != nil { 714 panic(err) 715 } 716 var buf bytes.Buffer 717 json.Indent(&buf, j, "", " ") 718 return buf.String() 719 } 720 721 func TestScopeEvalSelfBlock(t *testing.T) { 722 data := &dataForTests{ 723 PathAttrs: map[string]cty.Value{ 724 "module": cty.StringVal("foo/bar"), 725 "cwd": cty.StringVal("/home/foo/bar"), 726 "root": cty.StringVal("/home/foo"), 727 }, 728 TerraformAttrs: map[string]cty.Value{ 729 "workspace": cty.StringVal("default"), 730 }, 731 } 732 schema := &configschema.Block{ 733 Attributes: map[string]*configschema.Attribute{ 734 "attr": { 735 Type: cty.String, 736 }, 737 "num": { 738 Type: cty.Number, 739 }, 740 }, 741 } 742 743 tests := []struct { 744 Config string 745 Self cty.Value 746 KeyData instances.RepetitionData 747 Want map[string]cty.Value 748 }{ 749 { 750 Config: `attr = self.foo`, 751 Self: cty.ObjectVal(map[string]cty.Value{ 752 "foo": cty.StringVal("bar"), 753 }), 754 KeyData: instances.RepetitionData{ 755 CountIndex: cty.NumberIntVal(0), 756 }, 757 Want: map[string]cty.Value{ 758 "attr": cty.StringVal("bar"), 759 "num": cty.NullVal(cty.Number), 760 }, 761 }, 762 { 763 Config: `num = count.index`, 764 KeyData: instances.RepetitionData{ 765 CountIndex: cty.NumberIntVal(0), 766 }, 767 Want: map[string]cty.Value{ 768 "attr": cty.NullVal(cty.String), 769 "num": cty.NumberIntVal(0), 770 }, 771 }, 772 { 773 Config: `attr = each.key`, 774 KeyData: instances.RepetitionData{ 775 EachKey: cty.StringVal("a"), 776 }, 777 Want: map[string]cty.Value{ 778 "attr": cty.StringVal("a"), 779 "num": cty.NullVal(cty.Number), 780 }, 781 }, 782 { 783 Config: `attr = path.cwd`, 784 Want: map[string]cty.Value{ 785 "attr": cty.StringVal("/home/foo/bar"), 786 "num": cty.NullVal(cty.Number), 787 }, 788 }, 789 { 790 Config: `attr = path.module`, 791 Want: map[string]cty.Value{ 792 "attr": cty.StringVal("foo/bar"), 793 "num": cty.NullVal(cty.Number), 794 }, 795 }, 796 { 797 Config: `attr = path.root`, 798 Want: map[string]cty.Value{ 799 "attr": cty.StringVal("/home/foo"), 800 "num": cty.NullVal(cty.Number), 801 }, 802 }, 803 { 804 Config: `attr = terraform.workspace`, 805 Want: map[string]cty.Value{ 806 "attr": cty.StringVal("default"), 807 "num": cty.NullVal(cty.Number), 808 }, 809 }, 810 } 811 812 for _, test := range tests { 813 t.Run(test.Config, func(t *testing.T) { 814 file, parseDiags := hclsyntax.ParseConfig([]byte(test.Config), "", hcl.Pos{Line: 1, Column: 1}) 815 if len(parseDiags) != 0 { 816 t.Errorf("unexpected diagnostics during parse") 817 for _, diag := range parseDiags { 818 t.Errorf("- %s", diag) 819 } 820 return 821 } 822 823 body := file.Body 824 825 scope := &Scope{ 826 Data: data, 827 } 828 829 gotVal, ctxDiags := scope.EvalSelfBlock(body, test.Self, schema, test.KeyData) 830 if ctxDiags.HasErrors() { 831 t.Fatal(ctxDiags.Err()) 832 } 833 834 wantVal := cty.ObjectVal(test.Want) 835 836 if !gotVal.RawEquals(wantVal) { 837 t.Errorf( 838 "wrong result\nexpr: %s\ngot: %#v\nwant: %#v", 839 test.Config, gotVal, wantVal, 840 ) 841 } 842 }) 843 } 844 }