-
Notifications
You must be signed in to change notification settings - Fork 22
/
mocks.go
236 lines (215 loc) · 8.49 KB
/
mocks.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package ksql
import (
"context"
"fmt"
)
var _ Provider = Mock{}
// Mock implements the Provider interface in order to allow users
// to easily mock the behavior of a ksql.Provider.
//
// To mock a particular method, e.g. Insert, you just need to overwrite
// the corresponding function attribute whose name is InsertFn().
//
// NOTE: This mock should be instantiated inside each unit test not globally.
//
// For capturing input values use a closure as in the example:
//
// var insertRecord interface{}
// dbMock := Mock{
// InsertFn: func(ctx context.Context, table Table, record interface{}) error {
// insertRecord = record
// },
// }
//
// NOTE: It is recommended not to make assertions inside the mocked methods,
// you should only check the captured values afterwards as all tests should
// have 3 stages: (1) setup, (2) run and finally (3) assert.
//
// For cases where the function will be called several times you might want to capture
// the number of calls as well as the values passed each time for that
// use closures and a slice of values, e.g.:
//
// var insertRecords []interface{}
// dbMock := Mock{
// InsertFn: func(ctx context.Context, table Table, record interface{}) error {
// insertRecords = append(insertRecords, record)
// },
// }
//
// expectedNumberOfCalls := 2
// assert.Equal(t, expectedNumberOfCalls, len(insertRecords))
//
// expectedInsertedRecords := []interface{}{
// user1,
// user2,
// }
// assert.Equal(t, expectedInsertedRecords, insertRecords)
//
type Mock struct {
InsertFn func(ctx context.Context, table Table, record interface{}) error
PatchFn func(ctx context.Context, table Table, record interface{}) error
DeleteFn func(ctx context.Context, table Table, idOrRecord interface{}) error
QueryFn func(ctx context.Context, records interface{}, query string, params ...interface{}) error
QueryOneFn func(ctx context.Context, record interface{}, query string, params ...interface{}) error
QueryChunksFn func(ctx context.Context, parser ChunkParser) error
ExecFn func(ctx context.Context, query string, params ...interface{}) (Result, error)
TransactionFn func(ctx context.Context, fn func(db Provider) error) error
}
// MockResult implements the Result interface returned by the Exec function
//
// Use the constructor `NewMockResult(42, 42)` for a simpler instantiation of this mock.
//
// But if you want one of the functions to return an error you'll need
// to specify the desired behavior by overwriting one of the attributes
// of the struct.
type MockResult struct {
LastInsertIdFn func() (int64, error)
RowsAffectedFn func() (int64, error)
}
// SetFallbackDatabase will set all the Fn attributes to use
// the function from the input database.
//
// SetFallbackDatabase is useful when you only want to
// overwrite some of the operations, e.g. for testing errors
// or if you want to use the same setup for making unit tests
// and integration tests, this way instead of creating a new server
// with a real database and another with a mocked one you can start
// the server once and run both types of tests.
//
// Example Usage:
//
// db, err := ksql.New(...)
// if err != nil {
// t.Fatal(err.Error())
// }
//
// mockdb := ksql.Mock{
// PatchFn: func(_ context.Context, _ ksql.Table, record interface{}) error {
// return ksql.ErrRecordNotFound
// },
// }.SetFallbackDatabase(db)
//
// // Passing the address to the service so
// // you can change it for each test
// myService := myservice.New(..., &mockdb, ...)
func (m Mock) SetFallbackDatabase(db Provider) Mock {
if m.InsertFn == nil {
m.InsertFn = db.Insert
}
if m.PatchFn == nil {
m.PatchFn = db.Patch
}
if m.DeleteFn == nil {
m.DeleteFn = db.Delete
}
if m.QueryFn == nil {
m.QueryFn = db.Query
}
if m.QueryOneFn == nil {
m.QueryOneFn = db.QueryOne
}
if m.QueryChunksFn == nil {
m.QueryChunksFn = db.QueryChunks
}
if m.ExecFn == nil {
m.ExecFn = db.Exec
}
if m.TransactionFn == nil {
m.TransactionFn = db.Transaction
}
return m
}
// Insert mocks the behavior of the Insert method.
// If InsertFn is set it will just call it returning the same return values.
// If InsertFn is unset it will panic with an appropriate error message.
func (m Mock) Insert(ctx context.Context, table Table, record interface{}) error {
if m.InsertFn == nil {
panic(fmt.Errorf("ksql.Mock.Insert(ctx, %v, %v) called but the ksql.Mock.InsertFn() is not set", table, record))
}
return m.InsertFn(ctx, table, record)
}
// Patch mocks the behavior of the Patch method.
// If PatchFn is set it will just call it returning the same return values.
// If PatchFn is unset it will panic with an appropriate error message.
func (m Mock) Patch(ctx context.Context, table Table, record interface{}) error {
if m.PatchFn == nil {
panic(fmt.Errorf("ksql.Mock.Patch(ctx, %v, %v) called but the ksql.Mock.PatchFn() is not set", table, record))
}
return m.PatchFn(ctx, table, record)
}
// Delete mocks the behavior of the Delete method.
// If DeleteFn is set it will just call it returning the same return values.
// If DeleteFn is unset it will panic with an appropriate error message.
func (m Mock) Delete(ctx context.Context, table Table, idOrRecord interface{}) error {
if m.DeleteFn == nil {
panic(fmt.Errorf("ksql.Mock.Delete(ctx, %v, %v) called but the ksql.Mock.DeleteFn() is not set", table, idOrRecord))
}
return m.DeleteFn(ctx, table, idOrRecord)
}
// Query mocks the behavior of the Query method.
// If QueryFn is set it will just call it returning the same return values.
// If QueryFn is unset it will panic with an appropriate error message.
func (m Mock) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error {
if m.QueryFn == nil {
panic(fmt.Errorf("ksql.Mock.Query(ctx, %v, %s, %v) called but the ksql.Mock.QueryFn() is not set", records, query, params))
}
return m.QueryFn(ctx, records, query, params...)
}
// QueryOne mocks the behavior of the QueryOne method.
// If QueryOneFn is set it will just call it returning the same return values.
// If QueryOneFn is unset it will panic with an appropriate error message.
func (m Mock) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error {
if m.QueryOneFn == nil {
panic(fmt.Errorf("ksql.Mock.QueryOne(ctx, %v, %s, %v) called but the ksql.Mock.QueryOneFn() is not set", record, query, params))
}
return m.QueryOneFn(ctx, record, query, params...)
}
// QueryChunks mocks the behavior of the QueryChunks method.
// If QueryChunksFn is set it will just call it returning the same return values.
// If QueryChunksFn is unset it will panic with an appropriate error message.
func (m Mock) QueryChunks(ctx context.Context, parser ChunkParser) error {
if m.QueryChunksFn == nil {
panic(fmt.Errorf("ksql.Mock.QueryChunks(ctx, %v) called but the ksql.Mock.QueryChunksFn() is not set", parser))
}
return m.QueryChunksFn(ctx, parser)
}
// Exec mocks the behavior of the Exec method.
// If ExecFn is set it will just call it returning the same return values.
// If ExecFn is unset it will panic with an appropriate error message.
func (m Mock) Exec(ctx context.Context, query string, params ...interface{}) (Result, error) {
if m.ExecFn == nil {
panic(fmt.Errorf("ksql.Mock.Exec(ctx, %s, %v) called but the ksql.Mock.ExecFn() is not set", query, params))
}
return m.ExecFn(ctx, query, params...)
}
// Transaction mocks the behavior of the Transaction method.
// If TransactionFn is set it will just call it returning the same return values.
// If TransactionFn is unset it will just call the input function
// passing the Mock itself as the database.
func (m Mock) Transaction(ctx context.Context, fn func(db Provider) error) error {
if m.TransactionFn == nil {
return fn(m)
}
return m.TransactionFn(ctx, fn)
}
// NewMockResult returns a simple implementation of the Result interface.
func NewMockResult(lastInsertID int64, rowsAffected int64) Result {
return MockResult{
LastInsertIdFn: func() (int64, error) { return lastInsertID, nil },
RowsAffectedFn: func() (int64, error) { return rowsAffected, nil },
}
}
// LastInsertId implements the Result interface
func (m MockResult) LastInsertId() (int64, error) {
if m.LastInsertIdFn == nil {
panic(fmt.Errorf("ksql.MockResult.LastInsertId() called but ksql.MockResult.LastInsertIdFn is not set"))
}
return m.LastInsertIdFn()
}
// RowsAffected implements the Result interface
func (m MockResult) RowsAffected() (int64, error) {
if m.RowsAffectedFn == nil {
panic(fmt.Errorf("ksql.MockResult.RowsAffected() called but ksql.MockResult.RowsAffectedFn is not set"))
}
return m.RowsAffectedFn()
}