Skip to content
10 changes: 10 additions & 0 deletions helper/resource/environment_variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
9 changes: 9 additions & 0 deletions helper/resource/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
22 changes: 22 additions & 0 deletions helper/resource/testing_new_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
"os"

"github.com/hashicorp/terraform-exec/tfexec"
tfjson "github.com/hashicorp/terraform-json"
Expand All @@ -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,
Expand Down Expand Up @@ -443,5 +451,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 {
// Echo a searchable message to easily determine when this is no longer being used
fmt.Println(EnvTfAccRefreshAfterApply+":", "running apply -refresh-only -refresh=true")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We should probably use the logging package for this with whatever log level you think is appropriate

err := runProviderCommandApplyRefreshOnly(ctx, t, wd, providers)
if err != nil {
return fmt.Errorf("Error running apply refresh-only: %w", err)
}
}
}
}

return nil
}