From c0eecf17658689af2f98ec84c8377df4c614a47d Mon Sep 17 00:00:00 2001 From: Baraa Basata Date: Thu, 14 Aug 2025 16:25:58 -0400 Subject: [PATCH] helper/resource: compatibility refresh after config mode test step (#496) * experimental: refresh after config mode test step * fixup! experimental: refresh after config mode test step * Skip an unnecessary refresh * Rename compatibility flag and extract to compatibility.go * Add header * Use an environment variable * logging > fmt * Add changelog entry --- .../unreleased/NOTES-20250814-152742.yaml | 5 +++++ helper/resource/environment_variables.go | 10 +++++++++ helper/resource/plugin.go | 9 ++++++++ helper/resource/testing_new_config.go | 22 +++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 .changes/unreleased/NOTES-20250814-152742.yaml diff --git a/.changes/unreleased/NOTES-20250814-152742.yaml b/.changes/unreleased/NOTES-20250814-152742.yaml new file mode 100644 index 000000000..b763904b3 --- /dev/null +++ b/.changes/unreleased/NOTES-20250814-152742.yaml @@ -0,0 +1,5 @@ +kind: NOTES +body: Adds an opt-in compatibility flag for config mode tests to unlock upgrade from v1.5.1 to latest for specific providers. +time: 2025-08-14T15:27:42.215486-04:00 +custom: + Issue: "496" diff --git a/helper/resource/environment_variables.go b/helper/resource/environment_variables.go index 981908799..2f8967fc6 100644 --- a/helper/resource/environment_variables.go +++ b/helper/resource/environment_variables.go @@ -32,4 +32,14 @@ const ( // type Config field includes a provider source, such as the terraform // configuration block required_providers attribute. EnvTfAccProviderNamespace = "TF_ACC_PROVIDER_NAMESPACE" + + // This is an undocumented compatibility flag. When this is set, a + // `Config`-mode test step will invoke a refresh before successful + // completion. + // + // This is a compatibility measure for test cases that have different -- + // but semantically-equal -- state representations in their test steps. + // When comparing two states, the testing framework is not aware of + // semantic equality or set equality. + EnvTfAccRefreshAfterApply = "TF_ACC_REFRESH_AFTER_APPLY" ) diff --git a/helper/resource/plugin.go b/helper/resource/plugin.go index 6e16e1613..86c3b77ba 100644 --- a/helper/resource/plugin.go +++ b/helper/resource/plugin.go @@ -114,6 +114,15 @@ type providerFactories struct { protov6 protov6ProviderFactories } +func runProviderCommandApplyRefreshOnly(ctx context.Context, t testing.T, wd *plugintest.WorkingDir, factories *providerFactories) error { + t.Helper() + + fn := func() error { + return wd.Apply(ctx, tfexec.Refresh(true), tfexec.RefreshOnly(true)) + } + return runProviderCommand(ctx, t, wd, factories, fn) +} + func runProviderCommandCreatePlan(ctx context.Context, t testing.T, wd *plugintest.WorkingDir, factories *providerFactories) error { t.Helper() diff --git a/helper/resource/testing_new_config.go b/helper/resource/testing_new_config.go index babaf8410..8b9b979c8 100644 --- a/helper/resource/testing_new_config.go +++ b/helper/resource/testing_new_config.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "os" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" @@ -29,6 +30,13 @@ var expectNonEmptyPlanOutputChangesMinTFVersion = tfversion.Version0_14_0 func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugintest.WorkingDir, step TestStep, providers *providerFactories, stepIndex int, helper *plugintest.Helper) error { t.Helper() + // When `refreshAfterApply` is true, a `Config`-mode test step will invoke + // a refresh before successful completion. This is a compatibility measure + // for test cases that have different -- but semantically-equal -- state + // representations in their test steps. When comparing two states, the + // testing framework is not aware of semantic equality or set equality. + _, refreshAfterApply := os.LookupEnv(EnvTfAccRefreshAfterApply) + configRequest := teststep.PrepareConfigurationRequest{ Directory: step.ConfigDirectory, File: step.ConfigFile, @@ -442,5 +450,19 @@ func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugint } } + if refreshAfterApply && !step.Destroy && !step.PlanOnly { + if len(c.Steps) > stepIndex+1 { + // If the next step is a refresh, then we have no need to refresh here + if !c.Steps[stepIndex+1].RefreshState { + // Log a searchable message to easily determine when this is no longer being used + logging.HelperResourceDebug(ctx, EnvTfAccRefreshAfterApply+": running apply -refresh-only -refresh=true") + err := runProviderCommandApplyRefreshOnly(ctx, t, wd, providers) + if err != nil { + return fmt.Errorf("Error running apply refresh-only: %w", err) + } + } + } + } + return nil }