Skip to content

Commit a89a49d

Browse files
authored
isolate test cases (#12)
* chore: go mod * feat: add db truncator * feat: - truncate db after test case - log response body * feat: run setup funcs before test case * feat: - add Name to SetupTestCase - add Setup to TestCase * feat: add name to test case * feat: - remove Position field - fix tests * fix: catch unknown setups * fix: - add Truncator mock - add NewVariableMap * fix: inject idFn * fix: add test cases to TestExecutor_ExecuteTestCase * fix: report error with test case name
1 parent edb6036 commit a89a49d

21 files changed

+1181
-576
lines changed

case.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package immune
22

33
type SetupTestCase struct {
4+
Name string `json:"name"`
45
StoreResponseVariables S `json:"store_response_variables"`
56
RequestBody M `json:"request_body"`
67
ResponseBody bool `json:"response_body"`
78
Endpoint string `json:"endpoint"`
89
HTTPMethod Method `json:"http_method"`
910
StatusCode int `json:"status_code"`
10-
Position int `json:"-"`
1111
//Report *SetupTestCaseReport `json:"-"`
1212
}
1313

@@ -17,7 +17,8 @@ type SetupTestCaseReport struct {
1717
}
1818

1919
type TestCase struct {
20-
Position int `json:"-"`
20+
Name string `json:"name"`
21+
Setup []string `json:"setup"`
2122
StatusCode int `json:"status_code"`
2223
HTTPMethod Method `json:"http_method"`
2324
Endpoint string `json:"endpoint"`

database.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package immune
2+
3+
type Database struct {
4+
Type string `json:"type"`
5+
Dsn string `json:"dsn"`
6+
}

database/mongo/truncate.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package mongo
2+
3+
import (
4+
"context"
5+
"net/url"
6+
"strings"
7+
"time"
8+
9+
"go.mongodb.org/mongo-driver/mongo"
10+
"go.mongodb.org/mongo-driver/mongo/options"
11+
)
12+
13+
type Truncator struct {
14+
db *mongo.Database
15+
}
16+
17+
func NewTruncator(dsn string) (*Truncator, error) {
18+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
19+
defer cancel()
20+
21+
client, err := mongo.Connect(ctx, options.Client().ApplyURI(dsn))
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
u, err := url.Parse(dsn)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
dbName := strings.TrimPrefix(u.Path, "/")
32+
conn := client.Database(dbName, nil)
33+
34+
return &Truncator{db: conn}, err
35+
}
36+
37+
func (t *Truncator) Truncate(ctx context.Context) error {
38+
return t.db.Drop(ctx)
39+
}

database/noop/noop_truncator.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package noop
2+
3+
import "context"
4+
5+
type Truncator struct{}
6+
7+
func NewTruncator() (*Truncator, error) {
8+
return &Truncator{}, nil
9+
}
10+
11+
func (t *Truncator) Truncate(ctx context.Context) error {
12+
return nil
13+
}

database/truncator.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package database
2+
3+
import (
4+
"context"
5+
6+
"github.com/frain-dev/immune/database/noop"
7+
8+
"github.com/frain-dev/immune"
9+
"github.com/frain-dev/immune/database/mongo"
10+
)
11+
12+
type Truncator interface {
13+
Truncate(ctx context.Context) error
14+
}
15+
16+
func NewTruncator(db *immune.Database) (Truncator, error) {
17+
switch db.Type {
18+
case "mongo":
19+
return mongo.NewTruncator(db.Dsn)
20+
default:
21+
return noop.NewTruncator()
22+
}
23+
}

exec/net.go renamed to exec/executor.go

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import (
1010
"time"
1111

1212
"github.com/frain-dev/immune"
13+
"github.com/frain-dev/immune/database"
1314
"github.com/frain-dev/immune/url"
14-
"github.com/google/uuid"
1515
"github.com/pkg/errors"
1616
log "github.com/sirupsen/logrus"
1717
)
@@ -21,17 +21,28 @@ type Executor struct {
2121
callbackIDLocation string
2222
baseURL string
2323
maxCallbackWaitSeconds uint
24-
s immune.CallbackServer
24+
idFn func() string
2525
client *http.Client
26+
dbTruncator database.Truncator
2627
vm *immune.VariableMap
28+
s immune.CallbackServer
2729
}
2830

29-
func NewExecutor(s immune.CallbackServer, client *http.Client, vm *immune.VariableMap, maxCallbackWaitSeconds uint, baseURL string, callbackIDLocation string) *Executor {
31+
func NewExecutor(
32+
s immune.CallbackServer,
33+
client *http.Client,
34+
vm *immune.VariableMap,
35+
maxCallbackWaitSeconds uint,
36+
baseURL string,
37+
callbackIDLocation string,
38+
dbTruncator database.Truncator, idFn func() string) *Executor {
3039
return &Executor{
3140
s: s,
3241
vm: vm,
42+
idFn: idFn,
3343
client: client,
3444
baseURL: baseURL,
45+
dbTruncator: dbTruncator,
3546
callbackIDLocation: callbackIDLocation,
3647
maxCallbackWaitSeconds: maxCallbackWaitSeconds,
3748
}
@@ -41,12 +52,12 @@ func NewExecutor(s immune.CallbackServer, client *http.Client, vm *immune.Variab
4152
func (ex *Executor) ExecuteSetupTestCase(ctx context.Context, setupTC *immune.SetupTestCase) error {
4253
u, err := url.Parse(fmt.Sprintf("%s%s", ex.baseURL, setupTC.Endpoint))
4354
if err != nil {
44-
return errors.Wrapf(err, "setup_test_case %d: failed to parse url", setupTC.Position)
55+
return errors.Wrapf(err, "setup_test_case %s: failed to parse url", setupTC.Name)
4556
}
4657

4758
result, err := u.ProcessWithVariableMap(ex.vm)
4859
if err != nil {
49-
return errors.Wrapf(err, "setup_test_case %d: failed to process parsed url with variable map", setupTC.Position)
60+
return errors.Wrapf(err, "setup_test_case %s: failed to process parsed url with variable map", setupTC.Name)
5061
}
5162

5263
r := &request{
@@ -58,39 +69,39 @@ func (ex *Executor) ExecuteSetupTestCase(ctx context.Context, setupTC *immune.Se
5869

5970
err = r.processWithVariableMap(ex.vm)
6071
if err != nil {
61-
return errors.Wrapf(err, "setup_test_case %d: failed to process request body with variable map", setupTC.Position)
72+
return errors.Wrapf(err, "setup_test_case %s: failed to process request body with variable map", setupTC.Name)
6273
}
63-
//log.Infof("setup_test_case %d: request body: %s", setupTC.Position, pretty.Sprint(r.body))
74+
//log.Infof("setup_test_case %s: request body: %s", setupTC.Name, pretty.Sprint(r.body))
6475

6576
resp, err := ex.sendRequest(ctx, r)
6677
if err != nil {
6778
return err
6879
}
6980

7081
if setupTC.StatusCode != resp.statusCode {
71-
return errors.Errorf("setup_test_case %d: wants status code %d but got status code %d", setupTC.Position, setupTC.StatusCode, resp.statusCode)
82+
return errors.Errorf("setup_test_case %s: wants status code %d but got status code %d, response body: %s", setupTC.Name, setupTC.StatusCode, resp.statusCode, resp.body.String())
7283
}
7384

7485
if setupTC.ResponseBody {
7586
if resp.body.Len() == 0 {
76-
return errors.Errorf("setup_test_case %d: wants response body but got no response body", setupTC.Position)
87+
return errors.Errorf("setup_test_case %s: wants response body but got no response body", setupTC.Name)
7788
}
7889

7990
m := immune.M{}
8091
err = resp.Decode(&m)
8192
if err != nil {
82-
return errors.Wrapf(err, "setup_test_case %d: failed to decode response body", setupTC.Position)
93+
return errors.Wrapf(err, "setup_test_case %s: failed to decode response body: response body: %s", setupTC.Name, resp.body.String())
8394
}
8495

8596
if setupTC.StoreResponseVariables != nil {
8697
err = ex.vm.ProcessResponse(ctx, setupTC.StoreResponseVariables, m)
8798
if err != nil {
88-
return errors.Wrapf(err, "setup_test_case %d: failed to process response body", setupTC.Position)
99+
return errors.Wrapf(err, "setup_test_case %s: failed to process response body: response body: %s", setupTC.Name, resp.body.String())
89100
}
90101
}
91102
} else {
92103
if resp.body.Len() > 0 {
93-
return errors.Errorf("setup_test_case %d: does not want a response body but got a response body: '%s'", setupTC.Position, resp.body.String())
104+
return errors.Errorf("setup_test_case %s: does not want a response body but got a response body: '%s'", setupTC.Name, resp.body.String())
94105
}
95106
}
96107

@@ -101,20 +112,20 @@ func (ex *Executor) ExecuteSetupTestCase(ctx context.Context, setupTC *immune.Se
101112
func (ex *Executor) ExecuteTestCase(ctx context.Context, tc *immune.TestCase) error {
102113
u, err := url.Parse(fmt.Sprintf("%s%s", ex.baseURL, tc.Endpoint))
103114
if err != nil {
104-
return errors.Wrapf(err, "test_case %d: failed to parse url", tc.Position)
115+
return errors.Wrapf(err, "test_case %s: failed to parse url", tc.Name)
105116
}
106117

107118
result, err := u.ProcessWithVariableMap(ex.vm)
108119
if err != nil {
109-
return errors.Wrapf(err, "test_case %d: failed to process parsed url with variable map", tc.Position)
120+
return errors.Wrapf(err, "test_case %s: failed to process parsed url with variable map", tc.Name)
110121
}
111122

112123
var uid string
113124
if tc.Callback.Enabled {
114-
uid = uuid.New().String()
125+
uid = ex.idFn()
115126
err = immune.InjectCallbackID(ex.callbackIDLocation, uid, tc.RequestBody)
116127
if err != nil {
117-
return errors.Wrapf(err, "test_case %d: failed to inject callback id into request body", tc.Position)
128+
return errors.Wrapf(err, "test_case %s: failed to inject callback id into request body", tc.Name)
118129
}
119130
}
120131

@@ -127,7 +138,7 @@ func (ex *Executor) ExecuteTestCase(ctx context.Context, tc *immune.TestCase) er
127138

128139
err = r.processWithVariableMap(ex.vm)
129140
if err != nil {
130-
return errors.Wrapf(err, "test_case %d: failed to process request body with variable map", tc.Position)
141+
return errors.Wrapf(err, "test_case %s: failed to process request body with variable map", tc.Name)
131142
}
132143

133144
resp, err := ex.sendRequest(ctx, r)
@@ -136,23 +147,23 @@ func (ex *Executor) ExecuteTestCase(ctx context.Context, tc *immune.TestCase) er
136147
}
137148

138149
if tc.StatusCode != resp.statusCode {
139-
return errors.Errorf("test_case %d: wants status code %d but got status code %d", tc.Position, tc.StatusCode, resp.statusCode)
150+
return errors.Errorf("test_case %s: wants status code %d but got status code %d", tc.Name, tc.StatusCode, resp.statusCode)
140151
}
141152

142153
if tc.ResponseBody {
143154
if resp.body.Len() == 0 {
144-
return errors.Errorf("test_case %d: wants response body but got no response body: %+v", tc.Position, resp)
155+
return errors.Errorf("test_case %s: wants response body but got no response body: status_code: %d", tc.Name, resp.statusCode)
145156
}
146157

147158
m := immune.M{}
148159
err = resp.Decode(&m)
149160
if err != nil {
150-
return errors.Errorf("test_case %d: failed to decode response body: %+v", tc.Position, resp)
161+
return errors.Wrapf(err, "test_case %s: failed to decode response body: %s", tc.Name, string(resp.buf))
151162
}
152163

153164
} else {
154165
if resp.body.Len() > 0 {
155-
return errors.Wrapf(err, "test_case %d: does not want a response body but got a response body: '%s'", tc.Position, resp.body.String())
166+
return errors.Errorf("test_case %s: does not want a response body but got a response body: '%s'", tc.Name, resp.body.String())
156167
}
157168
}
158169

@@ -163,19 +174,19 @@ func (ex *Executor) ExecuteTestCase(ctx context.Context, tc *immune.TestCase) er
163174
for i := uint(1); i <= tc.Callback.Times; i++ {
164175
select {
165176
case <-cctx.Done():
166-
log.Infof("succesfully received %d callbacks for test_case %d before max callback wait seconds elapsed", i, tc.Position)
177+
log.Infof("succesfully received %d callbacks for test_case %s before max callback wait seconds elapsed", i, tc.Name)
167178
break
168179
default:
169180
sig := ex.s.ReceiveCallback()
170181
if sig.ImmuneCallBackID != uid {
171-
return errors.Errorf("test_case %d: incorrect callback_id: expected_callback_id '%s', got_callback_id '%s'", tc.Position, uid, sig.ImmuneCallBackID)
182+
return errors.Errorf("test_case %s: incorrect callback_id: expected_callback_id '%s', got_callback_id '%s'", tc.Name, uid, sig.ImmuneCallBackID)
172183
}
173-
log.Infof("callback %d for test_case %d received", i, tc.Position)
184+
log.Infof("callback %d for test_case %s received", i, tc.Name)
174185
}
175186
}
176187
}
177188

178-
return nil
189+
return ex.dbTruncator.Truncate(ctx)
179190
}
180191

181192
func (ex *Executor) sendRequest(ctx context.Context, r *request) (*response, error) {
@@ -202,5 +213,5 @@ func (ex *Executor) sendRequest(ctx context.Context, r *request) (*response, err
202213
return nil, err
203214
}
204215

205-
return &response{body: bytes.NewBuffer(buf), statusCode: resp.StatusCode}, nil
216+
return &response{body: bytes.NewBuffer(buf), buf: buf, statusCode: resp.StatusCode}, nil
206217
}

0 commit comments

Comments
 (0)