Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/unreleased/BUG FIXES-20250602-073221.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'helper/resource: Updated `ImportBlockWith*` import state modes to use the `ExpectNonEmpty` field to allow non-empty import plans to pass successfully.'
time: 2025-06-02T07:32:21.384247-04:00
custom:
Issue: "518"
52 changes: 52 additions & 0 deletions helper/resource/importstate/import_block_with_id_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,58 @@ func TestImportBlock_WithID_ExpectError(t *testing.T) {
})
}

func TestImportBlock_WithID_ExpectNonEmptyPlan(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_5_0), // ImportBlockWithID requires Terraform 1.5.0 or later
},
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"examplecloud": providerserver.NewProviderServer(testprovider.Provider{
Resources: map[string]testprovider.Resource{
"examplecloud_container": examplecloudResource(),
},
}),
},
Steps: []r.TestStep{
{
Config: `
resource "examplecloud_container" "test" {
location = "westeurope"
name = "somevalue"
}`,
},
{
Config: `
resource "examplecloud_container" "test" {
location = "eastus"
name = "somevalue"
}

import {
to = examplecloud_container.test
id = "westeurope/somevalue"
}
`,
ResourceName: "examplecloud_container.test",
ImportState: true,
ImportStateKind: r.ImportBlockWithID,
ImportStateConfigExact: true,
ExpectNonEmptyPlan: true,
ImportPlanChecks: r.ImportPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction("examplecloud_container.test", plancheck.ResourceActionUpdate),
// The location address is imported as "westeurope/somevalue", which will be updated by the config to "eastus"
plancheck.ExpectKnownValue("examplecloud_container.test", tfjsonpath.New("location"), knownvalue.StringExact("eastus")),
plancheck.ExpectUnknownValue("examplecloud_container.test", tfjsonpath.New("id")),
},
},
},
},
})
}

func TestImportBlock_WithID_FailWhenNotSupported(t *testing.T) {
t.Parallel()

Expand Down
5 changes: 5 additions & 0 deletions helper/resource/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,11 @@ type TestStep struct {
// ID of that resource.
ImportState bool

// ImportStateKind controls the method of import that is used in combination with the other import-related fields on the TestStep struct.
//
// - By default, ImportCommandWithID is used, which tests import by using the ID string with the `terraform import` command. This was the original behavior prior to introducing the ImportStateKind field.
// - ImportBlockWithID tests import by using the ID string in an import configuration block with the `terraform plan` command.
// - ImportBlockWithResourceIdentity imports the state using an import configuration block with a resource identity.
ImportStateKind ImportStateKind

// ImportStateId is the ID to perform an ImportState operation with.
Expand Down
5 changes: 3 additions & 2 deletions helper/resource/testing_new_import_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,9 @@ func testImportBlock(ctx context.Context, t testing.T, workingDir *plugintest.Wo
switch {
case importing == nil:
return fmt.Errorf("importing resource %s: expected an import operation, got %q action with plan \nstdout:\n\n%s", resourceChangeUnderTest.Address, actions, savedPlanRawStdout(ctx, t, workingDir, providers))

case !actions.NoOp():
// By default we want to ensure there isn't a proposed plan after importing, but for some resources this is unavoidable.
// An example would be importing a resource that cannot read it's entire value back from the remote API.
case !step.ExpectNonEmptyPlan && !actions.NoOp():
return fmt.Errorf("importing resource %s: expected a no-op import operation, got %q action with plan \nstdout:\n\n%s", resourceChangeUnderTest.Address, actions, savedPlanRawStdout(ctx, t, workingDir, providers))
}

Expand Down
Loading