forked from hashicorp/otto
/
testing_acceptance.go
130 lines (110 loc) · 3.66 KB
/
testing_acceptance.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package otto
import (
"fmt"
"log"
"os"
"testing"
"time"
)
// TestEnvVar must be set to a non-empty value for acceptance tests to run.
const TestEnvVar = "OTTO_ACC"
// TestCase is a single set of tests to run for an app to test the dev
// experience. See Test for more details on how this operates.
type TestCase struct {
// Precheck, if non-nil, will be called once before the test case
// runs at all. This can be used for some validation prior to the
// test running.
PreCheck func()
// Core is the core to use for testing. The Test* methods such
// as TestCoreConfig should be used to create the test core.
Core *Core
// Unit, if set to true, won't require the OTTO_ACC variable to be
// set and will run as a normal unit test.
Unit bool
// Steps are the set of operations that are run for this test case.
Steps []TestStep
// Teardown will be called before the test case is over regardless
// of if the test succeeded or failed. This should return an error
// in the case that the test can't guarantee all resources were
// properly cleaned up.
Teardown TestTeardownFunc
}
// TestStep is a single step within a TestCase.
type TestStep interface {
// Run is used to run the TestStep. It should return an error if
// the step failed. If the step fails, then no further steps are
// called. The Teardown will be called on the TestCase.
Run(*Core) error
}
// TestTeardownFunc is the callback used for Teardown in TestCase.
type TestTeardownFunc func(*Core) error
// Test performs an acceptance test on a backend with the given test case.
//
// Tests are not run unless an environmental variable "TF_ACC" is
// set to some non-empty value. This is to avoid test cases surprising
// a user by creating real resources.
//
// Tests will fail unless the verbose flag (`go test -v`, or explicitly
// the "-test.v" flag) is set. Because some acceptance tests take quite
// long, we require the verbose flag so users are able to see progress
// output.
func Test(t TestT, c TestCase) {
// We only run acceptance tests if an env var is set because they're
// slow and generally require some outside configuration.
if !c.Unit && os.Getenv(TestEnvVar) == "" {
t.Skip(fmt.Sprintf(
"Acceptance tests skipped unless env '%s' set",
TestEnvVar))
return
}
// We require verbose mode so that the user knows what is going on.
if !c.Unit && !testTesting && !testing.Verbose() {
t.Fatal("Acceptance tests must be run with the -v flag on tests")
return
}
// Run the PreCheck if we have it
if c.PreCheck != nil {
c.PreCheck()
}
// Check that the core is provided
if c.Core == nil {
t.Fatal("Must provide a core")
}
// Compile the app
log.Printf("[WARN] test: compiling appfile...")
if err := c.Core.Compile(); err != nil {
t.Fatal("error compiling: ", err)
}
// Run the steps
for i, s := range c.Steps {
log.Printf("[WARN] Executing test step %d", i+1)
if err := s.Run(c.Core); err != nil {
t.Error(fmt.Sprintf("Failed step %d: %s", i+1, err))
break
}
}
// Cleanup
if c.Teardown != nil {
if err := c.Teardown(c.Core); err != nil {
t.Error(fmt.Sprintf(
"Teardown failed! Dangling resources may exist. Error:\n\n%s",
err))
}
}
}
// TestStepSleep is a debugging test step that inserts a sleep.
// This is useful for debugging a failing acceptance test.
type TestStepSleep struct{ Duration time.Duration }
func (t *TestStepSleep) Run(*Core) error {
time.Sleep(t.Duration)
return nil
}
// TestT is the interface used to handle the test lifecycle of a test.
//
// Users should just use a *testing.T object, which implements this.
type TestT interface {
Error(args ...interface{})
Fatal(args ...interface{})
Skip(args ...interface{})
}
var testTesting = false