// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package cloud import ( "context" "testing" "github.com/google/go-cmp/cmp" "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/command/jsonformat" "github.com/hashicorp/terraform/internal/command/views" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configload" "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/internal/terraform" "github.com/hashicorp/terraform/internal/tfdiags" ) func TestTest(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } if _, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }); err != nil { t.Fatalf("failed to create registry module: %v", err) } runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test", TestingDirectory: "tests", Config: nil, // We don't need this for this test. Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: context.Background(), CancelledCtx: context.Background(), // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: &jsonformat.Renderer{ Streams: streams, Colorize: colorize, RunningInAutomation: false, }, View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } _, diags := runner.Test(false) if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := done(t) actual := output.All() expected := `main.tftest.hcl... in progress defaults... pass overrides... pass main.tftest.hcl... tearing down main.tftest.hcl... pass Success! 2 passed, 0 failed. ` if diff := cmp.Diff(expected, actual); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) } } func TestTest_Parallelism(t *testing.T) { streams, _ := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } if _, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }); err != nil { t.Fatalf("failed to create registry module: %v", err) } runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test", TestingDirectory: "tests", Config: nil, // We don't need this for this test. Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: context.Background(), CancelledCtx: context.Background(), // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, OperationParallelism: 4, Filters: nil, // Outputs Renderer: &jsonformat.Renderer{ Streams: streams, Colorize: colorize, RunningInAutomation: false, }, View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } _, diags := runner.Test(false) if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } if mock.TestRuns.parallelism != 4 { t.Errorf("expected parallelism to be 4 but was %d", mock.TestRuns.parallelism) } } func TestTest_JSON(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } if _, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }); err != nil { t.Fatalf("failed to create registry module: %v", err) } runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test", TestingDirectory: "tests", Config: nil, // We don't need this for this test. Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: context.Background(), CancelledCtx: context.Background(), // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: nil, // This should force the logs to render as JSON. View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } _, diags := runner.Test(false) if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := done(t) actual := output.All() expected := `{"@level":"info","@message":"Terraform 1.6.0-dev","@module":"terraform.ui","@timestamp":"2023-09-12T08:29:27.257413+02:00","terraform":"1.6.0-dev","type":"version","ui":"1.2"} {"@level":"info","@message":"Found 1 file and 2 run blocks","@module":"terraform.ui","@timestamp":"2023-09-12T08:29:27.268731+02:00","test_abstract":{"main.tftest.hcl":["defaults","overrides"]},"type":"test_abstract"} {"@level":"info","@message":"main.tftest.hcl... in progress","@module":"terraform.ui","@testfile":"main.tftest.hcl","@timestamp":"2023-09-12T08:29:27.268889+02:00","test_file":{"path":"main.tftest.hcl","progress":"starting"},"type":"test_file"} {"@level":"info","@message":" \"defaults\"... pass","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"defaults","@timestamp":"2023-09-12T08:29:27.710541+02:00","test_run":{"path":"main.tftest.hcl","run":"defaults","progress":"complete","status":"pass"},"type":"test_run"} {"@level":"info","@message":" \"overrides\"... pass","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"overrides","@timestamp":"2023-09-12T08:29:27.833351+02:00","test_run":{"path":"main.tftest.hcl","run":"overrides","progress":"complete","status":"pass"},"type":"test_run"} {"@level":"info","@message":"main.tftest.hcl... tearing down","@module":"terraform.ui","@testfile":"main.tftest.hcl","@timestamp":"2023-09-12T08:29:27.833375+02:00","test_file":{"path":"main.tftest.hcl","progress":"teardown"},"type":"test_file"} {"@level":"info","@message":"main.tftest.hcl... pass","@module":"terraform.ui","@testfile":"main.tftest.hcl","@timestamp":"2023-09-12T08:29:27.956488+02:00","test_file":{"path":"main.tftest.hcl","progress":"complete","status":"pass"},"type":"test_file"} {"@level":"info","@message":"Success! 2 passed, 0 failed.","@module":"terraform.ui","@timestamp":"2023-09-12T08:29:27.956510+02:00","test_summary":{"status":"pass","passed":2,"failed":0,"errored":0,"skipped":0},"type":"test_summary"} ` if diff := cmp.Diff(expected, actual); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) } } func TestTest_Verbose(t *testing.T) { directory := "testdata/test-verbose" loader, close := configload.NewLoaderForTests(t) defer close() rootMod, hclDiags := loader.LoadRootModuleWithTests(directory, "tests") if hclDiags.HasErrors() { t.Fatalf("failed to load root module: %v", hclDiags.Error()) } config, configDiags := terraform.BuildConfigWithGraph( rootMod, loader.ModuleWalker(), nil, configs.MockDataLoaderFunc(loader.LoadExternalMockData), ) if configDiags.HasErrors() { t.Fatalf("failed to load config: %v", configDiags.Err()) } streams, done := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } if _, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }); err != nil { t.Fatalf("failed to create registry module: %v", err) } runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: directory, TestingDirectory: "tests", Config: config, Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: context.Background(), CancelledCtx: context.Background(), // The test options don't actually matter, as we just retrieve whatever // is set in the log file. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: &jsonformat.Renderer{ Streams: streams, Colorize: colorize, RunningInAutomation: false, }, View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } _, diags := runner.Test(false) if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := done(t) actual := output.All() expected := `main.tftest.hcl... in progress defaults... pass Changes to Outputs: + input = "Hello, world!" You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure. ╷ │ Warning: Deprecated │ │ with data.null_data_source.values, │ on main.tf line 7, in data "null_data_source" "values": │ 7: data "null_data_source" "values" { │ │ The null_data_source was historically used to construct intermediate values │ to re-use elsewhere in configuration, the same can now be achieved using │ locals ╵ ╷ │ Warning: Deprecated │ │ with data.null_data_source.values, │ on main.tf line 7, in data "null_data_source" "values": │ 7: data "null_data_source" "values" { │ │ The null_data_source was historically used to construct intermediate values │ to re-use elsewhere in configuration, the same can now be achieved using │ locals ╵ overrides... pass # data.null_data_source.values: data "null_data_source" "values" { has_computed_default = "default" id = "static" inputs = { "data" = "Hello, universe!" } outputs = { "data" = "Hello, universe!" } random = "8484833523059069761" } Outputs: input = "Hello, universe!" ╷ │ Warning: Deprecated │ │ with data.null_data_source.values, │ on main.tf line 7, in data "null_data_source" "values": │ 7: data "null_data_source" "values" { │ │ The null_data_source was historically used to construct intermediate values │ to re-use elsewhere in configuration, the same can now be achieved using │ locals ╵ ╷ │ Warning: Deprecated │ │ with data.null_data_source.values, │ on main.tf line 7, in data "null_data_source" "values": │ 7: data "null_data_source" "values" { │ │ The null_data_source was historically used to construct intermediate values │ to re-use elsewhere in configuration, the same can now be achieved using │ locals ╵ main.tftest.hcl... tearing down main.tftest.hcl... pass Success! 2 passed, 0 failed. ` if diff := cmp.Diff(expected, actual); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) } } func TestTest_Cancel(t *testing.T) { streams, outputFn := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } module, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }) if err != nil { t.Fatalf("failed to create registry module: %v", err) } doneContext, done := context.WithCancel(context.Background()) stopContext, stop := context.WithCancel(context.Background()) runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test-cancel", TestingDirectory: "tests", Config: nil, // We don't need this for this test. Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: stopContext, CancelledCtx: context.Background(), // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: &jsonformat.Renderer{ Streams: streams, Colorize: colorize, RunningInAutomation: false, }, View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } // We're only going to be able to finish this if the cancellation calls // are done correctly. mock.TestRuns.targetCancels = 1 var diags tfdiags.Diagnostics go func() { defer done() _, diags = runner.Test(false) }() stop() // immediately cancel // Wait for finish! <-doneContext.Done() if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := outputFn(t) actual := output.All() expected := `main.tftest.hcl... in progress Interrupt received. Please wait for Terraform to exit or data loss may occur. Gracefully shutting down... defaults... pass overrides... skip main.tftest.hcl... tearing down main.tftest.hcl... pass Success! 1 passed, 0 failed, 1 skipped. ` if diff := cmp.Diff(expected, actual); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) } // We want to make sure the cancel signal actually made it through. // Luckily we can access the test runs directly in the mock client. tr := mock.TestRuns.modules[module.ID][0] if tr.Status != tfe.TestRunCanceled { t.Errorf("expected test run to have been cancelled but was %s", tr.Status) } if mock.TestRuns.cancels != 1 { t.Errorf("incorrect number of cancels, expected 1 but was %d", mock.TestRuns.cancels) } } // TestTest_DelayedCancel just makes sure that if we trigger the cancellation // during the log reading stage then it still cancels properly. func TestTest_DelayedCancel(t *testing.T) { streams, outputFn := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } module, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }) if err != nil { t.Fatalf("failed to create registry module: %v", err) } doneContext, done := context.WithCancel(context.Background()) stopContext, stop := context.WithCancel(context.Background()) mock.TestRuns.delayedCancel = stop // We're only going to be able to finish this if the cancellation calls // are done correctly. mock.TestRuns.targetCancels = 1 runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test-cancel", TestingDirectory: "tests", Config: nil, // We don't need this for this test. Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: stopContext, CancelledCtx: context.Background(), // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: &jsonformat.Renderer{ Streams: streams, Colorize: colorize, RunningInAutomation: false, }, View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } var diags tfdiags.Diagnostics go func() { defer done() _, diags = runner.Test(false) }() // Wait for finish! <-doneContext.Done() if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := outputFn(t) actual := output.All() expected := `main.tftest.hcl... in progress Interrupt received. Please wait for Terraform to exit or data loss may occur. Gracefully shutting down... defaults... pass overrides... skip main.tftest.hcl... tearing down main.tftest.hcl... pass Success! 1 passed, 0 failed, 1 skipped. ` if diff := cmp.Diff(expected, actual); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) } // We want to make sure the cancel signal actually made it through. // Luckily we can access the test runs directly in the mock client. tr := mock.TestRuns.modules[module.ID][0] if tr.Status != tfe.TestRunCanceled { t.Errorf("expected test run to have been cancelled but was %s", tr.Status) } } func TestTest_ForceCancel(t *testing.T) { loader, close := configload.NewLoaderForTests(t) defer close() rootMod, hclDiags := loader.LoadRootModuleWithTests("testdata/test-force-cancel", "tests") if hclDiags.HasErrors() { t.Fatalf("failed to load root module: %v", hclDiags.Error()) } config, configDiags := terraform.BuildConfigWithGraph( rootMod, loader.ModuleWalker(), nil, configs.MockDataLoaderFunc(loader.LoadExternalMockData), ) if configDiags.HasErrors() { t.Fatalf("failed to load config: %v", configDiags.Err()) } streams, outputFn := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } module, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }) if err != nil { t.Fatalf("failed to create registry module: %v", err) } doneContext, done := context.WithCancel(context.Background()) stopContext, stop := context.WithCancel(context.Background()) cancelContext, cancel := context.WithCancel(context.Background()) runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test-force-cancel", TestingDirectory: "tests", Config: config, Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: stopContext, CancelledCtx: cancelContext, // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: &jsonformat.Renderer{ Streams: streams, Colorize: colorize, RunningInAutomation: false, }, View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } // We're only going to be able to finish this if the cancellation calls // are done correctly. mock.TestRuns.targetCancels = 2 var diags tfdiags.Diagnostics go func() { defer done() _, diags = runner.Test(false) }() stop() cancel() // Wait for finish! <-doneContext.Done() if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := outputFn(t) expectedErr := `Terraform was interrupted during test execution, and may not have performed the expected cleanup operations. Terraform was in the process of creating the following resources for "overrides" from the module under test, and they may not have been destroyed: - time_sleep.wait_5_seconds - tfcoremock_simple_resource.resource ` actualErr := output.Stderr() if diff := cmp.Diff(expectedErr, actualErr); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expectedErr, actualErr, diff) } actualOut := output.Stdout() expectedOut := `main.tftest.hcl... in progress defaults... pass Interrupt received. Please wait for Terraform to exit or data loss may occur. Gracefully shutting down... Two interrupts received. Exiting immediately. Note that data loss may have occurred. overrides... fail ╷ │ Error: Test interrupted │ │ The test operation could not be completed due to an interrupt signal. │ Please read the remaining diagnostics carefully for any sign of failed │ state cleanup or dangling resources. ╵ ╷ │ Error: Create time sleep error │ │ with time_sleep.wait_5_seconds, │ on main.tf line 7, in resource "time_sleep" "wait_5_seconds": │ 7: resource "time_sleep" "wait_5_seconds" { │ │ Original Error: context canceled ╵ ╷ │ Error: execution halted │ ╵ ╷ │ Error: execution halted │ ╵ main.tftest.hcl... tearing down main.tftest.hcl... fail Failure! 1 passed, 1 failed. ` if diff := cmp.Diff(expectedOut, actualOut); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expectedOut, actualOut, diff) } // We want to make sure the cancel signal actually made it through. // Luckily we can access the test runs directly in the mock client. tr := mock.TestRuns.modules[module.ID][0] if tr.Status != tfe.TestRunCanceled { t.Errorf("expected test run to have been cancelled but was %s", tr.Status) } if mock.TestRuns.cancels != 2 { t.Errorf("incorrect number of cancels, expected 2 but was %d", mock.TestRuns.cancels) } } func TestTest_LongRunningTest(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } if _, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }); err != nil { t.Fatalf("failed to create registry module: %v", err) } runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test-long-running", TestingDirectory: "tests", Config: nil, // We don't need this for this test. Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: context.Background(), CancelledCtx: context.Background(), // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: &jsonformat.Renderer{ Streams: streams, Colorize: colorize, RunningInAutomation: false, }, View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } _, diags := runner.Test(false) if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := done(t) actual := output.All() // The long running test logs actually contain additional progress updates, // but this test should ignore them and just show the usual output. expected := `main.tftest.hcl... in progress just_go... pass main.tftest.hcl... tearing down main.tftest.hcl... pass Success! 1 passed, 0 failed. ` if diff := cmp.Diff(expected, actual); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) } } func TestTest_LongRunningTestJSON(t *testing.T) { streams, done := terminal.StreamsForTesting(t) view := views.NewTest(arguments.ViewHuman, views.NewView(streams)) colorize := mockColorize() colorize.Disable = true mock := NewMockClient() client := &tfe.Client{ ConfigurationVersions: mock.ConfigurationVersions, Organizations: mock.Organizations, RegistryModules: mock.RegistryModules, TestRuns: mock.TestRuns, } if _, err := client.Organizations.Create(context.Background(), tfe.OrganizationCreateOptions{ Name: tfe.String("organisation"), }); err != nil { t.Fatalf("failed to create organisation: %v", err) } if _, err := client.RegistryModules.Create(context.Background(), "organisation", tfe.RegistryModuleCreateOptions{ Name: tfe.String("name"), Provider: tfe.String("provider"), RegistryName: "app.terraform.io", Namespace: "organisation", }); err != nil { t.Fatalf("failed to create registry module: %v", err) } runner := TestSuiteRunner{ // Configuration data. ConfigDirectory: "testdata/test-long-running", TestingDirectory: "tests", Config: nil, // We don't need this for this test. Source: "app.terraform.io/organisation/name/provider", // Cancellation controls, we won't be doing any cancellations in this // test. Stopped: false, Cancelled: false, StoppedCtx: context.Background(), CancelledCtx: context.Background(), // Test Options, empty for this test. GlobalVariables: nil, Verbose: false, Filters: nil, // Outputs Renderer: nil, // This should force the logs to render as JSON. View: view, Streams: streams, // Networking Services: nil, // Don't need this when the client is overridden. clientOverride: client, } _, diags := runner.Test(false) if len(diags) > 0 { t.Errorf("found diags and expected none: %s", diags.ErrWithWarnings()) } output := done(t) actual := output.All() // This test should still include the progress updates as we're doing the // JSON output. expected := `{"@level":"info","@message":"Terraform 1.7.0-dev","@module":"terraform.ui","@timestamp":"2023-09-28T14:57:09.175210+02:00","terraform":"1.7.0-dev","type":"version","ui":"1.2"} {"@level":"info","@message":"Found 1 file and 1 run block","@module":"terraform.ui","@timestamp":"2023-09-28T14:57:09.189212+02:00","test_abstract":{"main.tftest.hcl":["just_go"]},"type":"test_abstract"} {"@level":"info","@message":"main.tftest.hcl... in progress","@module":"terraform.ui","@testfile":"main.tftest.hcl","@timestamp":"2023-09-28T14:57:09.189386+02:00","test_file":{"path":"main.tftest.hcl","progress":"starting"},"type":"test_file"} {"@level":"info","@message":" \"just_go\"... in progress","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"just_go","@timestamp":"2023-09-28T14:57:09.189429+02:00","test_run":{"path":"main.tftest.hcl","run":"just_go","progress":"starting","elapsed":0},"type":"test_run"} {"@level":"info","@message":" \"just_go\"... in progress","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"just_go","@timestamp":"2023-09-28T14:57:11.341278+02:00","test_run":{"path":"main.tftest.hcl","run":"just_go","progress":"running","elapsed":2152},"type":"test_run"} {"@level":"info","@message":" \"just_go\"... in progress","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"just_go","@timestamp":"2023-09-28T14:57:13.343465+02:00","test_run":{"path":"main.tftest.hcl","run":"just_go","progress":"running","elapsed":4154},"type":"test_run"} {"@level":"info","@message":" \"just_go\"... pass","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"just_go","@timestamp":"2023-09-28T14:57:14.381552+02:00","test_run":{"path":"main.tftest.hcl","run":"just_go","progress":"complete","status":"pass"},"type":"test_run"} {"@level":"info","@message":"main.tftest.hcl... tearing down","@module":"terraform.ui","@testfile":"main.tftest.hcl","@timestamp":"2023-09-28T14:57:14.381655+02:00","test_file":{"path":"main.tftest.hcl","progress":"teardown"},"type":"test_file"} {"@level":"info","@message":" \"just_go\"... tearing down","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"just_go","@timestamp":"2023-09-28T14:57:14.381712+02:00","test_run":{"path":"main.tftest.hcl","run":"just_go","progress":"teardown","elapsed":0},"type":"test_run"} {"@level":"info","@message":" \"just_go\"... tearing down","@module":"terraform.ui","@testfile":"main.tftest.hcl","@testrun":"just_go","@timestamp":"2023-09-28T14:57:16.477705+02:00","test_run":{"path":"main.tftest.hcl","run":"just_go","progress":"teardown","elapsed":2096},"type":"test_run"} {"@level":"info","@message":"main.tftest.hcl... pass","@module":"terraform.ui","@testfile":"main.tftest.hcl","@timestamp":"2023-09-28T14:57:17.517309+02:00","test_file":{"path":"main.tftest.hcl","progress":"complete","status":"pass"},"type":"test_file"} {"@level":"info","@message":"Success! 1 passed, 0 failed.","@module":"terraform.ui","@timestamp":"2023-09-28T14:57:17.517494+02:00","test_summary":{"status":"pass","passed":1,"failed":0,"errored":0,"skipped":0},"type":"test_summary"} ` if diff := cmp.Diff(expected, actual); len(diff) > 0 { t.Errorf("expected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff) } }