Skip to content

Commit a13eded

Browse files
committed
helper/resource: introduce testOutcome type
In the plugin test framework, the core test step logic can indicate error in two different ways: - Return an `error` to the plugin test framework. This is the plugin test framework's facility for test authors to make positive or negative assertions for their provider's behavior. The test author supplies a `TestStep.ExpectError` to match on the error. - Call the Go testing library's `t.Fatal`. This is a lower-level error that a test author cannot make assertions on. Core test step logic -- such as `ImportState` -- does both of these. The reason for using one or the other can be subtle and implicit. This poses a cognitive hurdle for contributing code changes. It also poses a hurdle to refactoring and breaking down large functions into smaller ones: does every smaller function need a `testing.T` parameter so that it can call `t.Fatal`? This change separates concerns by introducing a type to embed the first type of error in a result object: framework: `testOutcome`. Smaller functions can return two values: `testOutcome` and `error`, with the latter indicating a fatal error. A test step's entry point -- such as `testStepNewImportState` -- is the one place that knows that an `error` translates to `t.Fatalf` and a failed `testOutcome` is simply returned to the plugin test framework.
1 parent c4be868 commit a13eded

File tree

1 file changed

+46
-9
lines changed

1 file changed

+46
-9
lines changed

helper/resource/testing_new_import_state.go

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,54 @@ import (
2525
"github.com/hashicorp/terraform-plugin-testing/tfversion"
2626
)
2727

28+
type testOutcome struct {
29+
ok bool
30+
message string
31+
err error
32+
}
33+
34+
var testOK = testOutcome{ok: true}
35+
var testFail = func(format string, a ...any) testOutcome {
36+
return testOutcome{
37+
ok: false,
38+
message: fmt.Sprintf(format, a...),
39+
}
40+
}
41+
var testFailErr = func(err error) testOutcome {
42+
return testOutcome{
43+
ok: false,
44+
err: err,
45+
}
46+
}
47+
2848
func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest.Helper, wd *plugintest.WorkingDir, step TestStep, cfgRaw string, providers *providerFactories, stepNumber int) error {
2949
t.Helper()
3050

51+
outcome, fatalErr := runImportTestStep(ctx, t, helper, wd, step, cfgRaw, providers, stepNumber)
52+
if fatalErr != nil {
53+
t.Fatal(fatalErr)
54+
}
55+
56+
switch {
57+
case outcome.err != nil:
58+
return outcome.err
59+
case !outcome.ok:
60+
return fmt.Errorf(outcome.message)
61+
default:
62+
return nil
63+
}
64+
}
65+
func runImportTestStep(ctx context.Context, t testing.T, helper *plugintest.Helper, wd *plugintest.WorkingDir, step TestStep, cfgRaw string, providers *providerFactories, stepNumber int) (testOutcome, error) {
66+
t.Helper()
67+
3168
// step.ImportStateKind implicitly defaults to the zero-value (ImportCommandWithID) for backward compatibility
3269
kind := step.ImportStateKind
3370
if kind.plannable() {
3471
// Instead of calling [t.Fatal], return an error. This package's unit tests can use [TestStep.ExpectError] to match on the error message.
3572
// An alternative, [plugintest.TestExpectTFatal], does not have access to logged error messages, so it is open to false positives on this
3673
// complex code path.
3774
if err := requirePlannableImport(t, *helper.TerraformVersion()); err != nil {
38-
return err
75+
return testFailErr(err), nil
3976
}
4077
}
4178

@@ -172,7 +209,7 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
172209
return importWd.CreatePlan(ctx, opts...)
173210
}, importWd, providers)
174211
if err != nil {
175-
return err
212+
return testFailErr(err), nil
176213
}
177214

178215
err = runProviderCommand(ctx, t, func() error {
@@ -182,7 +219,7 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
182219
return err
183220
}, importWd, providers)
184221
if err != nil {
185-
return err
222+
return testFailErr(err), nil
186223
}
187224

188225
if plan.ResourceChanges != nil {
@@ -204,10 +241,10 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
204241
return err
205242
}, importWd, providers)
206243
if err != nil {
207-
return fmt.Errorf("retrieving formatted plan output: %w", err)
244+
return testFail("retrieving formatted plan output: %w", err), nil
208245
}
209246

210-
return fmt.Errorf("importing resource %s: expected a no-op resource action, got %q action with plan \nstdout:\n\n%s", rc.Address, action, stdout)
247+
return testFail("importing resource %s: expected a no-op resource action, got %q action with plan \nstdout:\n\n%s", rc.Address, action, stdout), nil
211248
}
212249
}
213250
}
@@ -217,14 +254,14 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
217254
// TODO compare plan to state from previous step
218255

219256
if err := runPlanChecks(ctx, t, plan, step.ImportPlanChecks.PreApply); err != nil {
220-
return err
257+
return testFailErr(err), nil
221258
}
222259
} else {
223260
err = runProviderCommand(ctx, t, func() error {
224261
return importWd.Import(ctx, resourceName, importId)
225262
}, importWd, providers)
226263
if err != nil {
227-
return err
264+
return testFailErr(err), nil
228265
}
229266
}
230267

@@ -380,13 +417,13 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
380417
}
381418

382419
if diff := cmp.Diff(expected, actual); diff != "" {
383-
return fmt.Errorf("ImportStateVerify attributes not equivalent. Difference is shown below. The - symbol indicates attributes missing after import.\n\n%s", diff)
420+
return testFail("ImportStateVerify attributes not equivalent. Difference is shown below. The - symbol indicates attributes missing after import.\n\n%s", diff), nil
384421
}
385422
}
386423
}
387424
}
388425

389-
return nil
426+
return testOK, nil
390427
}
391428

392429
func appendImportBlock(config string, resourceName string, importID string) string {

0 commit comments

Comments
 (0)