mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
1467 lines
38 KiB
Go
1467 lines
38 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package command
|
|
|
|
import (
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/mitchellh/cli"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
testing_command "github.com/opentofu/opentofu/internal/command/testing"
|
|
"github.com/opentofu/opentofu/internal/command/views"
|
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
|
"github.com/opentofu/opentofu/internal/providers"
|
|
"github.com/opentofu/opentofu/internal/terminal"
|
|
)
|
|
|
|
func TestTest(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
override string
|
|
args []string
|
|
expected string
|
|
code int
|
|
skip bool
|
|
}{
|
|
"simple_pass": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"simple_pass_nested": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"simple_pass_nested_alternate": {
|
|
args: []string{"-test-directory", "other"},
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"simple_pass_very_nested": {
|
|
args: []string{"-test-directory", "tests/subdir"},
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"simple_pass_very_nested_alternate": {
|
|
override: "simple_pass_very_nested",
|
|
args: []string{"-test-directory", "./tests/subdir"},
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"pass_with_locals": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"pass_with_outputs": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"pass_with_variables": {
|
|
expected: "2 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"plan_then_apply": {
|
|
expected: "2 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"expect_failures_checks": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"expect_failures_inputs": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"expect_failures_outputs": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"expect_runtime_check_fail": {
|
|
expected: "0 passed, 1 failed.",
|
|
code: 1,
|
|
},
|
|
"expect_runtime_check_pass_with_expect": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"expect_runtime_check_pass_command_plan_expected": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"expect_runtime_check_fail_command_plan": {
|
|
expected: "0 passed, 1 failed.",
|
|
code: 1,
|
|
},
|
|
"expect_failures_resources": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"multiple_files": {
|
|
expected: "2 passed, 0 failed",
|
|
code: 0,
|
|
},
|
|
"multiple_files_with_filter": {
|
|
override: "multiple_files",
|
|
args: []string{"-filter=one.tftest.hcl"},
|
|
expected: "1 passed, 0 failed",
|
|
code: 0,
|
|
},
|
|
"variables": {
|
|
expected: "2 passed, 0 failed",
|
|
code: 0,
|
|
},
|
|
"variables_overridden": {
|
|
override: "variables",
|
|
args: []string{"-var=input=foo"},
|
|
expected: "1 passed, 1 failed",
|
|
code: 1,
|
|
},
|
|
"simple_fail": {
|
|
expected: "0 passed, 1 failed.",
|
|
code: 1,
|
|
},
|
|
"custom_condition_checks": {
|
|
expected: "0 passed, 1 failed.",
|
|
code: 1,
|
|
},
|
|
"custom_condition_inputs": {
|
|
expected: "0 passed, 1 failed.",
|
|
code: 1,
|
|
},
|
|
"custom_condition_outputs": {
|
|
expected: "0 passed, 1 failed.",
|
|
code: 1,
|
|
},
|
|
"custom_condition_resources": {
|
|
expected: "0 passed, 1 failed.",
|
|
code: 1,
|
|
},
|
|
"no_providers_in_main": {
|
|
expected: "1 passed, 0 failed",
|
|
code: 0,
|
|
},
|
|
"default_variables": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"undefined_variables": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"refresh_only": {
|
|
expected: "3 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"null_output": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"pass_with_tests_dir_variables": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
"override_with_tests_dir_variables": {
|
|
expected: "1 passed, 0 failed.",
|
|
code: 0,
|
|
},
|
|
}
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
file := name
|
|
if len(tc.override) > 0 {
|
|
file = tc.override
|
|
}
|
|
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
view, done := testView(t)
|
|
|
|
c := &TestCommand{
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
code := c.Run(tc.args)
|
|
output := done(t)
|
|
|
|
if code != tc.code {
|
|
t.Errorf("expected status code %d but got %d", tc.code, code)
|
|
}
|
|
|
|
if !strings.Contains(output.Stdout(), tc.expected) {
|
|
t.Errorf("output didn't contain expected string:\n\n%s", output.All())
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
func TestTest_Full_Output(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
override string
|
|
args []string
|
|
expected string
|
|
code int
|
|
skip bool
|
|
}{
|
|
"broken_no_valid_hcl": {
|
|
expected: "Unsupported block type",
|
|
code: 1,
|
|
},
|
|
"expect_runtime_check_fail_command_plan": {
|
|
expected: "Check block assertion known after apply",
|
|
code: 1,
|
|
},
|
|
"broken_wrong_block_resource": {
|
|
expected: "Blocks of type \"resource\" are not expected here.",
|
|
code: 1,
|
|
},
|
|
"broken_wrong_block_data": {
|
|
expected: "Blocks of type \"data\" are not expected here.",
|
|
code: 1,
|
|
},
|
|
"broken_wrong_block_output": {
|
|
expected: "Blocks of type \"output\" are not expected here.",
|
|
code: 1,
|
|
},
|
|
"broken_wrong_block_check": {
|
|
expected: "Blocks of type \"check\" are not expected here.",
|
|
code: 1,
|
|
},
|
|
"not_exists_output": {
|
|
expected: "Error: Reference to undeclared output value",
|
|
args: []string{"-no-color"},
|
|
code: 1,
|
|
},
|
|
"refresh_conflicting_config": {
|
|
expected: "Incompatible plan options",
|
|
code: 1,
|
|
},
|
|
"is_sorted": {
|
|
expected: "1.tftest.hcl... pass\n run \"a\"... pass\n2.tftest.hcl... pass\n run \"b\"... pass\n3.tftest.hcl... pass\n run \"c\"... pass",
|
|
code: 0,
|
|
args: []string{"-no-color"},
|
|
},
|
|
}
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
file := name
|
|
if len(tc.override) > 0 {
|
|
file = tc.override
|
|
}
|
|
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
view, done := testView(t)
|
|
|
|
c := &TestCommand{
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
code := c.Run(tc.args)
|
|
output := done(t)
|
|
|
|
if code != tc.code {
|
|
t.Errorf("expected status code %d but got %d", tc.code, code)
|
|
}
|
|
|
|
if !strings.Contains(output.All(), tc.expected) {
|
|
t.Errorf("output didn't contain expected string:\n\n%s \n\n----\n\nexpected: %s", output.All(), tc.expected)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTest_Interrupt(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", "with_interrupt")), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
view, done := testView(t)
|
|
|
|
interrupt := make(chan struct{})
|
|
provider.Interrupt = interrupt
|
|
|
|
c := &TestCommand{
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
View: view,
|
|
ShutdownCh: interrupt,
|
|
},
|
|
}
|
|
|
|
c.Run(nil)
|
|
output := done(t).All()
|
|
|
|
if !strings.Contains(output, "Interrupt received") {
|
|
t.Errorf("output didn't produce the right output:\n\n%s", output)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
// we asked for a nice stop in this one, so it should still have tidied everything up.
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
}
|
|
|
|
func TestTest_DoubleInterrupt(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", "with_double_interrupt")), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
view, done := testView(t)
|
|
|
|
interrupt := make(chan struct{})
|
|
provider.Interrupt = interrupt
|
|
|
|
c := &TestCommand{
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
View: view,
|
|
ShutdownCh: interrupt,
|
|
},
|
|
}
|
|
|
|
c.Run(nil)
|
|
output := done(t).All()
|
|
|
|
if !strings.Contains(output, "Two interrupts received") {
|
|
t.Errorf("output didn't produce the right output:\n\n%s", output)
|
|
}
|
|
|
|
cleanupMessage := `OpenTofu was interrupted while executing main.tftest.hcl, and may not have
|
|
performed the expected cleanup operations.
|
|
|
|
OpenTofu has already created the following resources from the module under
|
|
test:
|
|
- test_resource.primary
|
|
- test_resource.secondary
|
|
- test_resource.tertiary`
|
|
|
|
// It's really important that the above message is printed, so we're testing
|
|
// for it specifically and making sure it contains all the resources.
|
|
if !strings.Contains(output, cleanupMessage) {
|
|
t.Errorf("output didn't produce the right output:\n\n%s", output)
|
|
}
|
|
|
|
// This time the test command shouldn't have cleaned up the resource because
|
|
// of the hard interrupt.
|
|
if provider.ResourceCount() != 3 {
|
|
// we asked for a nice stop in this one, so it should still have tidied everything up.
|
|
t.Errorf("should not have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
}
|
|
|
|
func TestTest_ProviderAlias(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", "with_provider_alias")), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
|
|
providerSource, close := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer close()
|
|
|
|
streams, done := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
|
|
command := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
code := command.Run(nil)
|
|
output := done(t)
|
|
|
|
printedOutput := false
|
|
|
|
if code != 0 {
|
|
printedOutput = true
|
|
t.Errorf("expected status code 0 but got %d: %s", code, output.All())
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
if !printedOutput {
|
|
t.Errorf("should have deleted all resources on completion but left %s\n\n%s", provider.ResourceString(), output.All())
|
|
} else {
|
|
t.Errorf("should have deleted all resources on completion but left %s", provider.ResourceString())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTest_ModuleDependencies(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", "with_setup_module")), td)
|
|
defer testChdir(t, td)()
|
|
|
|
// Our two providers will share a common set of values to make things
|
|
// easier.
|
|
store := &testing_command.ResourceStore{
|
|
Data: make(map[string]cty.Value),
|
|
}
|
|
|
|
// We set it up so the module provider will update the data sources
|
|
// available to the core mock provider.
|
|
test := testing_command.NewProvider(store)
|
|
setup := testing_command.NewProvider(store)
|
|
|
|
test.SetDataPrefix("data")
|
|
test.SetResourcePrefix("resource")
|
|
|
|
// Let's make the setup provider write into the data for test provider.
|
|
setup.SetResourcePrefix("data")
|
|
|
|
providerSource, close := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
"setup": {"1.0.0"},
|
|
})
|
|
defer close()
|
|
|
|
streams, done := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
|
|
meta := Meta{
|
|
testingOverrides: &testingOverrides{
|
|
Providers: map[addrs.Provider]providers.Factory{
|
|
addrs.NewDefaultProvider("test"): providers.FactoryFixed(test.Provider),
|
|
addrs.NewDefaultProvider("setup"): providers.FactoryFixed(setup.Provider),
|
|
},
|
|
},
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
|
|
command := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
code := command.Run(nil)
|
|
output := done(t)
|
|
|
|
printedOutput := false
|
|
|
|
if code != 0 {
|
|
printedOutput = true
|
|
t.Errorf("expected status code 0 but got %d: %s", code, output.All())
|
|
}
|
|
|
|
if test.ResourceCount() > 0 {
|
|
if !printedOutput {
|
|
printedOutput = true
|
|
t.Errorf("should have deleted all resources on completion but left %s\n\n%s", test.ResourceString(), output.All())
|
|
} else {
|
|
t.Errorf("should have deleted all resources on completion but left %s", test.ResourceString())
|
|
}
|
|
}
|
|
|
|
if setup.ResourceCount() > 0 {
|
|
if !printedOutput {
|
|
t.Errorf("should have deleted all resources on completion but left %s\n\n%s", setup.ResourceString(), output.All())
|
|
} else {
|
|
t.Errorf("should have deleted all resources on completion but left %s", setup.ResourceString())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTest_CatchesErrorsBeforeDestroy(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", "invalid_default_state")), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
view, done := testView(t)
|
|
|
|
c := &TestCommand{
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
code := c.Run([]string{"-no-color"})
|
|
output := done(t)
|
|
|
|
if code != 1 {
|
|
t.Errorf("expected status code 0 but got %d", code)
|
|
}
|
|
|
|
expectedOut := `main.tftest.hcl... fail
|
|
run "test"... fail
|
|
|
|
Failure! 0 passed, 1 failed.
|
|
`
|
|
|
|
expectedErr := `
|
|
Error: No value for required variable
|
|
|
|
on main.tf line 2:
|
|
2: variable "input" {
|
|
|
|
The root module input variable "input" is not set, and has no default value.
|
|
Use a -var or -var-file command line argument to provide a value for this
|
|
variable.
|
|
`
|
|
|
|
actualOut := output.Stdout()
|
|
actualErr := output.Stderr()
|
|
|
|
if diff := cmp.Diff(actualOut, expectedOut); len(diff) > 0 {
|
|
t.Errorf("std out didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedOut, actualOut, diff)
|
|
}
|
|
|
|
if diff := cmp.Diff(actualErr, expectedErr); len(diff) > 0 {
|
|
t.Errorf("std err didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedErr, actualErr, diff)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
}
|
|
|
|
func TestTest_Verbose(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", "plan_then_apply")), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
view, done := testView(t)
|
|
|
|
c := &TestCommand{
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
code := c.Run([]string{"-verbose", "-no-color"})
|
|
output := done(t)
|
|
|
|
if code != 0 {
|
|
t.Errorf("expected status code 0 but got %d", code)
|
|
}
|
|
|
|
expected := `main.tftest.hcl... pass
|
|
run "validate_test_resource"... pass
|
|
|
|
OpenTofu used the selected providers to generate the following execution
|
|
plan. Resource actions are indicated with the following symbols:
|
|
+ create
|
|
|
|
OpenTofu will perform the following actions:
|
|
|
|
# test_resource.foo will be created
|
|
+ resource "test_resource" "foo" {
|
|
+ id = "constant_value"
|
|
+ value = "bar"
|
|
}
|
|
|
|
Plan: 1 to add, 0 to change, 0 to destroy.
|
|
run "validate_test_resource"... pass
|
|
# test_resource.foo:
|
|
resource "test_resource" "foo" {
|
|
id = "constant_value"
|
|
value = "bar"
|
|
}
|
|
|
|
Success! 2 passed, 0 failed.
|
|
`
|
|
|
|
actual := output.All()
|
|
|
|
if diff := cmp.Diff(actual, expected); len(diff) > 0 {
|
|
t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
}
|
|
|
|
func TestTest_ValidatesBeforeExecution(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
expectedOut string
|
|
expectedErr string
|
|
}{
|
|
"invalid": {
|
|
expectedOut: `main.tftest.hcl... fail
|
|
run "invalid"... fail
|
|
|
|
Failure! 0 passed, 1 failed.
|
|
`,
|
|
expectedErr: `
|
|
Error: Invalid ` + "`expect_failures`" + ` reference
|
|
|
|
on main.tftest.hcl line 5, in run "invalid":
|
|
5: local.my_value,
|
|
|
|
You cannot expect failures from local.my_value. You can only expect failures
|
|
from checkable objects such as input variables, output values, check blocks,
|
|
managed resources and data sources.
|
|
`,
|
|
},
|
|
"invalid-module": {
|
|
expectedOut: `main.tftest.hcl... fail
|
|
run "invalid"... fail
|
|
run "test"... skip
|
|
|
|
Failure! 0 passed, 1 failed, 1 skipped.
|
|
`,
|
|
expectedErr: `
|
|
Error: Reference to undeclared input variable
|
|
|
|
on setup/main.tf line 3, in resource "test_resource" "setup":
|
|
3: value = var.not_real // Oh no!
|
|
|
|
An input variable with the name "not_real" has not been declared. This
|
|
variable can be declared with a variable "not_real" {} block.
|
|
`,
|
|
},
|
|
"missing-provider": {
|
|
expectedOut: `main.tftest.hcl... fail
|
|
run "passes_validation"... fail
|
|
|
|
Failure! 0 passed, 1 failed.
|
|
`,
|
|
expectedErr: `
|
|
Error: Provider configuration not present
|
|
|
|
To work with test_resource.secondary its original provider configuration at
|
|
provider["registry.opentofu.org/hashicorp/test"].secondary is required, but
|
|
it has been removed. This occurs when a provider configuration is removed
|
|
while objects created by that provider still exist in the state. Re-add the
|
|
provider configuration to destroy test_resource.secondary, after which you
|
|
can remove the provider configuration again.
|
|
`,
|
|
},
|
|
"missing-provider-in-run-block": {
|
|
expectedOut: `main.tftest.hcl... fail
|
|
run "passes_validation"... fail
|
|
|
|
Failure! 0 passed, 1 failed.
|
|
`,
|
|
expectedErr: `
|
|
Error: Provider configuration not present
|
|
|
|
To work with test_resource.secondary its original provider configuration at
|
|
provider["registry.opentofu.org/hashicorp/test"].secondary is required, but
|
|
it has been removed. This occurs when a provider configuration is removed
|
|
while objects created by that provider still exist in the state. Re-add the
|
|
provider configuration to destroy test_resource.secondary, after which you
|
|
can remove the provider configuration again.
|
|
`,
|
|
},
|
|
"missing-provider-in-test-module": {
|
|
expectedOut: `main.tftest.hcl... fail
|
|
run "passes_validation_primary"... pass
|
|
run "passes_validation_secondary"... fail
|
|
|
|
Failure! 1 passed, 1 failed.
|
|
`,
|
|
expectedErr: `
|
|
Error: Provider configuration not present
|
|
|
|
To work with test_resource.secondary its original provider configuration at
|
|
provider["registry.opentofu.org/hashicorp/test"].secondary is required, but
|
|
it has been removed. This occurs when a provider configuration is removed
|
|
while objects created by that provider still exist in the state. Re-add the
|
|
provider configuration to destroy test_resource.secondary, after which you
|
|
can remove the provider configuration again.
|
|
`,
|
|
},
|
|
}
|
|
|
|
for file, tc := range tcs {
|
|
t.Run(file, func(t *testing.T) {
|
|
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
|
|
providerSource, close := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer close()
|
|
|
|
streams, done := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
|
|
c := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
code := c.Run([]string{"-no-color"})
|
|
output := done(t)
|
|
|
|
if code != 1 {
|
|
t.Errorf("expected status code 1 but got %d", code)
|
|
}
|
|
|
|
actualOut, expectedOut := output.Stdout(), tc.expectedOut
|
|
actualErr, expectedErr := output.Stderr(), tc.expectedErr
|
|
|
|
if diff := cmp.Diff(actualOut, expectedOut); len(diff) > 0 {
|
|
t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedOut, actualOut, diff)
|
|
}
|
|
|
|
if diff := cmp.Diff(actualErr, expectedErr); len(diff) > 0 {
|
|
t.Errorf("error didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedErr, actualErr, diff)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTest_Modules(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
expected string
|
|
code int
|
|
skip bool
|
|
}{
|
|
"pass_module_with_no_resource": {
|
|
expected: "main.tftest.hcl... pass\n run \"run\"... pass\n\nSuccess! 1 passed, 0 failed.\n",
|
|
code: 0,
|
|
},
|
|
"with_nested_setup_modules": {
|
|
expected: "main.tftest.hcl... pass\n run \"load_module\"... pass\n\nSuccess! 1 passed, 0 failed.\n",
|
|
code: 0,
|
|
},
|
|
"with_verify_module": {
|
|
expected: "main.tftest.hcl... pass\n run \"test\"... pass\n run \"verify\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
|
|
code: 0,
|
|
},
|
|
"only_modules": {
|
|
expected: "main.tftest.hcl... pass\n run \"first\"... pass\n run \"second\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
|
|
code: 0,
|
|
},
|
|
"variables_reference": {
|
|
expected: "main.tftest.hcl... pass\n run \"setup\"... pass\n run \"test\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
|
|
code: 0,
|
|
},
|
|
"destroyed_mod_outputs": {
|
|
expected: "main.tftest.hcl... pass\n run \"first_apply\"... pass\n run \"second_apply\"... pass\n\nSuccess! 2 passed, 0 failed.\n",
|
|
code: 0,
|
|
},
|
|
}
|
|
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
file := name
|
|
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
providerSource, close := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer close()
|
|
|
|
streams, done := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
|
|
command := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
code := command.Run([]string{"-no-color"})
|
|
output := done(t)
|
|
printedOutput := false
|
|
|
|
if code != tc.code {
|
|
printedOutput = true
|
|
t.Errorf("expected status code %d but got %d: %s", tc.code, code, output.All())
|
|
}
|
|
|
|
actual := output.All()
|
|
|
|
if diff := cmp.Diff(actual, tc.expected); len(diff) > 0 {
|
|
t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", tc.expected, actual, diff)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
if !printedOutput {
|
|
t.Errorf("should have deleted all resources on completion but left %s\n\n%s", provider.ResourceString(), output.All())
|
|
} else {
|
|
t.Errorf("should have deleted all resources on completion but left %s", provider.ResourceString())
|
|
}
|
|
}
|
|
|
|
if provider.DataSourceCount() > 0 {
|
|
if !printedOutput {
|
|
t.Errorf("should have deleted all data sources on completion but left %s\n\n%s", provider.DataSourceString(), output.All())
|
|
} else {
|
|
t.Errorf("should have deleted all data sources on completion but left %s", provider.DataSourceString())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTest_StatePropagation(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", "state_propagation")), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
|
|
providerSource, close := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer close()
|
|
|
|
streams, done := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
|
|
c := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
code := c.Run([]string{"-verbose", "-no-color"})
|
|
output := done(t)
|
|
|
|
if code != 0 {
|
|
t.Errorf("expected status code 0 but got %d", code)
|
|
}
|
|
|
|
expected := `main.tftest.hcl... pass
|
|
run "initial_apply_example"... pass
|
|
# test_resource.module_resource:
|
|
resource "test_resource" "module_resource" {
|
|
id = "df6h8as9"
|
|
value = "start"
|
|
}
|
|
run "initial_apply"... pass
|
|
# test_resource.resource:
|
|
resource "test_resource" "resource" {
|
|
id = "598318e0"
|
|
value = "start"
|
|
}
|
|
run "plan_second_example"... pass
|
|
|
|
OpenTofu used the selected providers to generate the following execution
|
|
plan. Resource actions are indicated with the following symbols:
|
|
+ create
|
|
|
|
OpenTofu will perform the following actions:
|
|
|
|
# test_resource.second_module_resource will be created
|
|
+ resource "test_resource" "second_module_resource" {
|
|
+ id = "b6a1d8cb"
|
|
+ value = "start"
|
|
}
|
|
|
|
Plan: 1 to add, 0 to change, 0 to destroy.
|
|
run "plan_update"... pass
|
|
|
|
OpenTofu used the selected providers to generate the following execution
|
|
plan. Resource actions are indicated with the following symbols:
|
|
~ update in-place
|
|
|
|
OpenTofu will perform the following actions:
|
|
|
|
# test_resource.resource will be updated in-place
|
|
~ resource "test_resource" "resource" {
|
|
id = "598318e0"
|
|
~ value = "start" -> "update"
|
|
}
|
|
|
|
Plan: 0 to add, 1 to change, 0 to destroy.
|
|
run "plan_update_example"... pass
|
|
|
|
OpenTofu used the selected providers to generate the following execution
|
|
plan. Resource actions are indicated with the following symbols:
|
|
~ update in-place
|
|
|
|
OpenTofu will perform the following actions:
|
|
|
|
# test_resource.module_resource will be updated in-place
|
|
~ resource "test_resource" "module_resource" {
|
|
id = "df6h8as9"
|
|
~ value = "start" -> "update"
|
|
}
|
|
|
|
Plan: 0 to add, 1 to change, 0 to destroy.
|
|
|
|
Success! 5 passed, 0 failed.
|
|
`
|
|
|
|
actual := output.All()
|
|
|
|
if diff := cmp.Diff(actual, expected); len(diff) > 0 {
|
|
t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expected, actual, diff)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
}
|
|
|
|
func TestTest_PartialUpdates(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
expectedOut string
|
|
expectedErr string
|
|
expectedCode int
|
|
}{
|
|
"partial_updates": {
|
|
expectedOut: `main.tftest.hcl... pass
|
|
run "first"... pass
|
|
|
|
Warning: Resource targeting is in effect
|
|
|
|
You are creating a plan with either the -target option or the -exclude
|
|
option, which means that the result of this plan may not represent all of the
|
|
changes requested by the current configuration.
|
|
|
|
The -target and -exclude options are not for routine use, and are provided
|
|
only for exceptional situations such as recovering from errors or mistakes,
|
|
or when OpenTofu specifically suggests to use it as part of an error message.
|
|
|
|
Warning: Applied changes may be incomplete
|
|
|
|
The plan was created with the -target or the -exclude option in effect, so
|
|
some changes requested in the configuration may have been ignored and the
|
|
output values may not be fully updated. Run the following command to verify
|
|
that no other changes are pending:
|
|
tofu plan
|
|
|
|
Note that the -target and -exclude options are not suitable for routine use,
|
|
and are provided only for exceptional situations such as recovering from
|
|
errors or mistakes, or when OpenTofu specifically suggests to use it as part
|
|
of an error message.
|
|
run "second"... pass
|
|
|
|
Success! 2 passed, 0 failed.
|
|
`,
|
|
expectedCode: 0,
|
|
},
|
|
"partial_update_failure": {
|
|
expectedOut: `main.tftest.hcl... fail
|
|
run "partial"... fail
|
|
|
|
Warning: Resource targeting is in effect
|
|
|
|
You are creating a plan with either the -target option or the -exclude
|
|
option, which means that the result of this plan may not represent all of the
|
|
changes requested by the current configuration.
|
|
|
|
The -target and -exclude options are not for routine use, and are provided
|
|
only for exceptional situations such as recovering from errors or mistakes,
|
|
or when OpenTofu specifically suggests to use it as part of an error message.
|
|
|
|
Warning: Applied changes may be incomplete
|
|
|
|
The plan was created with the -target or the -exclude option in effect, so
|
|
some changes requested in the configuration may have been ignored and the
|
|
output values may not be fully updated. Run the following command to verify
|
|
that no other changes are pending:
|
|
tofu plan
|
|
|
|
Note that the -target and -exclude options are not suitable for routine use,
|
|
and are provided only for exceptional situations such as recovering from
|
|
errors or mistakes, or when OpenTofu specifically suggests to use it as part
|
|
of an error message.
|
|
|
|
Failure! 0 passed, 1 failed.
|
|
`,
|
|
expectedErr: `
|
|
Error: Unknown condition run
|
|
|
|
on main.tftest.hcl line 7, in run "partial":
|
|
7: condition = test_resource.bar.value == "bar"
|
|
|
|
Condition expression could not be evaluated at this time.
|
|
`,
|
|
expectedCode: 1,
|
|
},
|
|
}
|
|
|
|
for file, tc := range tcs {
|
|
t.Run(file, func(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
view, done := testView(t)
|
|
|
|
c := &TestCommand{
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
code := c.Run([]string{"-no-color"})
|
|
output := done(t)
|
|
|
|
actualOut, expectedOut := output.Stdout(), tc.expectedOut
|
|
actualErr, expectedErr := output.Stderr(), tc.expectedErr
|
|
expectedCode := tc.expectedCode
|
|
|
|
if code != expectedCode {
|
|
t.Errorf("expected status code %d but got %d", expectedCode, code)
|
|
}
|
|
|
|
if diff := cmp.Diff(actualOut, expectedOut); len(diff) > 0 {
|
|
t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedOut, actualOut, diff)
|
|
}
|
|
|
|
if diff := cmp.Diff(actualErr, expectedErr); len(diff) > 0 {
|
|
t.Errorf("error didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", expectedErr, actualErr, diff)
|
|
}
|
|
|
|
if provider.ResourceCount() > 0 {
|
|
t.Errorf("should have deleted all resources on completion but left %v", provider.ResourceString())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTest_LocalVariables(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
expected string
|
|
code int
|
|
skip bool
|
|
}{
|
|
"pass_with_local_variable": {
|
|
expected: `tests/test.tftest.hcl... pass
|
|
run "first"... pass
|
|
|
|
|
|
Outputs:
|
|
|
|
foo = "bar"
|
|
run "second"... pass
|
|
|
|
No changes. Your infrastructure matches the configuration.
|
|
|
|
OpenTofu has compared your real infrastructure against your configuration and
|
|
found no differences, so no changes are needed.
|
|
|
|
Success! 2 passed, 0 failed.
|
|
`,
|
|
code: 0,
|
|
},
|
|
"pass_var_inside_variables": {
|
|
expected: `main.tftest.hcl... pass
|
|
run "first"... pass
|
|
|
|
|
|
Outputs:
|
|
|
|
sss = "false"
|
|
|
|
Success! 1 passed, 0 failed.
|
|
`,
|
|
code: 0,
|
|
},
|
|
"pass_var_with_default_value_inside_variables": {
|
|
expected: `main.tftest.hcl... pass
|
|
run "first"... pass
|
|
|
|
|
|
Outputs:
|
|
|
|
sss = "true"
|
|
|
|
Success! 1 passed, 0 failed.
|
|
`,
|
|
code: 0,
|
|
},
|
|
}
|
|
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
file := name
|
|
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
providerSource, providerClose := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer providerClose()
|
|
|
|
streams, done := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
|
|
command := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
code := command.Run([]string{"-verbose", "-no-color"})
|
|
output := done(t)
|
|
|
|
if code != tc.code {
|
|
t.Errorf("expected status code %d but got %d: %s", tc.code, code, output.All())
|
|
}
|
|
|
|
actual := output.All()
|
|
|
|
if diff := cmp.Diff(actual, tc.expected); len(diff) > 0 {
|
|
t.Errorf("output didn't match expected:\nexpected:\n%s\nactual:\n%s\ndiff:\n%s", tc.expected, actual, diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTest_InvalidLocalVariables(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
contains []string
|
|
notContains []string
|
|
code int
|
|
skip bool
|
|
}{
|
|
"invalid_variable_warning_expected": {
|
|
contains: []string{
|
|
"Warning: Invalid Variable in test file",
|
|
"Error: Invalid value for variable",
|
|
"This was checked by the validation rule at main.tf:5,3-13.",
|
|
"This was checked by the validation rule at main.tf:14,3-13.",
|
|
},
|
|
code: 1,
|
|
},
|
|
"invalid_variable_warning_no_expected": {
|
|
contains: []string{
|
|
"Error: Invalid value for variable",
|
|
"This was checked by the validation rule at main.tf:5,3-13.",
|
|
"This was checked by the validation rule at main.tf:14,3-13.",
|
|
},
|
|
notContains: []string{
|
|
"Warning: Invalid Variable in test file",
|
|
},
|
|
code: 1,
|
|
},
|
|
}
|
|
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
file := name
|
|
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
providerSource, providerClose := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer providerClose()
|
|
|
|
streams, done := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
|
|
command := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
code := command.Run([]string{"-verbose", "-no-color"})
|
|
output := done(t)
|
|
|
|
if code != tc.code {
|
|
t.Errorf("expected status code %d but got %d: %s", tc.code, code, output.All())
|
|
}
|
|
|
|
actual := output.All()
|
|
|
|
for _, containsString := range tc.contains {
|
|
if !strings.Contains(actual, containsString) {
|
|
t.Errorf("expected '%s' in output but didn't find it: \n%s", containsString, output.All())
|
|
}
|
|
}
|
|
|
|
for _, notContainsString := range tc.notContains {
|
|
if strings.Contains(actual, notContainsString) {
|
|
t.Errorf("expected not to find '%s' in output: \n%s", notContainsString, output.All())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTest_RunBlock(t *testing.T) {
|
|
tcs := map[string]struct {
|
|
expected string
|
|
code int
|
|
skip bool
|
|
}{
|
|
"invalid_run_block_name": {
|
|
expected: `
|
|
Error: Invalid run block name
|
|
|
|
on tests/main.tftest.hcl line 1, in run "sample run":
|
|
1: run "sample run" {
|
|
|
|
A name must start with a letter or underscore and may contain only letters,
|
|
digits, underscores, and dashes.
|
|
`,
|
|
code: 1,
|
|
},
|
|
}
|
|
|
|
for name, tc := range tcs {
|
|
t.Run(name, func(t *testing.T) {
|
|
if tc.skip {
|
|
t.Skip()
|
|
}
|
|
|
|
file := name
|
|
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath(path.Join("test", file)), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
providerSource, close := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer close()
|
|
|
|
streams, _ := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
init := &InitCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := init.Run(nil); code != tc.code {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTest_MockProviderValidation checks if tofu test runs proper validation for
|
|
// mock_provider. Even if provider schema has required fields, tofu test should
|
|
// ignore it completely, because the provider is mocked.
|
|
func TestTest_MockProviderValidation(t *testing.T) {
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("test/mock_provider_validation"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
provider := testing_command.NewProvider(nil)
|
|
providerSource, closePS := newMockProviderSource(t, map[string][]string{
|
|
"test": {"1.0.0"},
|
|
})
|
|
defer closePS()
|
|
|
|
provider.Provider.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
|
Provider: providers.Schema{
|
|
Block: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"required_field": {
|
|
Type: cty.String,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ResourceTypes: map[string]providers.Schema{
|
|
"test_resource": {
|
|
Block: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"value": {
|
|
Type: cty.String,
|
|
Optional: true,
|
|
},
|
|
"computed_value": {
|
|
Type: cty.String,
|
|
Computed: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
streams, _ := terminal.StreamsForTesting(t)
|
|
view := views.NewView(streams)
|
|
ui := new(cli.MockUi)
|
|
meta := Meta{
|
|
testingOverrides: metaOverridesForProvider(provider.Provider),
|
|
Ui: ui,
|
|
View: view,
|
|
Streams: streams,
|
|
ProviderSource: providerSource,
|
|
}
|
|
|
|
testCmd := &TestCommand{
|
|
Meta: meta,
|
|
}
|
|
|
|
if code := testCmd.Run(nil); code != 0 {
|
|
t.Fatalf("expected status code 0 but got %d: %s", code, ui.ErrorWriter)
|
|
}
|
|
}
|