Skip to content

Commit b0a502e

Browse files
committed
ImportPlanVerify
1 parent c0fd026 commit b0a502e

File tree

8 files changed

+195
-26
lines changed

8 files changed

+195
-26
lines changed

helper/resource/importstate/import_block_in_config_directory_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ func Test_ImportBlock_InConfigDirectory(t *testing.T) {
3737
},
3838
},
3939
{
40-
ResourceName: "examplecloud_container.test",
41-
ImportState: true,
42-
ImportStateKind: r.ImportBlockWithID,
43-
ImportStateVerify: true,
40+
ResourceName: "examplecloud_container.test",
41+
ImportState: true,
42+
ImportStateKind: r.ImportBlockWithID,
43+
// ImportStateVerify: true,
4444
ConfigDirectory: func(config.TestStepConfigRequest) string {
4545
return `testdata/2`
4646
},

helper/resource/importstate/import_block_in_config_file_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ func Test_ImportBlock_InConfigFile(t *testing.T) {
3737
},
3838
},
3939
{
40-
ResourceName: "examplecloud_container.test",
41-
ImportState: true,
42-
ImportStateKind: r.ImportBlockWithID,
43-
ImportStateVerify: true,
40+
ResourceName: "examplecloud_container.test",
41+
ImportState: true,
42+
ImportStateKind: r.ImportBlockWithID,
43+
// ImportStateVerify: true,
4444
ConfigFile: func(config.TestStepConfigRequest) string {
4545
return `testdata/2/examplecloud_container_import.tf`
4646
},
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package importstate_test
5+
6+
import (
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
10+
11+
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider"
12+
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/providerserver"
13+
"github.com/hashicorp/terraform-plugin-testing/tfversion"
14+
15+
r "github.com/hashicorp/terraform-plugin-testing/helper/resource"
16+
)
17+
18+
func Test_ImportBlock_VerifyPlan(t *testing.T) {
19+
t.Parallel()
20+
21+
r.UnitTest(t, r.TestCase{
22+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
23+
tfversion.SkipBelow(tfversion.Version1_5_0), // ImportBlockWithID requires Terraform 1.5.0 or later
24+
},
25+
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
26+
"examplecloud": providerserver.NewProviderServer(testprovider.Provider{
27+
Resources: map[string]testprovider.Resource{
28+
"examplecloud_container": examplecloudResource(),
29+
},
30+
}),
31+
},
32+
Steps: []r.TestStep{
33+
{
34+
Config: `
35+
resource "examplecloud_container" "test" {
36+
location = "westeurope"
37+
name = "somevalue"
38+
}`,
39+
},
40+
{
41+
ResourceName: "examplecloud_container.test",
42+
ImportState: true,
43+
ImportStateKind: r.ImportBlockWithID,
44+
ImportPlanVerify: true,
45+
},
46+
},
47+
})
48+
}

