github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonstate/state_test.go (about) 1 package jsonstate 2 3 import ( 4 "encoding/json" 5 "reflect" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/zclconf/go-cty/cty" 10 11 "github.com/hashicorp/terraform/internal/addrs" 12 "github.com/hashicorp/terraform/internal/configs/configschema" 13 "github.com/hashicorp/terraform/internal/lang/marks" 14 "github.com/hashicorp/terraform/internal/states" 15 "github.com/hashicorp/terraform/internal/terraform" 16 ) 17 18 func TestMarshalOutputs(t *testing.T) { 19 tests := []struct { 20 Outputs map[string]*states.OutputValue 21 Want map[string]Output 22 Err bool 23 }{ 24 { 25 nil, 26 nil, 27 false, 28 }, 29 { 30 map[string]*states.OutputValue{ 31 "test": { 32 Sensitive: true, 33 Value: cty.StringVal("sekret"), 34 }, 35 }, 36 map[string]Output{ 37 "test": { 38 Sensitive: true, 39 Value: json.RawMessage(`"sekret"`), 40 Type: json.RawMessage(`"string"`), 41 }, 42 }, 43 false, 44 }, 45 { 46 map[string]*states.OutputValue{ 47 "test": { 48 Sensitive: false, 49 Value: cty.StringVal("not_so_sekret"), 50 }, 51 }, 52 map[string]Output{ 53 "test": { 54 Sensitive: false, 55 Value: json.RawMessage(`"not_so_sekret"`), 56 Type: json.RawMessage(`"string"`), 57 }, 58 }, 59 false, 60 }, 61 { 62 map[string]*states.OutputValue{ 63 "mapstring": { 64 Sensitive: false, 65 Value: cty.MapVal(map[string]cty.Value{ 66 "beep": cty.StringVal("boop"), 67 }), 68 }, 69 "setnumber": { 70 Sensitive: false, 71 Value: cty.SetVal([]cty.Value{ 72 cty.NumberIntVal(3), 73 cty.NumberIntVal(5), 74 cty.NumberIntVal(7), 75 cty.NumberIntVal(11), 76 }), 77 }, 78 }, 79 map[string]Output{ 80 "mapstring": { 81 Sensitive: false, 82 Value: json.RawMessage(`{"beep":"boop"}`), 83 Type: json.RawMessage(`["map","string"]`), 84 }, 85 "setnumber": { 86 Sensitive: false, 87 Value: json.RawMessage(`[3,5,7,11]`), 88 Type: json.RawMessage(`["set","number"]`), 89 }, 90 }, 91 false, 92 }, 93 } 94 95 for _, test := range tests { 96 got, err := MarshalOutputs(test.Outputs) 97 if test.Err { 98 if err == nil { 99 t.Fatal("succeeded; want error") 100 } 101 return 102 } else if err != nil { 103 t.Fatalf("unexpected error: %s", err) 104 } 105 if !cmp.Equal(test.Want, got) { 106 t.Fatalf("wrong result:\n%s", cmp.Diff(test.Want, got)) 107 } 108 } 109 } 110 111 func TestMarshalAttributeValues(t *testing.T) { 112 tests := []struct { 113 Attr cty.Value 114 Want AttributeValues 115 }{ 116 { 117 cty.NilVal, 118 nil, 119 }, 120 { 121 cty.NullVal(cty.String), 122 nil, 123 }, 124 { 125 cty.ObjectVal(map[string]cty.Value{ 126 "foo": cty.StringVal("bar"), 127 }), 128 AttributeValues{"foo": json.RawMessage(`"bar"`)}, 129 }, 130 { 131 cty.ObjectVal(map[string]cty.Value{ 132 "foo": cty.NullVal(cty.String), 133 }), 134 AttributeValues{"foo": json.RawMessage(`null`)}, 135 }, 136 { 137 cty.ObjectVal(map[string]cty.Value{ 138 "bar": cty.MapVal(map[string]cty.Value{ 139 "hello": cty.StringVal("world"), 140 }), 141 "baz": cty.ListVal([]cty.Value{ 142 cty.StringVal("goodnight"), 143 cty.StringVal("moon"), 144 }), 145 }), 146 AttributeValues{ 147 "bar": json.RawMessage(`{"hello":"world"}`), 148 "baz": json.RawMessage(`["goodnight","moon"]`), 149 }, 150 }, 151 // Marked values 152 { 153 cty.ObjectVal(map[string]cty.Value{ 154 "bar": cty.MapVal(map[string]cty.Value{ 155 "hello": cty.StringVal("world"), 156 }), 157 "baz": cty.ListVal([]cty.Value{ 158 cty.StringVal("goodnight"), 159 cty.StringVal("moon").Mark(marks.Sensitive), 160 }), 161 }), 162 AttributeValues{ 163 "bar": json.RawMessage(`{"hello":"world"}`), 164 "baz": json.RawMessage(`["goodnight","moon"]`), 165 }, 166 }, 167 } 168 169 for _, test := range tests { 170 got := marshalAttributeValues(test.Attr) 171 eq := reflect.DeepEqual(got, test.Want) 172 if !eq { 173 t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want) 174 } 175 } 176 } 177 178 func TestMarshalResources(t *testing.T) { 179 deposedKey := states.NewDeposedKey() 180 tests := map[string]struct { 181 Resources map[string]*states.Resource 182 Schemas *terraform.Schemas 183 Want []Resource 184 Err bool 185 }{ 186 "nil": { 187 nil, 188 nil, 189 nil, 190 false, 191 }, 192 "single resource": { 193 map[string]*states.Resource{ 194 "test_thing.baz": { 195 Addr: addrs.AbsResource{ 196 Resource: addrs.Resource{ 197 Mode: addrs.ManagedResourceMode, 198 Type: "test_thing", 199 Name: "bar", 200 }, 201 }, 202 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 203 addrs.NoKey: { 204 Current: &states.ResourceInstanceObjectSrc{ 205 Status: states.ObjectReady, 206 AttrsJSON: []byte(`{"woozles":"confuzles"}`), 207 }, 208 }, 209 }, 210 ProviderConfig: addrs.AbsProviderConfig{ 211 Provider: addrs.NewDefaultProvider("test"), 212 Module: addrs.RootModule, 213 }, 214 }, 215 }, 216 testSchemas(), 217 []Resource{ 218 { 219 Address: "test_thing.bar", 220 Mode: "managed", 221 Type: "test_thing", 222 Name: "bar", 223 Index: nil, 224 ProviderName: "registry.terraform.io/hashicorp/test", 225 AttributeValues: AttributeValues{ 226 "foozles": json.RawMessage(`null`), 227 "woozles": json.RawMessage(`"confuzles"`), 228 }, 229 SensitiveValues: json.RawMessage("{}"), 230 }, 231 }, 232 false, 233 }, 234 "resource with marks": { 235 map[string]*states.Resource{ 236 "test_thing.bar": { 237 Addr: addrs.AbsResource{ 238 Resource: addrs.Resource{ 239 Mode: addrs.ManagedResourceMode, 240 Type: "test_thing", 241 Name: "bar", 242 }, 243 }, 244 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 245 addrs.NoKey: { 246 Current: &states.ResourceInstanceObjectSrc{ 247 Status: states.ObjectReady, 248 AttrsJSON: []byte(`{"foozles":"confuzles"}`), 249 AttrSensitivePaths: []cty.PathValueMarks{{ 250 Path: cty.Path{cty.GetAttrStep{Name: "foozles"}}, 251 Marks: cty.NewValueMarks(marks.Sensitive)}, 252 }, 253 }, 254 }, 255 }, 256 ProviderConfig: addrs.AbsProviderConfig{ 257 Provider: addrs.NewDefaultProvider("test"), 258 Module: addrs.RootModule, 259 }, 260 }, 261 }, 262 testSchemas(), 263 []Resource{ 264 { 265 Address: "test_thing.bar", 266 Mode: "managed", 267 Type: "test_thing", 268 Name: "bar", 269 Index: nil, 270 ProviderName: "registry.terraform.io/hashicorp/test", 271 AttributeValues: AttributeValues{ 272 "foozles": json.RawMessage(`"confuzles"`), 273 "woozles": json.RawMessage(`null`), 274 }, 275 SensitiveValues: json.RawMessage(`{"foozles":true}`), 276 }, 277 }, 278 false, 279 }, 280 "single resource wrong schema": { 281 map[string]*states.Resource{ 282 "test_thing.baz": { 283 Addr: addrs.AbsResource{ 284 Resource: addrs.Resource{ 285 Mode: addrs.ManagedResourceMode, 286 Type: "test_thing", 287 Name: "bar", 288 }, 289 }, 290 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 291 addrs.NoKey: { 292 Current: &states.ResourceInstanceObjectSrc{ 293 SchemaVersion: 1, 294 Status: states.ObjectReady, 295 AttrsJSON: []byte(`{"woozles":["confuzles"]}`), 296 }, 297 }, 298 }, 299 ProviderConfig: addrs.AbsProviderConfig{ 300 Provider: addrs.NewDefaultProvider("test"), 301 Module: addrs.RootModule, 302 }, 303 }, 304 }, 305 testSchemas(), 306 nil, 307 true, 308 }, 309 "resource with count": { 310 map[string]*states.Resource{ 311 "test_thing.bar": { 312 Addr: addrs.AbsResource{ 313 Resource: addrs.Resource{ 314 Mode: addrs.ManagedResourceMode, 315 Type: "test_thing", 316 Name: "bar", 317 }, 318 }, 319 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 320 addrs.IntKey(0): { 321 Current: &states.ResourceInstanceObjectSrc{ 322 Status: states.ObjectReady, 323 AttrsJSON: []byte(`{"woozles":"confuzles"}`), 324 }, 325 }, 326 }, 327 ProviderConfig: addrs.AbsProviderConfig{ 328 Provider: addrs.NewDefaultProvider("test"), 329 Module: addrs.RootModule, 330 }, 331 }, 332 }, 333 testSchemas(), 334 []Resource{ 335 { 336 Address: "test_thing.bar[0]", 337 Mode: "managed", 338 Type: "test_thing", 339 Name: "bar", 340 Index: json.RawMessage(`0`), 341 ProviderName: "registry.terraform.io/hashicorp/test", 342 AttributeValues: AttributeValues{ 343 "foozles": json.RawMessage(`null`), 344 "woozles": json.RawMessage(`"confuzles"`), 345 }, 346 SensitiveValues: json.RawMessage("{}"), 347 }, 348 }, 349 false, 350 }, 351 "resource with for_each": { 352 map[string]*states.Resource{ 353 "test_thing.bar": { 354 Addr: addrs.AbsResource{ 355 Resource: addrs.Resource{ 356 Mode: addrs.ManagedResourceMode, 357 Type: "test_thing", 358 Name: "bar", 359 }, 360 }, 361 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 362 addrs.StringKey("rockhopper"): { 363 Current: &states.ResourceInstanceObjectSrc{ 364 Status: states.ObjectReady, 365 AttrsJSON: []byte(`{"woozles":"confuzles"}`), 366 }, 367 }, 368 }, 369 ProviderConfig: addrs.AbsProviderConfig{ 370 Provider: addrs.NewDefaultProvider("test"), 371 Module: addrs.RootModule, 372 }, 373 }, 374 }, 375 testSchemas(), 376 []Resource{ 377 { 378 Address: "test_thing.bar[\"rockhopper\"]", 379 Mode: "managed", 380 Type: "test_thing", 381 Name: "bar", 382 Index: json.RawMessage(`"rockhopper"`), 383 ProviderName: "registry.terraform.io/hashicorp/test", 384 AttributeValues: AttributeValues{ 385 "foozles": json.RawMessage(`null`), 386 "woozles": json.RawMessage(`"confuzles"`), 387 }, 388 SensitiveValues: json.RawMessage("{}"), 389 }, 390 }, 391 false, 392 }, 393 "deposed resource": { 394 map[string]*states.Resource{ 395 "test_thing.baz": { 396 Addr: addrs.AbsResource{ 397 Resource: addrs.Resource{ 398 Mode: addrs.ManagedResourceMode, 399 Type: "test_thing", 400 Name: "bar", 401 }, 402 }, 403 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 404 addrs.NoKey: { 405 Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ 406 states.DeposedKey(deposedKey): { 407 Status: states.ObjectReady, 408 AttrsJSON: []byte(`{"woozles":"confuzles"}`), 409 }, 410 }, 411 }, 412 }, 413 ProviderConfig: addrs.AbsProviderConfig{ 414 Provider: addrs.NewDefaultProvider("test"), 415 Module: addrs.RootModule, 416 }, 417 }, 418 }, 419 testSchemas(), 420 []Resource{ 421 { 422 Address: "test_thing.bar", 423 Mode: "managed", 424 Type: "test_thing", 425 Name: "bar", 426 Index: nil, 427 ProviderName: "registry.terraform.io/hashicorp/test", 428 DeposedKey: deposedKey.String(), 429 AttributeValues: AttributeValues{ 430 "foozles": json.RawMessage(`null`), 431 "woozles": json.RawMessage(`"confuzles"`), 432 }, 433 SensitiveValues: json.RawMessage("{}"), 434 }, 435 }, 436 false, 437 }, 438 "deposed and current resource": { 439 map[string]*states.Resource{ 440 "test_thing.baz": { 441 Addr: addrs.AbsResource{ 442 Resource: addrs.Resource{ 443 Mode: addrs.ManagedResourceMode, 444 Type: "test_thing", 445 Name: "bar", 446 }, 447 }, 448 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 449 addrs.NoKey: { 450 Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ 451 states.DeposedKey(deposedKey): { 452 Status: states.ObjectReady, 453 AttrsJSON: []byte(`{"woozles":"confuzles"}`), 454 }, 455 }, 456 Current: &states.ResourceInstanceObjectSrc{ 457 Status: states.ObjectReady, 458 AttrsJSON: []byte(`{"woozles":"confuzles"}`), 459 }, 460 }, 461 }, 462 ProviderConfig: addrs.AbsProviderConfig{ 463 Provider: addrs.NewDefaultProvider("test"), 464 Module: addrs.RootModule, 465 }, 466 }, 467 }, 468 testSchemas(), 469 []Resource{ 470 { 471 Address: "test_thing.bar", 472 Mode: "managed", 473 Type: "test_thing", 474 Name: "bar", 475 Index: nil, 476 ProviderName: "registry.terraform.io/hashicorp/test", 477 AttributeValues: AttributeValues{ 478 "foozles": json.RawMessage(`null`), 479 "woozles": json.RawMessage(`"confuzles"`), 480 }, 481 SensitiveValues: json.RawMessage("{}"), 482 }, 483 { 484 Address: "test_thing.bar", 485 Mode: "managed", 486 Type: "test_thing", 487 Name: "bar", 488 Index: nil, 489 ProviderName: "registry.terraform.io/hashicorp/test", 490 DeposedKey: deposedKey.String(), 491 AttributeValues: AttributeValues{ 492 "foozles": json.RawMessage(`null`), 493 "woozles": json.RawMessage(`"confuzles"`), 494 }, 495 SensitiveValues: json.RawMessage("{}"), 496 }, 497 }, 498 false, 499 }, 500 "resource with marked map attr": { 501 map[string]*states.Resource{ 502 "test_map_attr.bar": { 503 Addr: addrs.AbsResource{ 504 Resource: addrs.Resource{ 505 Mode: addrs.ManagedResourceMode, 506 Type: "test_map_attr", 507 Name: "bar", 508 }, 509 }, 510 Instances: map[addrs.InstanceKey]*states.ResourceInstance{ 511 addrs.NoKey: { 512 Current: &states.ResourceInstanceObjectSrc{ 513 Status: states.ObjectReady, 514 AttrsJSON: []byte(`{"data":{"woozles":"confuzles"}}`), 515 AttrSensitivePaths: []cty.PathValueMarks{{ 516 Path: cty.Path{cty.GetAttrStep{Name: "data"}}, 517 Marks: cty.NewValueMarks(marks.Sensitive)}, 518 }, 519 }, 520 }, 521 }, 522 ProviderConfig: addrs.AbsProviderConfig{ 523 Provider: addrs.NewDefaultProvider("test"), 524 Module: addrs.RootModule, 525 }, 526 }, 527 }, 528 testSchemas(), 529 []Resource{ 530 { 531 Address: "test_map_attr.bar", 532 Mode: "managed", 533 Type: "test_map_attr", 534 Name: "bar", 535 Index: nil, 536 ProviderName: "registry.terraform.io/hashicorp/test", 537 AttributeValues: AttributeValues{ 538 "data": json.RawMessage(`{"woozles":"confuzles"}`), 539 }, 540 SensitiveValues: json.RawMessage(`{"data":true}`), 541 }, 542 }, 543 false, 544 }, 545 } 546 547 for name, test := range tests { 548 t.Run(name, func(t *testing.T) { 549 got, err := marshalResources(test.Resources, addrs.RootModuleInstance, test.Schemas) 550 if test.Err { 551 if err == nil { 552 t.Fatal("succeeded; want error") 553 } 554 return 555 } else if err != nil { 556 t.Fatalf("unexpected error: %s", err) 557 } 558 559 diff := cmp.Diff(got, test.Want) 560 if diff != "" { 561 t.Fatalf("wrong result: %s\n", diff) 562 } 563 564 }) 565 } 566 } 567 568 func TestMarshalModules_basic(t *testing.T) { 569 childModule, _ := addrs.ParseModuleInstanceStr("module.child") 570 subModule, _ := addrs.ParseModuleInstanceStr("module.submodule") 571 testState := states.BuildState(func(s *states.SyncState) { 572 s.SetResourceInstanceCurrent( 573 addrs.Resource{ 574 Mode: addrs.ManagedResourceMode, 575 Type: "test_instance", 576 Name: "foo", 577 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 578 &states.ResourceInstanceObjectSrc{ 579 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 580 Status: states.ObjectReady, 581 }, 582 addrs.AbsProviderConfig{ 583 Provider: addrs.NewDefaultProvider("test"), 584 Module: addrs.RootModule, 585 }, 586 ) 587 s.SetResourceInstanceCurrent( 588 addrs.Resource{ 589 Mode: addrs.ManagedResourceMode, 590 Type: "test_instance", 591 Name: "foo", 592 }.Instance(addrs.NoKey).Absolute(childModule), 593 &states.ResourceInstanceObjectSrc{ 594 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 595 Status: states.ObjectReady, 596 }, 597 addrs.AbsProviderConfig{ 598 Provider: addrs.NewDefaultProvider("test"), 599 Module: childModule.Module(), 600 }, 601 ) 602 s.SetResourceInstanceCurrent( 603 addrs.Resource{ 604 Mode: addrs.ManagedResourceMode, 605 Type: "test_instance", 606 Name: "foo", 607 }.Instance(addrs.NoKey).Absolute(subModule), 608 &states.ResourceInstanceObjectSrc{ 609 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 610 Status: states.ObjectReady, 611 }, 612 addrs.AbsProviderConfig{ 613 Provider: addrs.NewDefaultProvider("test"), 614 Module: subModule.Module(), 615 }, 616 ) 617 }) 618 moduleMap := make(map[string][]addrs.ModuleInstance) 619 moduleMap[""] = []addrs.ModuleInstance{childModule, subModule} 620 621 got, err := marshalModules(testState, testSchemas(), moduleMap[""], moduleMap) 622 623 if err != nil { 624 t.Fatalf("unexpected error: %s", err.Error()) 625 } 626 627 if len(got) != 2 { 628 t.Fatalf("wrong result! got %d modules, expected 2", len(got)) 629 } 630 631 if got[0].Address != "module.child" || got[1].Address != "module.submodule" { 632 t.Fatalf("wrong result! got %#v\n", got) 633 } 634 635 } 636 637 func TestMarshalModules_nested(t *testing.T) { 638 childModule, _ := addrs.ParseModuleInstanceStr("module.child") 639 subModule, _ := addrs.ParseModuleInstanceStr("module.child.module.submodule") 640 testState := states.BuildState(func(s *states.SyncState) { 641 s.SetResourceInstanceCurrent( 642 addrs.Resource{ 643 Mode: addrs.ManagedResourceMode, 644 Type: "test_instance", 645 Name: "foo", 646 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 647 &states.ResourceInstanceObjectSrc{ 648 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 649 Status: states.ObjectReady, 650 }, 651 addrs.AbsProviderConfig{ 652 Provider: addrs.NewDefaultProvider("test"), 653 Module: addrs.RootModule, 654 }, 655 ) 656 s.SetResourceInstanceCurrent( 657 addrs.Resource{ 658 Mode: addrs.ManagedResourceMode, 659 Type: "test_instance", 660 Name: "foo", 661 }.Instance(addrs.NoKey).Absolute(childModule), 662 &states.ResourceInstanceObjectSrc{ 663 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 664 Status: states.ObjectReady, 665 }, 666 addrs.AbsProviderConfig{ 667 Provider: addrs.NewDefaultProvider("test"), 668 Module: childModule.Module(), 669 }, 670 ) 671 s.SetResourceInstanceCurrent( 672 addrs.Resource{ 673 Mode: addrs.ManagedResourceMode, 674 Type: "test_instance", 675 Name: "foo", 676 }.Instance(addrs.NoKey).Absolute(subModule), 677 &states.ResourceInstanceObjectSrc{ 678 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 679 Status: states.ObjectReady, 680 }, 681 addrs.AbsProviderConfig{ 682 Provider: addrs.NewDefaultProvider("test"), 683 Module: subModule.Module(), 684 }, 685 ) 686 }) 687 moduleMap := make(map[string][]addrs.ModuleInstance) 688 moduleMap[""] = []addrs.ModuleInstance{childModule} 689 moduleMap[childModule.String()] = []addrs.ModuleInstance{subModule} 690 691 got, err := marshalModules(testState, testSchemas(), moduleMap[""], moduleMap) 692 693 if err != nil { 694 t.Fatalf("unexpected error: %s", err.Error()) 695 } 696 697 if len(got) != 1 { 698 t.Fatalf("wrong result! got %d modules, expected 1", len(got)) 699 } 700 701 if got[0].Address != "module.child" { 702 t.Fatalf("wrong result! got %#v\n", got) 703 } 704 705 if got[0].ChildModules[0].Address != "module.child.module.submodule" { 706 t.Fatalf("wrong result! got %#v\n", got) 707 } 708 } 709 710 func TestMarshalModules_parent_no_resources(t *testing.T) { 711 subModule, _ := addrs.ParseModuleInstanceStr("module.child.module.submodule") 712 testState := states.BuildState(func(s *states.SyncState) { 713 s.SetResourceInstanceCurrent( 714 addrs.Resource{ 715 Mode: addrs.ManagedResourceMode, 716 Type: "test_instance", 717 Name: "foo", 718 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 719 &states.ResourceInstanceObjectSrc{ 720 AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), 721 Status: states.ObjectReady, 722 }, 723 addrs.AbsProviderConfig{ 724 Provider: addrs.NewDefaultProvider("test"), 725 Module: addrs.RootModule, 726 }, 727 ) 728 s.SetResourceInstanceCurrent( 729 addrs.Resource{ 730 Mode: addrs.ManagedResourceMode, 731 Type: "test_instance", 732 Name: "foo", 733 }.Instance(addrs.NoKey).Absolute(subModule), 734 &states.ResourceInstanceObjectSrc{ 735 AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), 736 Status: states.ObjectReady, 737 }, 738 addrs.AbsProviderConfig{ 739 Provider: addrs.NewDefaultProvider("test"), 740 Module: subModule.Module(), 741 }, 742 ) 743 }) 744 got, err := marshalRootModule(testState, testSchemas()) 745 746 if err != nil { 747 t.Fatalf("unexpected error: %s", err.Error()) 748 } 749 750 if len(got.ChildModules) != 1 { 751 t.Fatalf("wrong result! got %d modules, expected 1", len(got.ChildModules)) 752 } 753 754 if got.ChildModules[0].Address != "module.child" { 755 t.Fatalf("wrong result! got %#v\n", got) 756 } 757 758 if got.ChildModules[0].ChildModules[0].Address != "module.child.module.submodule" { 759 t.Fatalf("wrong result! got %#v\n", got) 760 } 761 } 762 763 func testSchemas() *terraform.Schemas { 764 return &terraform.Schemas{ 765 Providers: map[addrs.Provider]*terraform.ProviderSchema{ 766 addrs.NewDefaultProvider("test"): { 767 ResourceTypes: map[string]*configschema.Block{ 768 "test_thing": { 769 Attributes: map[string]*configschema.Attribute{ 770 "woozles": {Type: cty.String, Optional: true, Computed: true}, 771 "foozles": {Type: cty.String, Optional: true, Sensitive: true}, 772 }, 773 }, 774 "test_instance": { 775 Attributes: map[string]*configschema.Attribute{ 776 "id": {Type: cty.String, Optional: true, Computed: true}, 777 "foo": {Type: cty.String, Optional: true}, 778 "bar": {Type: cty.String, Optional: true}, 779 }, 780 }, 781 "test_map_attr": { 782 Attributes: map[string]*configschema.Attribute{ 783 "data": {Type: cty.Map(cty.String), Optional: true, Computed: true, Sensitive: true}, 784 }, 785 }, 786 }, 787 }, 788 }, 789 } 790 } 791 792 func TestSensitiveAsBool(t *testing.T) { 793 tests := []struct { 794 Input cty.Value 795 Want cty.Value 796 }{ 797 { 798 cty.StringVal("hello"), 799 cty.False, 800 }, 801 { 802 cty.NullVal(cty.String), 803 cty.False, 804 }, 805 { 806 cty.StringVal("hello").Mark(marks.Sensitive), 807 cty.True, 808 }, 809 { 810 cty.NullVal(cty.String).Mark(marks.Sensitive), 811 cty.True, 812 }, 813 814 { 815 cty.NullVal(cty.DynamicPseudoType).Mark(marks.Sensitive), 816 cty.True, 817 }, 818 { 819 cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})), 820 cty.False, 821 }, 822 { 823 cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})).Mark(marks.Sensitive), 824 cty.True, 825 }, 826 { 827 cty.DynamicVal, 828 cty.False, 829 }, 830 { 831 cty.DynamicVal.Mark(marks.Sensitive), 832 cty.True, 833 }, 834 835 { 836 cty.ListValEmpty(cty.String), 837 cty.EmptyTupleVal, 838 }, 839 { 840 cty.ListValEmpty(cty.String).Mark(marks.Sensitive), 841 cty.True, 842 }, 843 { 844 cty.ListVal([]cty.Value{ 845 cty.StringVal("hello"), 846 cty.StringVal("friend").Mark(marks.Sensitive), 847 }), 848 cty.TupleVal([]cty.Value{ 849 cty.False, 850 cty.True, 851 }), 852 }, 853 { 854 cty.SetValEmpty(cty.String), 855 cty.EmptyTupleVal, 856 }, 857 { 858 cty.SetValEmpty(cty.String).Mark(marks.Sensitive), 859 cty.True, 860 }, 861 { 862 cty.SetVal([]cty.Value{cty.StringVal("hello")}), 863 cty.TupleVal([]cty.Value{cty.False}), 864 }, 865 { 866 cty.SetVal([]cty.Value{cty.StringVal("hello").Mark(marks.Sensitive)}), 867 cty.True, 868 }, 869 { 870 cty.EmptyTupleVal.Mark(marks.Sensitive), 871 cty.True, 872 }, 873 { 874 cty.TupleVal([]cty.Value{ 875 cty.StringVal("hello"), 876 cty.StringVal("friend").Mark(marks.Sensitive), 877 }), 878 cty.TupleVal([]cty.Value{ 879 cty.False, 880 cty.True, 881 }), 882 }, 883 { 884 cty.MapValEmpty(cty.String), 885 cty.EmptyObjectVal, 886 }, 887 { 888 cty.MapValEmpty(cty.String).Mark(marks.Sensitive), 889 cty.True, 890 }, 891 { 892 cty.MapVal(map[string]cty.Value{ 893 "greeting": cty.StringVal("hello"), 894 "animal": cty.StringVal("horse"), 895 }), 896 cty.EmptyObjectVal, 897 }, 898 { 899 cty.MapVal(map[string]cty.Value{ 900 "greeting": cty.StringVal("hello"), 901 "animal": cty.StringVal("horse").Mark(marks.Sensitive), 902 }), 903 cty.ObjectVal(map[string]cty.Value{ 904 "animal": cty.True, 905 }), 906 }, 907 { 908 cty.MapVal(map[string]cty.Value{ 909 "greeting": cty.StringVal("hello"), 910 "animal": cty.StringVal("horse").Mark(marks.Sensitive), 911 }).Mark(marks.Sensitive), 912 cty.True, 913 }, 914 { 915 cty.EmptyObjectVal, 916 cty.EmptyObjectVal, 917 }, 918 { 919 cty.ObjectVal(map[string]cty.Value{ 920 "greeting": cty.StringVal("hello"), 921 "animal": cty.StringVal("horse"), 922 }), 923 cty.EmptyObjectVal, 924 }, 925 { 926 cty.ObjectVal(map[string]cty.Value{ 927 "greeting": cty.StringVal("hello"), 928 "animal": cty.StringVal("horse").Mark(marks.Sensitive), 929 }), 930 cty.ObjectVal(map[string]cty.Value{ 931 "animal": cty.True, 932 }), 933 }, 934 { 935 cty.ObjectVal(map[string]cty.Value{ 936 "greeting": cty.StringVal("hello"), 937 "animal": cty.StringVal("horse").Mark(marks.Sensitive), 938 }).Mark(marks.Sensitive), 939 cty.True, 940 }, 941 { 942 cty.ListVal([]cty.Value{ 943 cty.ObjectVal(map[string]cty.Value{ 944 "a": cty.UnknownVal(cty.String), 945 }), 946 cty.ObjectVal(map[string]cty.Value{ 947 "a": cty.StringVal("known").Mark(marks.Sensitive), 948 }), 949 }), 950 cty.TupleVal([]cty.Value{ 951 cty.EmptyObjectVal, 952 cty.ObjectVal(map[string]cty.Value{ 953 "a": cty.True, 954 }), 955 }), 956 }, 957 { 958 cty.ListVal([]cty.Value{ 959 cty.MapValEmpty(cty.String), 960 cty.MapVal(map[string]cty.Value{ 961 "a": cty.StringVal("known").Mark(marks.Sensitive), 962 }), 963 cty.MapVal(map[string]cty.Value{ 964 "a": cty.UnknownVal(cty.String), 965 }), 966 }), 967 cty.TupleVal([]cty.Value{ 968 cty.EmptyObjectVal, 969 cty.ObjectVal(map[string]cty.Value{ 970 "a": cty.True, 971 }), 972 cty.EmptyObjectVal, 973 }), 974 }, 975 { 976 cty.ObjectVal(map[string]cty.Value{ 977 "list": cty.UnknownVal(cty.List(cty.String)), 978 "set": cty.UnknownVal(cty.Set(cty.Bool)), 979 "tuple": cty.UnknownVal(cty.Tuple([]cty.Type{cty.String, cty.Number})), 980 "map": cty.UnknownVal(cty.Map(cty.String)), 981 "object": cty.UnknownVal(cty.Object(map[string]cty.Type{"a": cty.String})), 982 }), 983 cty.ObjectVal(map[string]cty.Value{ 984 "list": cty.EmptyTupleVal, 985 "set": cty.EmptyTupleVal, 986 "tuple": cty.EmptyTupleVal, 987 "map": cty.EmptyObjectVal, 988 "object": cty.EmptyObjectVal, 989 }), 990 }, 991 } 992 993 for _, test := range tests { 994 got := SensitiveAsBool(test.Input) 995 if !reflect.DeepEqual(got, test.Want) { 996 t.Errorf( 997 "wrong result\ninput: %#v\ngot: %#v\nwant: %#v", 998 test.Input, got, test.Want, 999 ) 1000 } 1001 } 1002 }