testing: Add option to run only a plan on a TestStep configuration

This commit is contained in:
clint shryock 2017-03-22 14:42:01 -05:00 committed by Clint
parent 5671b5a6b1
commit 61355c33c5
3 changed files with 88 additions and 27 deletions

View File

@ -151,6 +151,11 @@ type TestStep struct {
// test to pass.
ExpectError *regexp.Regexp
// PlanOnly can be set to only run `plan` with this configuration, and not
// actually apply it. This is useful for ensuring config changes result in
// no-op plans
PlanOnly bool
// PreventPostDestroyRefresh can be set to true for cases where data sources
// are tested alongside real resources
PreventPostDestroyRefresh bool

View File

@ -53,34 +53,38 @@ func testStep(
"Error refreshing: %s", err)
}
// Plan!
if p, err := ctx.Plan(); err != nil {
return state, fmt.Errorf(
"Error planning: %s", err)
} else {
log.Printf("[WARN] Test: Step plan: %s", p)
}
// We need to keep a copy of the state prior to destroying
// such that destroy steps can verify their behaviour in the check
// function
stateBeforeApplication := state.DeepCopy()
// Apply!
state, err = ctx.Apply()
if err != nil {
return state, fmt.Errorf("Error applying: %s", err)
}
// Check! Excitement!
if step.Check != nil {
if step.Destroy {
if err := step.Check(stateBeforeApplication); err != nil {
return state, fmt.Errorf("Check failed: %s", err)
}
// If this step is a PlanOnly step, skip over this first Plan and subsequent
// Apply, and use the follow up Plan that checks for perpetual diffs
if !step.PlanOnly {
// Plan!
if p, err := ctx.Plan(); err != nil {
return state, fmt.Errorf(
"Error planning: %s", err)
} else {
if err := step.Check(state); err != nil {
return state, fmt.Errorf("Check failed: %s", err)
log.Printf("[WARN] Test: Step plan: %s", p)
}
// We need to keep a copy of the state prior to destroying
// such that destroy steps can verify their behaviour in the check
// function
stateBeforeApplication := state.DeepCopy()
// Apply!
state, err = ctx.Apply()
if err != nil {
return state, fmt.Errorf("Error applying: %s", err)
}
// Check! Excitement!
if step.Check != nil {
if step.Destroy {
if err := step.Check(stateBeforeApplication); err != nil {
return state, fmt.Errorf("Check failed: %s", err)
}
} else {
if err := step.Check(state); err != nil {
return state, fmt.Errorf("Check failed: %s", err)
}
}
}
}

View File

@ -120,6 +120,58 @@ func TestTest(t *testing.T) {
}
}
func TestTest_plan_only(t *testing.T) {
mp := testProvider()
mp.ApplyReturn = &terraform.InstanceState{
ID: "foo",
}
checkDestroy := false
checkDestroyFn := func(*terraform.State) error {
checkDestroy = true
return nil
}
mt := new(mockT)
Test(mt, TestCase{
Providers: map[string]terraform.ResourceProvider{
"test": mp,
},
CheckDestroy: checkDestroyFn,
Steps: []TestStep{
TestStep{
Config: testConfigStr,
PlanOnly: true,
ExpectNonEmptyPlan: false,
},
},
})
if !mt.failed() {
t.Fatal("test should've failed")
}
expected := `Step 0 error: After applying this step, the plan was not empty:
DIFF:
CREATE: test_instance.foo
foo: "" => "bar"
STATE:
<no state>`
if mt.failMessage() != expected {
t.Fatalf("Expected message: %s\n\ngot:\n\n%s", expected, mt.failMessage())
}
if !checkDestroy {
t.Fatal("didn't call check for destroy")
}
}
func TestTest_idRefresh(t *testing.T) {
// Refresh count should be 3:
// 1.) initial Ref/Plan/Apply