github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/test_test.go (about) 1 package command 2 3 import ( 4 "path" 5 "strings" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/mitchellh/cli" 10 "github.com/zclconf/go-cty/cty" 11 12 "github.com/terramate-io/tf/addrs" 13 testing_command "github.com/terramate-io/tf/command/testing" 14 "github.com/terramate-io/tf/command/views" 15 "github.com/terramate-io/tf/providers" 16 "github.com/terramate-io/tf/terminal" 17 ) 18 19 func TestTest(t *testing.T) { 20 tcs := map[string]struct { 21 override string 22 args []string 23 expected string 24 code int 25 skip bool 26 }{ 27 "simple_pass": { 28 expected: "1 passed, 0 failed.", 29 code: 0, 30 }, 31 "simple_pass_nested": { 32 expected: "1 passed, 0 failed.", 33 code: 0, 34 }, 35 "simple_pass_nested_alternate": { 36 args: []string{"-test-directory", "other"}, 37 expected: "1 passed, 0 failed.", 38 code: 0, 39 }, 40 "simple_pass_very_nested": { 41 args: []string{"-test-directory", "tests/subdir"}, 42 expected: "1 passed, 0 failed.", 43 code: 0, 44 }, 45 "simple_pass_very_nested_alternate": { 46 override: "simple_pass_very_nested", 47 args: []string{"-test-directory", "./tests/subdir"}, 48 expected: "1 passed, 0 failed.", 49 code: 0, 50 }, 51 "pass_with_locals": { 52 expected: "1 passed, 0 failed.", 53 code: 0, 54 }, 55 "pass_with_outputs": { 56 expected: "1 passed, 0 failed.", 57 code: 0, 58 }, 59 "pass_with_variables": { 60 expected: "2 passed, 0 failed.", 61 code: 0, 62 }, 63 "plan_then_apply": { 64 expected: "2 passed, 0 failed.", 65 code: 0, 66 }, 67 "expect_failures_checks": { 68 expected: "1 passed, 0 failed.", 69 code: 0, 70 }, 71 "expect_failures_inputs": { 72 expected: "1 passed, 0 failed.", 73 code: 0, 74 }, 75 "expect_failures_outputs": { 76 expected: "1 passed, 0 failed.", 77 code: 0, 78 }, 79 "expect_failures_resources": { 80 expected: "1 passed, 0 failed.", 81 code: 0, 82 }, 83 "multiple_files": { 84 expected: "2 passed, 0 failed", 85 code: 0, 86 }, 87 "multiple_files_with_filter": { 88 override: "multiple_files", 89 args: []string{"-filter=one.tftest.hcl"}, 90 expected: "1 passed, 0 failed", 91 code: 0, 92 }, 93 "variables": { 94 expected: "2 passed, 0 failed", 95 code: 0, 96 }, 97 "variables_overridden": { 98 override: "variables", 99 args: []string{"-var=input=foo"}, 100 expected: "1 passed, 1 failed", 101 code: 1, 102 }, 103 "simple_fail": { 104 expected: "0 passed, 1 failed.", 105 code: 1, 106 }, 107 "custom_condition_checks": { 108 expected: "0 passed, 1 failed.", 109 code: 1, 110 }, 111 "custom_condition_inputs": { 112 expected: "0 passed, 1 failed.", 113 code: 1, 114 }, 115 "custom_condition_outputs": { 116 expected: "0 passed, 1 failed.", 117 code: 1, 118 }, 119 "custom_condition_resources": { 120 expected: "0 passed, 1 failed.", 121 code: 1, 122 }, 123 "no_providers_in_main": { 124 expected: "1 passed, 0 failed", 125 code: 0, 126 }, 127 "default_variables": { 128 expected: "1 passed, 0 failed.", 129 code: 0, 130 }, 131 "undefined_variables": { 132 expected: "1 passed, 0 failed.", 133 code: 0, 134 }, 135 } 136 for name, tc := range tcs { 137 t.Run(name, func(t *testing.T) { 138 if tc.skip { 139 t.Skip() 140 } 141 142 file := name 143 if len(tc.override) > 0 { 144 file = tc.override 145 } 146 147 td := t.TempDir() 148 testCopyDir(t, testFixturePath(path.Join("test", file)), td) 149 defer testChdir(t, td)() 150 151 provider := testing_command.NewProvider(nil) 152 view, done := testView(t) 153 154 c := &TestCommand{ 155 Meta: Meta{ 156 testingOverrides: metaOverridesForProvider(provider.Provider), 157 View: view, 158 }, 159 } 160 161 code := c.Run(tc.args) 162 output := done(t) 163 164 if code != tc.code { 165 t.Errorf("expected status code %d but got %d", tc.code, code) 166 } 167 168 if !strings.Contains(output.Stdout(), tc.expected) { 169 t.Errorf("output didn't contain expected string:\n\n%s", output.All()) 170 } 171 172 if provider.ResourceCount() > 0 { 173 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 174 } 175 }) 176 } 177 } 178 179 func TestTest_Interrupt(t *testing.T) { 180 td := t.TempDir() 181 testCopyDir(t, testFixturePath(path.Join("test", "with_interrupt")), td) 182 defer testChdir(t, td)() 183 184 provider := testing_command.NewProvider(nil) 185 view, done := testView(t) 186 187 interrupt := make(chan struct{}) 188 provider.Interrupt = interrupt 189 190 c := &TestCommand{ 191 Meta: Meta{ 192 testingOverrides: metaOverridesForProvider(provider.Provider), 193 View: view, 194 ShutdownCh: interrupt, 195 }, 196 } 197 198 c.Run(nil) 199 output := done(t).All() 200 201 if !strings.Contains(output, "Interrupt received") { 202 t.Errorf("output didn't produce the right output:\n\n%s", output) 203 } 204 205 if provider.ResourceCount() > 0 { 206 // we asked for a nice stop in this one, so it should still have tidied everything up. 207 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 208 } 209 } 210 211 func TestTest_DoubleInterrupt(t *testing.T) { 212 td := t.TempDir() 213 testCopyDir(t, testFixturePath(path.Join("test", "with_double_interrupt")), td) 214 defer testChdir(t, td)() 215 216 provider := testing_command.NewProvider(nil) 217 view, done := testView(t) 218 219 interrupt := make(chan struct{}) 220 provider.Interrupt = interrupt 221 222 c := &TestCommand{ 223 Meta: Meta{ 224 testingOverrides: metaOverridesForProvider(provider.Provider), 225 View: view, 226 ShutdownCh: interrupt, 227 }, 228 } 229 230 c.Run(nil) 231 output := done(t).All() 232 233 if !strings.Contains(output, "Two interrupts received") { 234 t.Errorf("output didn't produce the right output:\n\n%s", output) 235 } 236 237 cleanupMessage := `Terraform was interrupted while executing main.tftest.hcl, and may not have 238 performed the expected cleanup operations. 239 240 Terraform has already created the following resources from the module under 241 test: 242 - test_resource.primary 243 - test_resource.secondary 244 - test_resource.tertiary` 245 246 // It's really important that the above message is printed, so we're testing 247 // for it specifically and making sure it contains all the resources. 248 if !strings.Contains(output, cleanupMessage) { 249 t.Errorf("output didn't produce the right output:\n\n%s", output) 250 } 251 252 // This time the test command shouldn't have cleaned up the resource because 253 // of the hard interrupt. 254 if provider.ResourceCount() != 3 { 255 // we asked for a nice stop in this one, so it should still have tidied everything up. 256 t.Errorf("should not have deleted all resources on completion but left %v", provider.ResourceString()) 257 } 258 } 259 260 func TestTest_ProviderAlias(t *testing.T) { 261 td := t.TempDir() 262 testCopyDir(t, testFixturePath(path.Join("test", "with_provider_alias")), td) 263 defer testChdir(t, td)() 264 265 provider := testing_command.NewProvider(nil) 266 267 providerSource, close := newMockProviderSource(t, map[string][]string{ 268 "test": {"1.0.0"}, 269 }) 270 defer close() 271 272 streams, done := terminal.StreamsForTesting(t) 273 view := views.NewView(streams) 274 ui := new(cli.MockUi) 275 276 meta := Meta{ 277 testingOverrides: metaOverridesForProvider(provider.Provider), 278 Ui: ui, 279 View: view, 280 Streams: streams, 281 ProviderSource: providerSource, 282 } 283 284 init := &InitCommand{ 285 Meta: meta, 286 } 287 288 if code := init.Run(nil); code != 0 { 289 t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter) 290 } 291 292 command := &TestCommand{ 293 Meta: meta, 294 } 295 296 code := command.Run(nil) 297 output := done(t) 298 299 printedOutput := false 300 301 if code != 0 { 302 printedOutput = true 303 t.Errorf("expected status code 0 but got %d: %s", code, output.All()) 304 } 305 306 if provider.ResourceCount() > 0 { 307 if !printedOutput { 308 t.Errorf("should have deleted all resources on completion but left %s\n\n%s", provider.ResourceString(), output.All()) 309 } else { 310 t.Errorf("should have deleted all resources on completion but left %s", provider.ResourceString()) 311 } 312 } 313 } 314 315 func TestTest_ModuleDependencies(t *testing.T) { 316 td := t.TempDir() 317 testCopyDir(t, testFixturePath(path.Join("test", "with_setup_module")), td) 318 defer testChdir(t, td)() 319 320 // Our two providers will share a common set of values to make things 321 // easier. 322 store := &testing_command.ResourceStore{ 323 Data: make(map[string]cty.Value), 324 } 325 326 // We set it up so the module provider will update the data sources 327 // available to the core mock provider. 328 test := testing_command.NewProvider(store) 329 setup := testing_command.NewProvider(store) 330 331 test.SetDataPrefix("data") 332 test.SetResourcePrefix("resource") 333 334 // Let's make the setup provider write into the data for test provider. 335 setup.SetResourcePrefix("data") 336 337 providerSource, close := newMockProviderSource(t, map[string][]string{ 338 "test": {"1.0.0"}, 339 "setup": {"1.0.0"}, 340 }) 341 defer close() 342 343 streams, done := terminal.StreamsForTesting(t) 344 view := views.NewView(streams) 345 ui := new(cli.MockUi) 346 347 meta := Meta{ 348 testingOverrides: &testingOverrides{ 349 Providers: map[addrs.Provider]providers.Factory{ 350 addrs.NewDefaultProvider("test"): providers.FactoryFixed(test.Provider), 351 addrs.NewDefaultProvider("setup"): providers.FactoryFixed(setup.Provider), 352 }, 353 }, 354 Ui: ui, 355 View: view, 356 Streams: streams, 357 ProviderSource: providerSource, 358 } 359 360 init := &InitCommand{ 361 Meta: meta, 362 } 363 364 if code := init.Run(nil); code != 0 { 365 t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter) 366 } 367 368 command := &TestCommand{ 369 Meta: meta, 370 } 371 372 code := command.Run(nil) 373 output := done(t) 374 375 printedOutput := false 376 377 if code != 0 { 378 printedOutput = true 379 t.Errorf("expected status code 0 but got %d: %s", code, output.All()) 380 } 381 382 if test.ResourceCount() > 0 { 383 if !printedOutput { 384 printedOutput = true 385 t.Errorf("should have deleted all resources on completion but left %s\n\n%s", test.ResourceString(), output.All()) 386 } else { 387 t.Errorf("should have deleted all resources on completion but left %s", test.ResourceString()) 388 } 389 } 390 391 if setup.ResourceCount() > 0 { 392 if !printedOutput { 393 t.Errorf("should have deleted all resources on completion but left %s\n\n%s", setup.ResourceString(), output.All()) 394 } else { 395 t.Errorf("should have deleted all resources on completion but left %s", setup.ResourceString()) 396 } 397 } 398 } 399 400 func TestTest_CatchesErrorsBeforeDestroy(t *testing.T) { 401 td := t.TempDir() 402 testCopyDir(t, testFixturePath(path.Join("test", "invalid_default_state")), td) 403 defer testChdir(t, td)() 404 405 provider := testing_command.NewProvider(nil) 406 view, done := testView(t) 407 408 c := &TestCommand{ 409 Meta: Meta{ 410 testingOverrides: metaOverridesForProvider(provider.Provider), 411 View: view, 412 }, 413 } 414 415 code := c.Run([]string{"-no-color"}) 416 output := done(t) 417 418 if code != 1 { 419 t.Errorf("expected status code 0 but got %d", code) 420 } 421 422 expectedOut := `main.tftest.hcl... fail 423 run "test"... fail 424 425 Failure! 0 passed, 1 failed. 426 ` 427 428 expectedErr := ` 429 Error: No value for required variable 430 431 on main.tf line 2: 432 2: variable "input" { 433 434 The root module input variable "input" is not set, and has no default value. 435 Use a -var or -var-file command line argument to provide a value for this 436 variable. 437 ` 438 439 actualOut := output.Stdout() 440 actualErr := output.Stderr() 441 442 if diff := cmp.Diff(actualOut, expectedOut); len(diff) > 0 { 443 t.Errorf("std out didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedOut, actualOut, diff) 444 } 445 446 if diff := cmp.Diff(actualErr, expectedErr); len(diff) > 0 { 447 t.Errorf("std err didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedErr, actualErr, diff) 448 } 449 450 if provider.ResourceCount() > 0 { 451 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 452 } 453 } 454 455 func TestTest_Verbose(t *testing.T) { 456 td := t.TempDir() 457 testCopyDir(t, testFixturePath(path.Join("test", "plan_then_apply")), td) 458 defer testChdir(t, td)() 459 460 provider := testing_command.NewProvider(nil) 461 view, done := testView(t) 462 463 c := &TestCommand{ 464 Meta: Meta{ 465 testingOverrides: metaOverridesForProvider(provider.Provider), 466 View: view, 467 }, 468 } 469 470 code := c.Run([]string{"-verbose", "-no-color"}) 471 output := done(t) 472 473 if code != 0 { 474 t.Errorf("expected status code 0 but got %d", code) 475 } 476 477 expected := `main.tftest.hcl... pass 478 run "validate_test_resource"... pass 479 480 Terraform used the selected providers to generate the following execution 481 plan. Resource actions are indicated with the following symbols: 482 + create 483 484 Terraform will perform the following actions: 485 486 # test_resource.foo will be created 487 + resource "test_resource" "foo" { 488 + id = "constant_value" 489 + value = "bar" 490 } 491 492 Plan: 1 to add, 0 to change, 0 to destroy. 493 run "validate_test_resource"... pass 494 # test_resource.foo: 495 resource "test_resource" "foo" { 496 id = "constant_value" 497 value = "bar" 498 } 499 500 Success! 2 passed, 0 failed. 501 ` 502 503 actual := output.All() 504 505 if diff := cmp.Diff(actual, expected); len(diff) > 0 { 506 t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) 507 } 508 509 if provider.ResourceCount() > 0 { 510 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 511 } 512 } 513 514 func TestTest_ValidatesBeforeExecution(t *testing.T) { 515 tcs := map[string]struct { 516 expectedOut string 517 expectedErr string 518 }{ 519 "invalid": { 520 expectedOut: `main.tftest.hcl... fail 521 run "invalid"... fail 522 523 Failure! 0 passed, 1 failed. 524 `, 525 expectedErr: ` 526 Error: Invalid ` + "`expect_failures`" + ` reference 527 528 on main.tftest.hcl line 5, in run "invalid": 529 5: local.my_value, 530 531 You cannot expect failures from local.my_value. You can only expect failures 532 from checkable objects such as input variables, output values, check blocks, 533 managed resources and data sources. 534 `, 535 }, 536 "invalid-module": { 537 expectedOut: `main.tftest.hcl... fail 538 run "invalid"... fail 539 run "test"... skip 540 541 Failure! 0 passed, 1 failed, 1 skipped. 542 `, 543 expectedErr: ` 544 Error: Reference to undeclared input variable 545 546 on setup/main.tf line 3, in resource "test_resource" "setup": 547 3: value = var.not_real // Oh no! 548 549 An input variable with the name "not_real" has not been declared. This 550 variable can be declared with a variable "not_real" {} block. 551 `, 552 }, 553 "missing-provider": { 554 expectedOut: `main.tftest.hcl... fail 555 run "passes_validation"... fail 556 557 Failure! 0 passed, 1 failed. 558 `, 559 expectedErr: ` 560 Error: Provider configuration not present 561 562 To work with test_resource.secondary its original provider configuration at 563 provider["registry.terraform.io/hashicorp/test"].secondary is required, but 564 it has been removed. This occurs when a provider configuration is removed 565 while objects created by that provider still exist in the state. Re-add the 566 provider configuration to destroy test_resource.secondary, after which you 567 can remove the provider configuration again. 568 `, 569 }, 570 "missing-provider-in-run-block": { 571 expectedOut: `main.tftest.hcl... fail 572 run "passes_validation"... fail 573 574 Failure! 0 passed, 1 failed. 575 `, 576 expectedErr: ` 577 Error: Provider configuration not present 578 579 To work with test_resource.secondary its original provider configuration at 580 provider["registry.terraform.io/hashicorp/test"].secondary is required, but 581 it has been removed. This occurs when a provider configuration is removed 582 while objects created by that provider still exist in the state. Re-add the 583 provider configuration to destroy test_resource.secondary, after which you 584 can remove the provider configuration again. 585 `, 586 }, 587 "missing-provider-in-test-module": { 588 expectedOut: `main.tftest.hcl... fail 589 run "passes_validation_primary"... pass 590 run "passes_validation_secondary"... fail 591 592 Failure! 1 passed, 1 failed. 593 `, 594 expectedErr: ` 595 Error: Provider configuration not present 596 597 To work with test_resource.secondary its original provider configuration at 598 provider["registry.terraform.io/hashicorp/test"].secondary is required, but 599 it has been removed. This occurs when a provider configuration is removed 600 while objects created by that provider still exist in the state. Re-add the 601 provider configuration to destroy test_resource.secondary, after which you 602 can remove the provider configuration again. 603 `, 604 }, 605 } 606 607 for file, tc := range tcs { 608 t.Run(file, func(t *testing.T) { 609 610 td := t.TempDir() 611 testCopyDir(t, testFixturePath(path.Join("test", file)), td) 612 defer testChdir(t, td)() 613 614 provider := testing_command.NewProvider(nil) 615 616 providerSource, close := newMockProviderSource(t, map[string][]string{ 617 "test": {"1.0.0"}, 618 }) 619 defer close() 620 621 streams, done := terminal.StreamsForTesting(t) 622 view := views.NewView(streams) 623 ui := new(cli.MockUi) 624 625 meta := Meta{ 626 testingOverrides: metaOverridesForProvider(provider.Provider), 627 Ui: ui, 628 View: view, 629 Streams: streams, 630 ProviderSource: providerSource, 631 } 632 633 init := &InitCommand{ 634 Meta: meta, 635 } 636 637 if code := init.Run(nil); code != 0 { 638 t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter) 639 } 640 641 c := &TestCommand{ 642 Meta: meta, 643 } 644 645 code := c.Run([]string{"-no-color"}) 646 output := done(t) 647 648 if code != 1 { 649 t.Errorf("expected status code 1 but got %d", code) 650 } 651 652 actualOut, expectedOut := output.Stdout(), tc.expectedOut 653 actualErr, expectedErr := output.Stderr(), tc.expectedErr 654 655 if diff := cmp.Diff(actualOut, expectedOut); len(diff) > 0 { 656 t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedOut, actualOut, diff) 657 } 658 659 if diff := cmp.Diff(actualErr, expectedErr); len(diff) > 0 { 660 t.Errorf("error didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedErr, actualErr, diff) 661 } 662 663 if provider.ResourceCount() > 0 { 664 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 665 } 666 }) 667 } 668 } 669 670 func TestTest_NestedSetupModules(t *testing.T) { 671 td := t.TempDir() 672 testCopyDir(t, testFixturePath(path.Join("test", "with_nested_setup_modules")), td) 673 defer testChdir(t, td)() 674 675 provider := testing_command.NewProvider(nil) 676 677 providerSource, close := newMockProviderSource(t, map[string][]string{ 678 "test": {"1.0.0"}, 679 }) 680 defer close() 681 682 streams, done := terminal.StreamsForTesting(t) 683 view := views.NewView(streams) 684 ui := new(cli.MockUi) 685 686 meta := Meta{ 687 testingOverrides: metaOverridesForProvider(provider.Provider), 688 Ui: ui, 689 View: view, 690 Streams: streams, 691 ProviderSource: providerSource, 692 } 693 694 init := &InitCommand{ 695 Meta: meta, 696 } 697 698 if code := init.Run(nil); code != 0 { 699 t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter) 700 } 701 702 command := &TestCommand{ 703 Meta: meta, 704 } 705 706 code := command.Run(nil) 707 output := done(t) 708 709 printedOutput := false 710 711 if code != 0 { 712 printedOutput = true 713 t.Errorf("expected status code 0 but got %d: %s", code, output.All()) 714 } 715 716 if provider.ResourceCount() > 0 { 717 if !printedOutput { 718 t.Errorf("should have deleted all resources on completion but left %s\n\n%s", provider.ResourceString(), output.All()) 719 } else { 720 t.Errorf("should have deleted all resources on completion but left %s", provider.ResourceString()) 721 } 722 } 723 } 724 725 func TestTest_StatePropagation(t *testing.T) { 726 td := t.TempDir() 727 testCopyDir(t, testFixturePath(path.Join("test", "state_propagation")), td) 728 defer testChdir(t, td)() 729 730 provider := testing_command.NewProvider(nil) 731 732 providerSource, close := newMockProviderSource(t, map[string][]string{ 733 "test": {"1.0.0"}, 734 }) 735 defer close() 736 737 streams, done := terminal.StreamsForTesting(t) 738 view := views.NewView(streams) 739 ui := new(cli.MockUi) 740 741 meta := Meta{ 742 testingOverrides: metaOverridesForProvider(provider.Provider), 743 Ui: ui, 744 View: view, 745 Streams: streams, 746 ProviderSource: providerSource, 747 } 748 749 init := &InitCommand{ 750 Meta: meta, 751 } 752 753 if code := init.Run(nil); code != 0 { 754 t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter) 755 } 756 757 c := &TestCommand{ 758 Meta: meta, 759 } 760 761 code := c.Run([]string{"-verbose", "-no-color"}) 762 output := done(t) 763 764 if code != 0 { 765 t.Errorf("expected status code 0 but got %d", code) 766 } 767 768 expected := `main.tftest.hcl... pass 769 run "initial_apply_example"... pass 770 # test_resource.module_resource: 771 resource "test_resource" "module_resource" { 772 id = "df6h8as9" 773 value = "start" 774 } 775 run "initial_apply"... pass 776 # test_resource.resource: 777 resource "test_resource" "resource" { 778 id = "598318e0" 779 value = "start" 780 } 781 run "plan_second_example"... pass 782 783 Terraform used the selected providers to generate the following execution 784 plan. Resource actions are indicated with the following symbols: 785 + create 786 787 Terraform will perform the following actions: 788 789 # test_resource.second_module_resource will be created 790 + resource "test_resource" "second_module_resource" { 791 + id = "b6a1d8cb" 792 + value = "start" 793 } 794 795 Plan: 1 to add, 0 to change, 0 to destroy. 796 run "plan_update"... pass 797 798 Terraform used the selected providers to generate the following execution 799 plan. Resource actions are indicated with the following symbols: 800 ~ update in-place 801 802 Terraform will perform the following actions: 803 804 # test_resource.resource will be updated in-place 805 ~ resource "test_resource" "resource" { 806 id = "598318e0" 807 ~ value = "start" -> "update" 808 } 809 810 Plan: 0 to add, 1 to change, 0 to destroy. 811 run "plan_update_example"... pass 812 813 Terraform used the selected providers to generate the following execution 814 plan. Resource actions are indicated with the following symbols: 815 ~ update in-place 816 817 Terraform will perform the following actions: 818 819 # test_resource.module_resource will be updated in-place 820 ~ resource "test_resource" "module_resource" { 821 id = "df6h8as9" 822 ~ value = "start" -> "update" 823 } 824 825 Plan: 0 to add, 1 to change, 0 to destroy. 826 827 Success! 5 passed, 0 failed. 828 ` 829 830 actual := output.All() 831 832 if diff := cmp.Diff(actual, expected); len(diff) > 0 { 833 t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) 834 } 835 836 if provider.ResourceCount() > 0 { 837 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 838 } 839 } 840 841 func TestTest_OnlyExternalModules(t *testing.T) { 842 td := t.TempDir() 843 testCopyDir(t, testFixturePath(path.Join("test", "only_modules")), td) 844 defer testChdir(t, td)() 845 846 provider := testing_command.NewProvider(nil) 847 848 providerSource, close := newMockProviderSource(t, map[string][]string{ 849 "test": {"1.0.0"}, 850 }) 851 defer close() 852 853 streams, done := terminal.StreamsForTesting(t) 854 view := views.NewView(streams) 855 ui := new(cli.MockUi) 856 857 meta := Meta{ 858 testingOverrides: metaOverridesForProvider(provider.Provider), 859 Ui: ui, 860 View: view, 861 Streams: streams, 862 ProviderSource: providerSource, 863 } 864 865 init := &InitCommand{ 866 Meta: meta, 867 } 868 869 if code := init.Run(nil); code != 0 { 870 t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter) 871 } 872 873 c := &TestCommand{ 874 Meta: meta, 875 } 876 877 code := c.Run([]string{"-no-color"}) 878 output := done(t) 879 880 if code != 0 { 881 t.Errorf("expected status code 0 but got %d", code) 882 } 883 884 expected := `main.tftest.hcl... pass 885 run "first"... pass 886 run "second"... pass 887 888 Success! 2 passed, 0 failed. 889 ` 890 891 actual := output.All() 892 893 if diff := cmp.Diff(actual, expected); len(diff) > 0 { 894 t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) 895 } 896 897 if provider.ResourceCount() > 0 { 898 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 899 } 900 } 901 902 func TestTest_PartialUpdates(t *testing.T) { 903 td := t.TempDir() 904 testCopyDir(t, testFixturePath(path.Join("test", "partial_updates")), td) 905 defer testChdir(t, td)() 906 907 provider := testing_command.NewProvider(nil) 908 view, done := testView(t) 909 910 c := &TestCommand{ 911 Meta: Meta{ 912 testingOverrides: metaOverridesForProvider(provider.Provider), 913 View: view, 914 }, 915 } 916 917 code := c.Run([]string{"-no-color"}) 918 output := done(t) 919 920 if code != 0 { 921 t.Errorf("expected status code 0 but got %d", code) 922 } 923 924 expected := `main.tftest.hcl... pass 925 run "first"... pass 926 927 Warning: Resource targeting is in effect 928 929 You are creating a plan with the -target option, which means that the result 930 of this plan may not represent all of the changes requested by the current 931 configuration. 932 933 The -target option is not for routine use, and is provided only for 934 exceptional situations such as recovering from errors or mistakes, or when 935 Terraform specifically suggests to use it as part of an error message. 936 937 Warning: Applied changes may be incomplete 938 939 The plan was created with the -target option in effect, so some changes 940 requested in the configuration may have been ignored and the output values 941 may not be fully updated. Run the following command to verify that no other 942 changes are pending: 943 terraform plan 944 945 Note that the -target option is not suitable for routine use, and is provided 946 only for exceptional situations such as recovering from errors or mistakes, 947 or when Terraform specifically suggests to use it as part of an error 948 message. 949 run "second"... pass 950 951 Success! 2 passed, 0 failed. 952 ` 953 954 actual := output.All() 955 956 if diff := cmp.Diff(actual, expected); len(diff) > 0 { 957 t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) 958 } 959 960 if provider.ResourceCount() > 0 { 961 t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString()) 962 } 963 }