Skip to content

Commit

Permalink
tests - add check to see if a resource is being recreated (hashicor…
Browse files Browse the repository at this point in the history
  • Loading branch information
mbfrahry authored Sep 10, 2024
1 parent 6367166 commit 3a3acb3
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 0 deletions.
89 changes: 89 additions & 0 deletions internal/acceptance/helpers/is_not_resource_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package helpers

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-testing/plancheck"
)

var _ plancheck.PlanCheck = isNotResourceAction{}

type isNotResourceAction struct {
resourceAddress string
actionType plancheck.ResourceActionType
}

// CheckPlan implements the plan check logic.
func (e isNotResourceAction) CheckPlan(ctx context.Context, req plancheck.CheckPlanRequest, resp *plancheck.CheckPlanResponse) {
foundResource := false

for _, rc := range req.Plan.ResourceChanges {
if e.resourceAddress != rc.Address {
continue
}

switch e.actionType {
case plancheck.ResourceActionNoop:
if rc.Change.Actions.NoOp() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
case plancheck.ResourceActionCreate:
if rc.Change.Actions.Create() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
case plancheck.ResourceActionRead:
if rc.Change.Actions.Read() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
case plancheck.ResourceActionUpdate:
if rc.Change.Actions.Update() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
case plancheck.ResourceActionDestroy:
if rc.Change.Actions.Delete() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
case plancheck.ResourceActionDestroyBeforeCreate:
if rc.Change.Actions.DestroyBeforeCreate() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
case plancheck.ResourceActionCreateBeforeDestroy:
if rc.Change.Actions.CreateBeforeDestroy() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
case plancheck.ResourceActionReplace:
if rc.Change.Actions.Replace() {
resp.Error = fmt.Errorf("'%s' - expected action to not be %s", rc.Address, e.actionType)
return
}
default:
resp.Error = fmt.Errorf("%s - unexpected ResourceActionType: %s", rc.Address, e.actionType)
return
}

foundResource = true
break
}

if !foundResource {
resp.Error = fmt.Errorf("%s - Resource not found in plan ResourceChanges", e.resourceAddress)
return
}
}

// IsNotResourceAction returns a plan check that asserts that a given resource will not have a specific resource change type in the plan.
// Valid actionType are an enum of type plancheck.ResourceActionType, examples: NoOp, DestroyBeforeCreate, Update (in-place), etc.
func IsNotResourceAction(resourceAddress string, actionType plancheck.ResourceActionType) plancheck.PlanCheck {
return isNotResourceAction{
resourceAddress: resourceAddress,
actionType: actionType,
}
}
43 changes: 43 additions & 0 deletions internal/acceptance/testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/helpers"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/testclient"
Expand Down Expand Up @@ -48,6 +49,48 @@ func (td TestData) ResourceTest(t *testing.T, testResource types.TestResource, s
RefreshState: true,
}

newSteps := make([]TestStep, 0)
for _, step := range steps {
// This block adds a check to make sure tests aren't recreating a resource
if (step.Config != "" || step.ConfigDirectory != nil || step.ConfigFile != nil) && !step.PlanOnly {
step.ConfigPlanChecks = resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
helpers.IsNotResourceAction(td.ResourceName, plancheck.ResourceActionReplace),
},
}
}

if !step.ImportState {
newSteps = append(newSteps, step)
} else {
newSteps = append(newSteps, refreshStep)
newSteps = append(newSteps, step)
}
}
steps = newSteps

testCase := resource.TestCase{
PreCheck: func() { PreCheck(t) },
CheckDestroy: func(s *terraform.State) error {
client, err := testclient.Build()
if err != nil {
return fmt.Errorf("building client: %+v", err)
}
return helpers.CheckDestroyedFunc(client, testResource, td.ResourceType, td.ResourceName)(s)
},
Steps: steps,
}
td.runAcceptanceTest(t, testCase)
}

// ResourceTestIgnoreRecreate should be used when checking that a resource should be recreated during a test.
func (td TestData) ResourceTestIgnoreRecreate(t *testing.T, testResource types.TestResource, steps []TestStep) {
// Testing framework as of 1.6.0 no longer auto-refreshes state, so adding it back in here for all steps that update
// the config rather than having to modify 1000's of tests individually to add a refresh-only step
var refreshStep = TestStep{
RefreshState: true,
}

newSteps := make([]TestStep, 0)
for _, step := range steps {
if !step.ImportState {
Expand Down

0 comments on commit 3a3acb3

Please sign in to comment.