helper/resource/importstate/import_block_with_id_test.go

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,9 @@ func Test_TestStep_ImportBlockId(t *testing.T) {
4444
}`,
4545
},
4646
{
47-
ResourceName: "examplecloud_container.test",
48-
ImportState: true,
49-
ImportStateKind: r.ImportBlockWithID,
50-
ImportStateVerify: true,
47+
ResourceName: "examplecloud_container.test",
48+
ImportState: true,
49+
ImportStateKind: r.ImportBlockWithID,
5150
},
5251
},
5352
})
@@ -81,11 +80,10 @@ func TestTest_TestStep_ImportBlockId_ExpectError(t *testing.T) {
8180
location = "eastus"
8281
name = "somevalue"
8382
}`,
84-
ResourceName: "examplecloud_container.test",
85-
ImportState: true,
86-
ImportStateKind: r.ImportBlockWithID,
87-
ImportStateVerify: true,
88-
ExpectError: regexp.MustCompile(`importing resource examplecloud_container.test: expected a no-op resource action, got "update" action with plan(.?)`),
83+
ResourceName: "examplecloud_container.test",
84+
ImportState: true,
85+
ImportStateKind: r.ImportBlockWithID,
86+
ExpectError: regexp.MustCompile(`importing resource examplecloud_container.test: expected a no-op resource action, got "update" action with plan(.?)`),
8987
},
9088
},
9189
})
@@ -120,11 +118,10 @@ func TestTest_TestStep_ImportBlockId_FailWhenPlannableImportIsNotSupported(t *te
120118
location = "eastus"
121119
name = "somevalue"
122120
}`,
123-
ResourceName: "examplecloud_container.test",
124-
ImportState: true,
125-
ImportStateKind: r.ImportBlockWithID,
126-
ImportStateVerify: true,
127-
ExpectError: regexp.MustCompile(`Terraform 1.5.0`),
121+
ResourceName: "examplecloud_container.test",
122+
ImportState: true,
123+
ImportStateKind: r.ImportBlockWithID,
124+
ExpectError: regexp.MustCompile(`Terraform 1.5.0`),
128125
},
129126
},
130127
})

helper/resource/testing.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@ type TestStep struct {
700700
// [plancheck]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck
701701
ImportPlanChecks ImportPlanChecks
702702

703+
ImportPlanVerify bool
704+
703705
// ImportStateVerify, if true, will also check that the state values
704706
// that are finally put into the state after import match for all the
705707
// IDs returned by the Import. Note that this checks for strict equality

helper/resource/testing_new_import_state.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
7272
}
7373

7474
// Determine the ID to import
75-
var importId string
75+
var importId string //nolint:revive
7676
switch {
7777
case step.ImportStateIdFunc != nil:
7878
logging.HelperResourceTrace(ctx, "Using TestStep ImportStateIdFunc for import identifier")
@@ -185,15 +185,19 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
185185
return err
186186
}
187187

188-
if plan.ResourceChanges != nil {
188+
if len(plan.ResourceChanges) > 0 {
189189
logging.HelperResourceDebug(ctx, fmt.Sprintf("ImportBlockWithId: %d resource changes", len(plan.ResourceChanges)))
190190

191191
if err := requireNoopResourceAction(ctx, t, plan, resourceName, importWd, providers); err != nil {
192192
return err
193193
}
194-
}
195194

196-
// TODO compare plan to state from previous step
195+
if step.ImportPlanVerify {
196+
if err := teststep.VerifyImportPlan(plan, state); err != nil {
197+
return err
198+
}
199+
}
200+
}
197201

198202
if err := runPlanChecks(ctx, t, plan, step.ImportPlanChecks.PreApply); err != nil {
199203
return err
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package teststep
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
tfjson "github.com/hashicorp/terraform-json"
8+
"github.com/hashicorp/terraform-plugin-testing/terraform"
9+
)
10+
11+
// VerifyImportPlan compares a Terraform plan against a known good state
12+
func VerifyImportPlan(plan *tfjson.Plan, state *terraform.State) error {
13+
if state == nil {
14+
return fmt.Errorf("state is nil")
15+
}
16+
if plan == nil {
17+
return fmt.Errorf("plan is nil")
18+
}
19+
oldResources := make(map[string]*terraform.ResourceState)
20+
for logicalResourceName, resourceState := range state.RootModule().Resources {
21+
if !strings.HasPrefix(logicalResourceName, "data.") {
22+
oldResources[logicalResourceName] = resourceState
23+
}
24+
}
25+
for _, rc := range plan.ResourceChanges {
26+
if rc.Change == nil || rc.Change.Actions == nil {
27+
// does this matter?
28+
continue
29+
}
30+
31+
if !rc.Change.Actions.NoOp() {
32+
return fmt.Errorf("importing resource %s: expected a no-op resource action, got %q action", rc.Address, rc.Change.Actions)
33+
}
34+
35+
if rc.Change.Importing == nil {
36+
return fmt.Errorf("importing resource %s: expected importing to be true", rc.Address)
37+
}
38+
}
39+
40+
for _, rc := range plan.ResourceChanges {
41+
after, ok := rc.Change.After.(map[string]interface{})
42+
if !ok {
43+
panic(fmt.Sprintf("unexpected type %T", rc.Change.After))
44+
}
45+
46+
for k, v := range after {
47+
vs, ok := v.(string)
48+
if !ok {
49+
panic(fmt.Sprintf("unexpected type %T", v))
50+
}
51+
oldResource := oldResources[rc.Address]
52+
if oldResource == nil {
53+
// does this matter?
54+
return fmt.Errorf("importing resource %s: expected resource %s to exist in known state", rc.Address, rc.Change.Importing.ID)
55+
}
56+
attr, ok := oldResource.Primary.Attributes[k]
57+
if attr != vs {
58+
return fmt.Errorf("importing resource %s: expected %s in known state to be %q, got %q", rc.Address, k, oldResource.Primary.Attributes[k], vs)
59+
}
60+
}
61+
}
62+
return nil
63+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package teststep_test
2+
3+
import (
4+
"testing"
5+
6+
tfjson "github.com/hashicorp/terraform-json"
7+
"github.com/hashicorp/terraform-plugin-testing/internal/teststep"
8+
"github.com/hashicorp/terraform-plugin-testing/terraform"
9+
)
10+
11+
func Test_VerifyImportPlan(t *testing.T) {
12+
t.Parallel()
13+
14+
state := &terraform.State{
15+
Version: 6,
16+
Modules: []*terraform.ModuleState{
17+
{
18+
Path: []string{"root"},
19+
Resources: map[string]*terraform.ResourceState{
20+
"example_resource.instance-1": {
21+
Primary: &terraform.InstanceState{
22+
Attributes: map[string]string{
23+
"attr1": "value1",
24+
},
25+
},
26+
},
27+
},
28+
},
29+
},
30+
}
31+
32+
plan := new(tfjson.Plan)
33+
plan.ResourceChanges = []*tfjson.ResourceChange{
34+
{
35+
Address: "example_resource.instance-1",
36+
Change: &tfjson.Change{
37+
Actions: []tfjson.Action{tfjson.ActionNoop},
38+
After: map[string]interface{}{
39+
"attr1": "value1",
40+
},
41+
Before: map[string]interface{}{
42+
"attr1": "value1",
43+
},
44+
Importing: &tfjson.Importing{
45+
ID: "instance-1",
46+
},
47+
},
48+
},
49+
}
50+
51+
if err := teststep.VerifyImportPlan(plan, state); err != nil {
52+
t.Fatal(err)
53+
}
54+
55+
}

0 commit comments

Comments
 (0)