Skip to content

Commit

Permalink
helper/resource: Skip data source states with TestStep.ImportStateChe…
Browse files Browse the repository at this point in the history
…ck (#1089)

* helper/resource: Skip data source states with TestStep.ImportStateCheck

Reference: #1060

The `TestStep` type `ImportStateCheck` functionality is for verifying two specific scenarios over `ImportStateVerify`:

- Resources which create multiple resources during import (an implementation detail which is an legacy anti-pattern that should no longer be present in resources).
- Resources where importing may cause attributes to have syntactically different but semantically/functionally equivalent values that requires special logic to check.

Terraform 1.3 and later can include data source states when importing resources. Rather than forcing provider developers to account for this Terraform version-specific behavior, the `ImportStateCheck` logic will now only receive managed resource states to match the intended usage.

Previously with Terraform 1.2.9 and earlier:

```
--- PASS: TestTest_TestStep_ImportStateCheck_SkipDataSourceState (1.58s)
```

Previously with Terraform 1.3.4:

```
--- FAIL: TestTest_TestStep_ImportStateCheck_SkipDataSourceState (0.63s)
    /Users/bflad/src/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_import_state_test.go:16: expected 1 state, got: 2
```

Now with Terraform 1.3.4:

```
--- PASS: TestTest_TestStep_ImportStateCheck_SkipDataSourceState (0.91s)
```

* Update CHANGELOG for #1089
  • Loading branch information
bflad authored Nov 4, 2022
1 parent f5c914f commit ba4b604
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .changelog/1089.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
helper/resource: Fixed `TestStep` type `ImportStateCheck` field so that it only matches against resources following a change in behaviour in Terraform 1.3 that imports both resources and data sources into state
```
10 changes: 10 additions & 0 deletions helper/resource/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,16 @@ type TestStep struct {
// ImportStateCheck checks the results of ImportState. It should be
// used to verify that the resulting value of ImportState has the
// proper resources, IDs, and attributes.
//
// Prefer ImportStateVerify over ImportStateCheck, unless the resource
// import explicitly is expected to create multiple resources (not a
// recommended resource implementation) or if attributes are imported with
// syntactically different but semantically/functionally equivalent values
// where special logic is needed.
//
// Terraform versions 1.3 and later can include data source states during
// import, which the testing framework will skip to prevent the need for
// Terraform version specific logic in provider testing.
ImportStateCheck ImportStateCheckFunc

// ImportStateVerify, if true, will also check that the state values
Expand Down
16 changes: 11 additions & 5 deletions helper/resource/testing_new_import_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,18 @@ func testStepNewImportState(ctx context.Context, t testing.T, helper *plugintest
logging.HelperResourceTrace(ctx, "Using TestStep ImportStateCheck")

var states []*terraform.InstanceState
for _, r := range importState.RootModule().Resources {
if r.Primary != nil {
is := r.Primary.DeepCopy()
is.Ephemeral.Type = r.Type // otherwise the check function cannot see the type
states = append(states, is)
for address, r := range importState.RootModule().Resources {
if strings.HasPrefix(address, "data.") {
continue
}

if r.Primary == nil {
continue
}

is := r.Primary.DeepCopy()
is.Ephemeral.Type = r.Type // otherwise the check function cannot see the type
states = append(states, is)
}

logging.HelperResourceDebug(ctx, "Calling TestStep ImportStateCheck")
Expand Down
82 changes: 82 additions & 0 deletions helper/resource/testing_new_import_state_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package resource

import (
"context"
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

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

UnitTest(t, TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"examplecloud": func() (*schema.Provider, error) { //nolint:unparam // required signature
return &schema.Provider{
DataSourcesMap: map[string]*schema.Resource{
"examplecloud_thing": {
ReadContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
d.SetId("datasource-test")

return nil
},
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
ResourcesMap: map[string]*schema.Resource{
"examplecloud_thing": {
CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
d.SetId("resource-test")

return nil
},
DeleteContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
return nil
},
ReadContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
return nil
},
Schema: map[string]*schema.Schema{
"id": {
Computed: true,
Type: schema.TypeString,
},
},
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
},
},
}, nil
},
},
Steps: []TestStep{
{
Config: `
data "examplecloud_thing" "test" {}
resource "examplecloud_thing" "test" {}
`,
},
{
ResourceName: "examplecloud_thing.test",
ImportState: true,
ImportStateCheck: func(is []*terraform.InstanceState) error {
if len(is) > 1 {
return fmt.Errorf("expected 1 state, got: %d", len(is))
}

return nil
},
},
},
})
}

0 comments on commit ba4b604

Please sign in to comment.