diff --git a/.changelog/1089.txt b/.changelog/1089.txt new file mode 100644 index 0000000000..395f3e8bd7 --- /dev/null +++ b/.changelog/1089.txt @@ -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 +``` diff --git a/helper/resource/testing.go b/helper/resource/testing.go index c77f0629ac..caadc3ddc5 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -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 diff --git a/helper/resource/testing_new_import_state.go b/helper/resource/testing_new_import_state.go index 17b4c2add4..0ec007168b 100644 --- a/helper/resource/testing_new_import_state.go +++ b/helper/resource/testing_new_import_state.go @@ -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") diff --git a/helper/resource/testing_new_import_state_test.go b/helper/resource/testing_new_import_state_test.go new file mode 100644 index 0000000000..ad4774e460 --- /dev/null +++ b/helper/resource/testing_new_import_state_test.go @@ -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 + }, + }, + }, + }) +}