forked from dugancathal/dynago
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mock_executor.go
287 lines (246 loc) · 8.94 KB
/
mock_executor.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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
package dynago
/*
MockExecutor is a fake executor to help with building tests.
This Executor doesn't actually run any network requests, so it can be used in
unit testing for your own application which uses Dynago. It can be asserted
on whether a specific underlying method got called, what it was called with,
and the nature of how the query was built. You can also for most methods
control which result is returned in order to control how results make it back
to the application.
// example, normally you'd call into a real application not inside your
// test module and use dependency injection to specify the client.
func application(client *dynago.Client) int {
result, err := client.PutItem("mytable", doc).Execute()
// do something with result maybe.
}
func TestApplication() {
executor := &dynago.MockExecutor{}
client := dynago.NewClient(executor)
executor.PutItemResult = &dynago.PutItemResult{}
// call into application
application(client)
// assert things on the executor.
assert.Equal(true, executor.PutItemCalled)
assert.Equal("mytable", executor.PutItemCall.Table)
... and so on
}
*/
type MockExecutor struct {
Calls []MockExecutorCall // All calls made through this executor
DeleteItemCalled bool
DeleteItemCall *MockExecutorCall
DeleteItemResult *DeleteItemResult
DeleteItemError error
PutItemCalled bool
PutItemCall *MockExecutorCall
PutItemResult *PutItemResult
PutItemError error
GetItemCalled bool
GetItemCall *MockExecutorCall
GetItemResult *GetItemResult
GetItemError error
BatchGetItemCalled bool
BatchGetItemCall *MockExecutorCall
BatchGetItemResult *BatchGetResult
BatchWriteItemCalled bool
BatchWriteItemCall *MockExecutorCall
BatchWriteItemError error
QueryCalled bool // True if query was called at least once
QueryCall *MockExecutorCall // Info for the last call to Query
QueryError error // Specify the error from Query
QueryResult *QueryResult // Specify the result from Query
ScanCalled bool
ScanCall *MockExecutorCall
ScanResult *ScanResult
ScanError error
UpdateItemCalled bool
UpdateItemCall *MockExecutorCall
UpdateItemResult *UpdateItemResult
UpdateItemError error
}
/*
MockExecutorCall is created for all calls into this executor.
This allows for inspecting in a safe manner the attributes which would have
been sent to DynamoDB. Only attributes relevant to the call in question are
set.
*/
type MockExecutorCall struct {
// used for all calls
Method string
Table string
// used for calls with expressions (most of them)
ExpressionAttributeNames map[string]string
ExpressionAttributeValues Document
Key Document
Item Document
UpdateExpression string
ConditionExpression string
ReturnValues ReturnValues
ConsistentRead bool
// Query and Scan
IndexName string
KeyConditionExpression string
FilterExpression string
ProjectionExpression string
Ascending bool
Limit uint
ExclusiveStartKey Document
Select Select
Segment *int
TotalSegments *int
BatchWrites BatchWriteTableMap
BatchGets BatchGetTableMap
ReturnConsumedCapacity CapacityDetail
}
func (e *MockExecutor) BatchGetItem(batchGet *BatchGet) (*BatchGetResult, error) {
e.BatchGetItemCalled = true
e.addCall(&e.BatchGetItemCall, MockExecutorCall{
Method: "BatchGetItem",
BatchGets: batchGet.buildTableMap(),
ReturnConsumedCapacity: batchGet.capacityDetail,
})
return e.BatchGetItemResult, nil
}
func (e *MockExecutor) BatchWriteItem(batchWrite *BatchWrite) (*BatchWriteResult, error) {
e.BatchWriteItemCalled = true
e.addCall(&e.BatchWriteItemCall, MockExecutorCall{
Method: "BatchWriteItem",
BatchWrites: batchWrite.buildTableMap(),
ReturnConsumedCapacity: batchWrite.capacityDetail,
})
return &BatchWriteResult{}, e.BatchWriteItemError
}
func (e *MockExecutor) DeleteItem(deleteItem *DeleteItem) (*DeleteItemResult, error) {
e.DeleteItemCalled = true
e.addCall(&e.DeleteItemCall, MockExecutorCall{
Method: "DeleteItem",
Table: deleteItem.req.TableName,
Key: deleteItem.req.Key,
ConditionExpression: deleteItem.req.ConditionExpression,
ExpressionAttributeNames: deleteItem.req.ExpressionAttributeNames,
ExpressionAttributeValues: deleteItem.req.ExpressionAttributeValues,
ReturnConsumedCapacity: deleteItem.req.ReturnConsumedCapacity,
ReturnValues: deleteItem.req.ReturnValues,
})
return e.DeleteItemResult, e.DeleteItemError
}
func (e *MockExecutor) GetItem(getItem *GetItem) (*GetItemResult, error) {
e.GetItemCalled = true
call := MockExecutorCall{
Method: "GetItem",
Table: getItem.req.TableName,
Key: getItem.req.Key,
ConsistentRead: getItem.req.ConsistentRead,
ExpressionAttributeValues: getItem.req.ExpressionAttributeValues,
ExpressionAttributeNames: getItem.req.ExpressionAttributeNames,
ProjectionExpression: getItem.req.ProjectionExpression,
ReturnConsumedCapacity: getItem.req.ReturnConsumedCapacity,
}
e.addCall(&e.GetItemCall, call)
return e.GetItemResult, e.GetItemError
}
func (e *MockExecutor) PutItem(putItem *PutItem) (*PutItemResult, error) {
e.PutItemCalled = true
call := MockExecutorCall{
Method: "PutItem",
Table: putItem.req.TableName,
Item: putItem.req.Item,
ReturnValues: putItem.req.ReturnValues,
ConditionExpression: putItem.req.ConditionExpression,
ExpressionAttributeNames: putItem.req.ExpressionAttributeNames,
ExpressionAttributeValues: putItem.req.ExpressionAttributeValues,
ReturnConsumedCapacity: putItem.req.ReturnConsumedCapacity,
}
e.addCall(&e.PutItemCall, call)
return e.PutItemResult, e.PutItemError
}
func callFromQueryReq(req queryRequest) MockExecutorCall {
ascending, consistent := true, false
if req.ScanIndexForward != nil {
ascending = *req.ScanIndexForward
}
if req.ConsistentRead != nil {
consistent = *req.ConsistentRead
}
return MockExecutorCall{
Method: "Query",
Table: req.TableName,
IndexName: req.IndexName,
KeyConditionExpression: req.KeyConditionExpression,
FilterExpression: req.FilterExpression,
ExpressionAttributeNames: req.ExpressionAttributeNames,
ExpressionAttributeValues: req.ExpressionAttributeValues,
ProjectionExpression: req.ProjectionExpression,
Select: req.Select,
Ascending: ascending,
ConsistentRead: consistent,
Limit: req.Limit,
ExclusiveStartKey: req.ExclusiveStartKey,
ReturnConsumedCapacity: req.ReturnConsumedCapacity,
}
}
func (e *MockExecutor) Query(query *Query) (*QueryResult, error) {
e.QueryCalled = true
e.addCall(&e.QueryCall, callFromQueryReq(query.req))
result := e.QueryResult
if result != nil {
result.query = query
if result.Count == 0 {
result.Count = len(result.Items)
}
if result.ScannedCount == 0 {
result.ScannedCount = result.Count
}
}
return result, e.QueryError
}
func (e *MockExecutor) Scan(scan *Scan) (*ScanResult, error) {
e.ScanCalled = true
call := callFromQueryReq(scan.req.queryRequest)
call.Method = "Scan"
call.Segment = scan.req.Segment
call.TotalSegments = scan.req.TotalSegments
e.addCall(&e.ScanCall, call)
return e.ScanResult, e.ScanError
}
func (e *MockExecutor) UpdateItem(update *UpdateItem) (*UpdateItemResult, error) {
e.UpdateItemCalled = true
e.addCall(&e.UpdateItemCall, MockExecutorCall{
Method: "UpdateItem",
Table: update.req.TableName,
UpdateExpression: update.req.UpdateExpression,
ConditionExpression: update.req.ConditionExpression,
ExpressionAttributeNames: update.req.ExpressionAttributeNames,
ExpressionAttributeValues: update.req.ExpressionAttributeValues,
})
return e.UpdateItemResult, e.UpdateItemError
}
// Currently we don't implement mocking for SchemaExecutor. Returns nil.
func (e *MockExecutor) SchemaExecutor() SchemaExecutor {
return nil
}
// Reduce boilerplate on adding a call
func (e *MockExecutor) addCall(target **MockExecutorCall, call MockExecutorCall) {
e.Calls = append(e.Calls, call)
if target != nil {
*target = &call
}
}
// GetDeleteKeys is a convenience method to get delete keys in this batch write map for a specific table
func (m BatchWriteTableMap) GetDeleteKeys(table string) (deletes []Document) {
for _, entry := range m[table] {
if entry.DeleteRequest != nil {
deletes = append(deletes, entry.DeleteRequest.Key)
}
}
return
}
// GetPuts is a convenience method to get all put documents in this batch write map for a specific table
func (m BatchWriteTableMap) GetPuts(table string) (puts []Document) {
for _, entry := range m[table] {
if entry.PutRequest != nil {
puts = append(puts, entry.PutRequest.Item)
}
}
return
}