diff --git a/internal/command/test.go b/internal/command/test.go index abcea6148c63..1af06d3e4808 100644 --- a/internal/command/test.go +++ b/internal/command/test.go @@ -130,21 +130,37 @@ func (c *TestCommand) Run(rawArgs []string) int { return 1 } - // Per file, ensure backends aren't reused - var duplicateBackendDiags tfdiags.Diagnostics + // Per file, ensure backends: + // * aren't reused + // * are valid types + var backendDiags tfdiags.Diagnostics for _, tf := range config.Module.Tests { bucketHashes := make(map[int]string) // Use an ordered list of backends, so that errors are raised by 2nd+ time // that a backend config is used in a file. for _, bc := range orderBackendsByDeclarationLine(tf.BackendConfigs) { f := backendInit.Backend(bc.Backend.Type) + if f == nil { + detail := fmt.Sprintf("There is no backend type named %q.", bc.Backend.Type) + if msg, removed := backendInit.RemovedBackends[bc.Backend.Type]; removed { + detail = msg + } + backendDiags = backendDiags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Unsupported backend type", + Detail: detail, + Subject: &bc.Backend.TypeRange, + }) + continue + } + b := f() schema := b.ConfigSchema() hash := bc.Backend.Hash(schema) if runName, exists := bucketHashes[hash]; exists { // This backend's been encountered before - duplicateBackendDiags = duplicateBackendDiags.Append( + backendDiags = backendDiags.Append( &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Repeat use of the same backend block", @@ -157,8 +173,8 @@ func (c *TestCommand) Run(rawArgs []string) int { bucketHashes[bc.Backend.Hash(schema)] = bc.Run.Name } } - diags = diags.Append(duplicateBackendDiags) - if duplicateBackendDiags.HasErrors() { + diags = diags.Append(backendDiags) + if backendDiags.HasErrors() { view.Diagnostics(nil, nil, diags) return 1 } diff --git a/internal/command/test_test.go b/internal/command/test_test.go index 85abfc7fda2a..50de443d606f 100644 --- a/internal/command/test_test.go +++ b/internal/command/test_test.go @@ -2564,12 +2564,13 @@ Failure! 0 passed, 1 failed. } } -// TestTest_ReusedBackendConfiguration asserts that it's not valid to re-use the same backend config (i.e the same state file) -// in parallel runs. This would result in multiple actions attempting to set state, potentially with different resource configurations. +// TestTest_ValidateBackendConfiguration tests validation of how backends are declared in test files: +// * it's not valid to re-use the same backend config (i.e the same state file) +// * it's not valid to use a deprecated backend type +// * it's not valid to use a non-existent backend type // -// Note - this test is written to assert that diagnostics are returned about re-used backend blocks between run blocks, but it allows either -// of the conflicting run blocks to cause the error to be raised. This is because run blocks without matching state keys aren't run in a -// deterministic order. +// Backend validaton performed in the command package is dependent on the internal/backend/init package, +// which cannot be imported in configuration parsing packages without creating an import cycle. func TestTest_ReusedBackendConfiguration(t *testing.T) { testCases := map[string]struct { @@ -2600,6 +2601,28 @@ Error: Repeat use of the same backend block The run "test_2" contains a backend configuration that's already been used in run "test_1". Sharing the same backend configuration between separate runs will result in conflicting state updates. +`, + }, + "validation detects when a deprecated backend type is used": { + dirName: "removed-backend-type", + expectErr: ` +Error: Unsupported backend type + + on main.tftest.hcl line 7, in run "test_removed_backend": + 7: backend "etcd" { + +The "etcd" backend is not supported in Terraform v1.3 or later. +`, + }, + "validation detects when a non-existent backend type": { + dirName: "non-existent-backend-type", + expectErr: ` +Error: Unsupported backend type + + on main.tftest.hcl line 7, in run "test_invalid_backend": + 7: backend "foobar" { + +There is no backend type named "foobar". `, }, } diff --git a/internal/command/testdata/test/non-existent-backend-type/main.tf b/internal/command/testdata/test/non-existent-backend-type/main.tf new file mode 100644 index 000000000000..6c4306eacb9a --- /dev/null +++ b/internal/command/testdata/test/non-existent-backend-type/main.tf @@ -0,0 +1,10 @@ + +variable "input" { + type = string +} + +resource "test_resource" "a" { + value = var.input +} + +resource "test_resource" "c" {} diff --git a/internal/command/testdata/test/non-existent-backend-type/main.tftest.hcl b/internal/command/testdata/test/non-existent-backend-type/main.tftest.hcl new file mode 100644 index 000000000000..06de286b5e7c --- /dev/null +++ b/internal/command/testdata/test/non-existent-backend-type/main.tftest.hcl @@ -0,0 +1,9 @@ +# The "foobar" backend does not exist and isn't a removed backend either +run "test_invalid_backend" { + variables { + input = "foobar" + } + + backend "foobar" { + } +} diff --git a/internal/command/testdata/test/removed-backend-type/main.tf b/internal/command/testdata/test/removed-backend-type/main.tf new file mode 100644 index 000000000000..6c4306eacb9a --- /dev/null +++ b/internal/command/testdata/test/removed-backend-type/main.tf @@ -0,0 +1,10 @@ + +variable "input" { + type = string +} + +resource "test_resource" "a" { + value = var.input +} + +resource "test_resource" "c" {} diff --git a/internal/command/testdata/test/removed-backend-type/main.tftest.hcl b/internal/command/testdata/test/removed-backend-type/main.tftest.hcl new file mode 100644 index 000000000000..b713d0ee5da4 --- /dev/null +++ b/internal/command/testdata/test/removed-backend-type/main.tftest.hcl @@ -0,0 +1,9 @@ +# The "etcd" backend has been removed from Terraform versions 1.3+ +run "test_removed_backend" { + variables { + input = "foobar" + } + + backend "etcd" { + } +}