@@ -24,35 +24,16 @@ import (
2424 "github.com/hashicorp/terraform-plugin-testing/tfversion"
2525)
2626
27- func requirePlannableImport (t testing.T , versionUnderTest version.Version ) error {
28- t .Helper ()
29-
30- if versionUnderTest .LessThan (tfversion .Version1_5_0 ) {
31- return fmt .Errorf (
32- `ImportState steps using plannable import blocks require Terraform 1.5.0 or later. Either ` +
33- `upgrade the Terraform version running the test or add a ` + "`TerraformVersionChecks`" + ` to ` +
34- `the test case to skip this test.` + "\n \n " +
35- `https://developer.hashicorp.com/terraform/plugin/testing/acceptance-tests/tfversion-checks#skip-version-checks` )
36- }
37-
38- return nil
39- }
40-
4127func testStepNewImportState (ctx context.Context , t testing.T , helper * plugintest.Helper , wd * plugintest.WorkingDir , step TestStep , cfgRaw string , providers * providerFactories , stepNumber int ) error {
4228 t .Helper ()
4329
44- // Currently import modes `ImportBlockWithId` and `ImportBlockWithResourceIdentity` cannot support config file or directory
45- // since these modes append the import block to the configuration automatically
46- if step .ImportStateKind != ImportCommandWithId {
47- if step .ConfigFile != nil || step .ConfigDirectory != nil {
48- t .Fatalf ("ImportStateKind %q is not supported for config file or directory" , step .ImportStateKind )
49- }
50- }
51-
52- if step .ImportStateKind != ImportCommandWithId {
53- if err := requirePlannableImport (t , * helper .TerraformVersion ()); err != nil {
54- return err
55- }
30+ // step.ImportStateKind implicitly defaults to the zero-value (ImportCommandWithID) for backward compatibility
31+ kind := step .ImportStateKind
32+ // Instead of calling [t.Fatal], return an error. This package's unit tests can use [TestStep.ExpectError] to match on the error message.
33+ // An alternative, [plugintest.TestExpectTFatal], does not have access to logged error messages, so it is open to false positives on this
34+ // complex code path.
35+ if err := checkTerraformVersion (t , kind , * helper .TerraformVersion ()); err != nil {
36+ return err
5637 }
5738
5839 configRequest := teststep.PrepareConfigurationRequest {
@@ -132,19 +113,12 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
132113 importConfig = cfgRaw
133114 }
134115
135- // Update the test config dependent on the kind of import test being performed
136- switch step .ImportStateKind {
137- case ImportBlockWithResourceIdentity :
138- t .Fatalf ("TODO implement me" )
139- case ImportBlockWithId :
140- importConfig += fmt .Sprintf (`
141- import {
142- to = %s
143- id = %q
116+ if kind .plannable () {
117+ if kind .resourceIdentity () {
118+ importConfig = appendImportWithResourceIDBlock (importConfig , resourceName , importId )
119+ } else {
120+ importConfig = appendImportWithIDBlock (importConfig , resourceName , importId )
144121 }
145- ` , step .ResourceName , importId )
146- default :
147- // Not an import block test so nothing to do here
148122 }
149123
150124 confRequest := teststep.PrepareConfigurationRequest {
@@ -426,3 +400,104 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
426400
427401 return nil
428402}
403+
404+ func requireNoopResourceAction (ctx context.Context , t testing.T , plan * tfjson.Plan , resourceName string , importWd * plugintest.WorkingDir , providers * providerFactories ) error {
405+ t .Helper ()
406+
407+ rc := findResourceChangeInPlan (t , plan , resourceName )
408+ if rc == nil || rc .Change == nil || rc .Change .Actions == nil {
409+ // does this matter?
410+ return nil
411+ }
412+
413+ // should this be length checked and used as a condition, if it's a no-op then there shouldn't be any other changes here
414+ for _ , action := range rc .Change .Actions {
415+ if action != "no-op" {
416+ var stdout string
417+ err := runProviderCommand (ctx , t , func () error {
418+ var err error
419+ stdout , err = importWd .SavedPlanRawStdout (ctx )
420+ return err
421+ }, importWd , providers )
422+ if err != nil {
423+ return fmt .Errorf ("retrieving formatted plan output: %w" , err )
424+ }
425+
426+ return fmt .Errorf ("importing resource %s: expected a no-op resource action, got %q action with plan \n stdout:\n \n %s" , rc .Address , action , stdout )
427+ }
428+ }
429+
430+ return nil
431+ }
432+
433+ func findResourceChangeInPlan (t testing.T , plan * tfjson.Plan , resourceName string ) * tfjson.ResourceChange {
434+ t .Helper ()
435+
436+ for _ , rc := range plan .ResourceChanges {
437+ if rc .Address == resourceName {
438+ return rc
439+ }
440+ }
441+ return nil
442+ }
443+
444+ func appendImportWithIDBlock (config string , resourceName string , importID string ) string {
445+ return config + fmt .Sprintf (`` + "\n " +
446+ `import {` + "\n " +
447+ ` to = %s` + "\n " +
448+ ` id = %q` + "\n " +
449+ `}` ,
450+ resourceName , importID )
451+ }
452+
453+ func appendImportWithResourceIDBlock (config string , resourceName string , importID string ) string {
454+ return config + fmt .Sprintf (`` + "\n " +
455+ `import {` + "\n " +
456+ ` to = %s` + "\n " +
457+ ` identity = {` + "\n " +
458+ ` // Add identity attributes here` + "\n " +
459+ ` }` + "\n " +
460+ `}` + "\n " ,
461+ resourceName ) //, importID)
462+ }
463+
464+ func checkTerraformVersion (t testing.T , kind ImportStateKind , versionUnderTest version.Version ) error {
465+ t .Helper ()
466+
467+ if versionUnderTest .Core ().LessThan (kind .terraformVersion ()) {
468+ return fmt .Errorf (
469+ `%s steps require Terraform %s. Detected Terraform %s. Either upgrade the Terraform version running the test ` +
470+ `or add ` + "`TerraformVersionChecks`" + ` to the test case to skip this test.` + "\n \n " +
471+ `https://developer.hashicorp.com/terraform/plugin/testing/acceptance-tests/tfversion-checks#skip-version-checks` ,
472+ kind , kind .terraformVersion (), versionUnderTest )
473+ }
474+
475+ return nil
476+ }
477+
478+ func runImportStateCheckFunction (ctx context.Context , t testing.T , importState * terraform.State , step TestStep ) {
479+ t .Helper ()
480+
481+ var states []* terraform.InstanceState
482+ for address , r := range importState .RootModule ().Resources {
483+ if strings .HasPrefix (address , "data." ) {
484+ continue
485+ }
486+
487+ if r .Primary == nil {
488+ continue
489+ }
490+
491+ is := r .Primary .DeepCopy () //nolint:staticcheck // legacy usage
492+ is .Ephemeral .Type = r .Type // otherwise the check function cannot see the type
493+ states = append (states , is )
494+ }
495+
496+ logging .HelperResourceTrace (ctx , "Calling TestStep ImportStateCheck" )
497+
498+ if err := step .ImportStateCheck (states ); err != nil {
499+ t .Fatal (err )
500+ }
501+
502+ logging .HelperResourceTrace (ctx , "Called TestStep ImportStateCheck" )
503+ }
0 commit comments