forked from dwyl/aws-sdk-mock
/
index.js
298 lines (269 loc) · 8.33 KB
/
index.js
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
288
289
290
291
292
293
294
295
296
297
298
'use strict';
/**
* Helpers to mock the AWS SDK Services using sinon.js under the hood
* Export two functions:
* - mock
* - restore
*
* Mocking is done in two steps:
* - mock of the constructor for the service on AWS
* - mock of the method on the service
**/
var sinon = require('sinon');
var traverse = require('traverse');
var _AWS = require('aws-sdk');
var Readable = require('stream').Readable;
var AWS = {};
var services = {};
/**
* Sets the aws-sdk to be mocked.
*/
AWS.setSDK = function(path) {
_AWS = require(path);
};
AWS.setSDKInstance = function(sdk) {
_AWS = sdk;
};
/**
* Stubs the service and registers the method that needs to be mocked.
*/
AWS.mock = function(service, method, replace) {
// If the service does not exist yet, we need to create and stub it.
if (!services[service]) {
services[service] = {};
/**
* Save the real constructor so we can invoke it later on.
* Uses traverse for easy access to nested services (dot-separated)
*/
services[service].Constructor = traverse(_AWS).get(service.split('.'));
services[service].methodMocks = {};
services[service].invoked = false;
mockService(service);
}
// Register the method to be mocked out.
if (!services[service].methodMocks[method]) {
services[service].methodMocks[method] = { replace: replace };
// If the constructor was already invoked, we need to mock the method here.
if (services[service].invoked) {
mockServiceMethod(service, services[service].client, method, replace);
}
}
return services[service].methodMocks[method];
};
/**
* Stubs the service and registers the method that needs to be re-mocked.
*/
AWS.remock = function(service, method, replace) {
if (services[service].methodMocks[method]) {
restoreMethod(service, method);
services[service].methodMocks[method] = {
replace: replace
};
}
if (services[service].invoked) {
mockServiceMethod(service, services[service].client, method, replace);
}
return services[service].methodMocks[method];
}
/**
* Stub the constructor for the service on AWS.
* E.g. calls of new AWS.SNS() are replaced.
*/
function mockService(service) {
var nestedServices = service.split('.');
var method = nestedServices.pop();
var object = traverse(_AWS).get(nestedServices);
var serviceStub = sinon.stub(object, method).callsFake(function(...args) {
services[service].invoked = true;
/**
* Create an instance of the service by calling the real constructor
* we stored before. E.g. var client = new AWS.SNS()
* This is necessary in order to mock methods on the service.
*/
var client = new services[service].Constructor(...args);
services[service].client = client;
// Once this has been triggered we can mock out all the registered methods.
for (var key in services[service].methodMocks) {
mockServiceMethod(service, client, key, services[service].methodMocks[key].replace);
};
return client;
});
services[service].stub = serviceStub;
};
/**
* Stubs the method on a service.
*
* All AWS service methods take two argument:
* - params: an object.
* - callback: of the form 'function(err, data) {}'.
*/
function mockServiceMethod(service, client, method, replace) {
services[service].methodMocks[method].stub = sinon.stub(client, method).callsFake(function() {
var args = Array.prototype.slice.call(arguments);
var userArgs, userCallback;
if (typeof args[(args.length || 1) - 1] === 'function') {
userArgs = args.slice(0, -1);
userCallback = args[(args.length || 1) - 1];
} else {
userArgs = args;
}
var havePromises = typeof AWS.Promise === 'function';
var promise, resolve, reject, storedResult;
var tryResolveFromStored = function() {
if (storedResult && promise) {
if (typeof storedResult.then === 'function') {
storedResult.then(resolve, reject)
} else if (storedResult.reject) {
reject(storedResult.reject);
} else {
resolve(storedResult.resolve);
}
}
};
var callback = function(err, data) {
if (!storedResult) {
if (err) {
storedResult = {reject: err};
} else {
storedResult = {resolve: data};
}
}
if (userCallback) {
userCallback(err, data);
}
tryResolveFromStored();
};
var request = {
promise: havePromises ? function() {
if (!promise) {
promise = new AWS.Promise(function (resolve_, reject_) {
resolve = resolve_;
reject = reject_;
});
}
tryResolveFromStored();
return promise;
} : undefined,
createReadStream: function() {
if (replace instanceof Readable) {
return replace;
} else {
var stream = new Readable();
stream._read = function(size) {
if (typeof replace === 'string' || Buffer.isBuffer(replace)) {
this.push(replace);
}
this.push(null);
};
return stream;
}
},
on: function(eventName, callback) {
},
send: function(callback) {
}
};
// different locations for the paramValidation property
var config = (client.config || client.options || _AWS.config);
if (config.paramValidation) {
try {
// different strategies to find method, depending on wether the service is nested/unnested
var inputRules =
((client.api && client.api.operations[method]) || client[method] || {}).input;
if (inputRules) {
var params = userArgs[(userArgs.length || 1) - 1];
new _AWS.ParamValidator((client.config || _AWS.config).paramValidation).validate(inputRules, params);
}
} catch (e) {
callback(e, null);
return request;
}
}
// If the value of 'replace' is a function we call it with the arguments.
if (typeof replace === 'function') {
var result = replace.apply(replace, userArgs.concat([callback]));
if (storedResult === undefined && result != null &&
typeof result.then === 'function') {
storedResult = result
}
}
// Else we call the callback with the value of 'replace'.
else {
callback(null, replace);
}
return request;
});
}
/**
* Restores the mocks for just one method on a service, the entire service, or all mocks.
*
* When no parameters are passed, everything will be reset.
* When only the service is passed, that specific service will be reset.
* When a service and method are passed, only that method will be reset.
*/
AWS.restore = function(service, method) {
if (!service) {
restoreAllServices();
} else {
if (method) {
restoreMethod(service, method);
} else {
restoreService(service);
}
};
};
/**
* Restores all mocked service and their corresponding methods.
*/
function restoreAllServices() {
for (var service in services) {
restoreService(service);
}
}
/**
* Restores a single mocked service and its corresponding methods.
*/
function restoreService(service) {
if (services[service]) {
restoreAllMethods(service);
if (services[service].stub)
services[service].stub.restore();
delete services[service];
} else {
console.log('Service ' + service + ' was never instantiated yet you try to restore it.');
}
}
/**
* Restores all mocked methods on a service.
*/
function restoreAllMethods(service) {
for (var method in services[service].methodMocks) {
restoreMethod(service, method);
}
}
/**
* Restores a single mocked method on a service.
*/
function restoreMethod(service, method) {
if (services[service] && services[service].methodMocks[method]) {
if (services[service].methodMocks[method].stub) {
services[service].methodMocks[method].stub.restore();
}
delete services[service].methodMocks[method];
} else {
console.log('Method ' + service + ' was never instantiated yet you try to restore it.');
}
}
(function() {
var setPromisesDependency = _AWS.config.setPromisesDependency;
/* istanbul ignore next */
/* only to support for older versions of aws-sdk */
if (typeof setPromisesDependency === 'function') {
AWS.Promise = global.Promise;
_AWS.config.setPromisesDependency = function(p) {
AWS.Promise = p;
return setPromisesDependency(p);
};
}
})();
module.exports = AWS;