testing: Add option to run only a plan on a TestStep configuration
This commit is contained in:
parent
5671b5a6b1
commit
61355c33c5
|
@ -151,6 +151,11 @@ type TestStep struct {
|
||||||
// test to pass.
|
// test to pass.
|
||||||
ExpectError *regexp.Regexp
|
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
|
// PreventPostDestroyRefresh can be set to true for cases where data sources
|
||||||
// are tested alongside real resources
|
// are tested alongside real resources
|
||||||
PreventPostDestroyRefresh bool
|
PreventPostDestroyRefresh bool
|
||||||
|
|
|
@ -53,34 +53,38 @@ func testStep(
|
||||||
"Error refreshing: %s", err)
|
"Error refreshing: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plan!
|
// If this step is a PlanOnly step, skip over this first Plan and subsequent
|
||||||
if p, err := ctx.Plan(); err != nil {
|
// Apply, and use the follow up Plan that checks for perpetual diffs
|
||||||
return state, fmt.Errorf(
|
if !step.PlanOnly {
|
||||||
"Error planning: %s", err)
|
// Plan!
|
||||||
} else {
|
if p, err := ctx.Plan(); err != nil {
|
||||||
log.Printf("[WARN] Test: Step plan: %s", p)
|
return state, fmt.Errorf(
|
||||||
}
|
"Error planning: %s", err)
|
||||||
|
|
||||||
// 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 {
|
} else {
|
||||||
if err := step.Check(state); err != nil {
|
log.Printf("[WARN] Test: Step plan: %s", p)
|
||||||
return state, fmt.Errorf("Check failed: %s", err)
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
func TestTest_idRefresh(t *testing.T) {
|
||||||
// Refresh count should be 3:
|
// Refresh count should be 3:
|
||||||
// 1.) initial Ref/Plan/Apply
|
// 1.) initial Ref/Plan/Apply
|
||||||
|
|
Loading…
Reference in New Issue