Coverage

100%
167
167
0

/Users/mk2/git/mm/index.js

100%
1
1
0
LineHitsSource
11module.exports = require('./lib/mm');

/Users/mk2/git/mm/lib/mm.js

100%
166
166
0
LineHitsSource
1/*!
2 * mm - lib/mm.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var EventEmitter = require('events').EventEmitter;
141var muk = require('muk');
151var http = require('http');
161var https = require('https');
171var cp = require('child_process');
181var EventEmitter = require('events').EventEmitter;
19
201exports = module.exports = function mock(obj, key, method) {
211 return muk.apply(null, arguments);
22};
23
241function getCallback(args) {
2517 var index = args.length - 1;
2617 var callback = args[index];
2717 while (typeof callback !== 'function') {
285 index--;
295 if (index < 0) {
301 break;
31 }
324 callback = args[index];
33 }
3417 if (!callback) {
351 throw new TypeError('Can\'t find callback function');
36 }
3716 return callback;
38}
39
40/**
41 * Mock async function error.
42 * @param {Object} mod, module object
43 * @param {String} method, mock module object method name.
44 * @param {String|Error} error, error string message or error instance.
45 * @param {Number} [tiemout], mock async callback timeout, default is 0.
46 */
471exports.error = function (mod, method, error, timeout) {
487 if (!error) {
491 error = new Error('mm mock error');
501 error.name = 'MockError';
51 }
527 if (typeof error === 'string') {
535 error = new Error(error);
545 error.name = 'MockError';
55 }
567 if (timeout) {
571 timeout = parseInt(timeout, 10);
58 }
597 timeout = timeout || 0;
607 muk(mod, method, function () {
618 var callback = getCallback(arguments);
627 setTimeout(function () {
637 callback(error);
64 }, timeout);
65 });
667 return this;
67};
68
69/**
70 * mock return callback(null, data1, data2).
71 *
72 * @param {Object} mod, module object
73 * @param {String} method, mock module object method name.
74 * @param {Array} datas, return datas array.
75 * @param {Number} [tiemout], mock async callback timeout, default is 0.
76 */
771exports.datas = function (mod, method, datas, timeout) {
788 if (timeout) {
791 timeout = parseInt(timeout, 10);
80 }
818 timeout = timeout || 0;
828 if (!Array.isArray(datas)) {
832 datas = [ datas ];
84 }
858 muk(mod, method, function () {
869 var callback = getCallback(arguments);
879 setTimeout(function () {
889 callback.apply(null, [null].concat(datas));
89 }, timeout);
90 });
918 return this;
92};
93
94/**
95 * mock return callback(null, data).
96 *
97 * @param {Object} mod, module object
98 * @param {String} method, mock module object method name.
99 * @param {Object} data, return data.
100 * @param {Number} [tiemout], mock async callback timeout, default is 0.
101 */
1021exports.data = function (mod, method, data, timeout) {
1033 return exports.datas(mod, method, [ data ], timeout);
104};
105
106/**
107 * mock return callback(null, null).
108 *
109 * @param {Object} mod, module object
110 * @param {String} method, mock module object method name.
111 * @param {Number} [tiemout], mock async callback timeout, default is 0.
112 */
1131exports.empty = function (mod, method, timeout) {
1141 return exports.datas(mod, method, null, timeout);
115};
116
1171exports.http = {};
1181exports.https = {};
119
1201http.__sourceRequest = http.request;
1211https.__sourceRequest = https.request;
122
1231function matchURL(options, url) {
12424 var pathname = options.path || options.pathname;
12524 var match = false;
12624 if (pathname) {
12724 if (typeof url === 'string') {
12814 match = pathname === url;
129 } else {
13010 match = url.test(pathname);
131 }
132 }
13324 return match;
134}
135
1361function mockRequest() {
13720 var req = new EventEmitter();
13820 req.write = function () {};
13920 req.end = function () {};
14020 req.abort = function () {
1416 req._aborted = true;
1426 process.nextTick(function () {
1436 var err = new Error('socket hang up');
1446 err.code = 'ECONNRESET';
1456 req.emit('error', err);
146 });
147 };
14820 return req;
149}
150
151/**
152 * Mock http.request().
153 * @param {String|RegExp} url, request url path.
154 * @param {String|Buffer} data, mock response data.
155 * If data is Array, then res will emit `data` event many times.
156 * @param {Object} headers, mock response headers.
157 * @param {Number} [delay], response delay time, default is 0.
158 */
1591exports.http.request = function (url, data, headers, delay) {
1605 return _request.call(this, http, url, data, headers, delay);
161};
162
163/**
164 * Mock https.request().
165 * @param {String|RegExp} url, request url path.
166 * @param {String|Buffer} data, mock response data.
167 * If data is Array, then res will emit `data` event many times.
168 * @param {Object} headers, mock response headers.
169 * @param {Number} [delay], response delay time, default is 0.
170 */
1711exports.https.request = function (url, data, headers, delay) {
1725 return _request.call(this, https, url, data, headers, delay);
173};
174
1751function _request(mod, url, data, headers, delay) {
17610 headers = headers || {};
17710 if (delay) {
1786 delay = parseInt(delay, 10);
179 }
18010 delay = delay || 0;
18110 mod.request = function (options, callback) {
18214 var datas = [];
18314 if (!Array.isArray(data)) {
18412 datas = [data];
185 } else {
1862 for (var i = 0; i < data.length; i++) {
1874 datas.push(data[i]);
188 }
189 }
190
19114 var match = matchURL(options, url);
19214 if (!match) {
1932 return mod.__sourceRequest(options, callback);
194 }
195
19612 var req = mockRequest();
197
19812 if (callback) {
19910 req.on('response', callback);
200 }
201
20212 var res = new EventEmitter();
20312 res.statusCode = headers.statusCode || 200;
20412 res.setEncoding = function (charset) {
2052 res.charset = charset;
206 };
20712 res.headers = headers;
20812 var ondata = function () {
20922 var chunk = datas.shift();
21022 if (!chunk) {
21110 if (!req._aborted) {
21210 res.emit('end');
213 }
21410 return;
215 }
216
21712 if (!req._aborted) {
21812 if (typeof chunk === 'string') {
21912 chunk = new Buffer(chunk);
220 }
22112 if (res.charset) {
2224 chunk = chunk.toString(res.charset);
223 }
22412 res.emit('data', chunk);
225 }
22612 process.nextTick(ondata);
227 };
228
22912 setTimeout(function () {
23012 if (!req._aborted) {
23110 req.emit('response', res);
23210 process.nextTick(ondata);
233 }
234 }, delay);
235
23612 return req;
237 };
23810 return this;
239}
240
241/**
242 * Mock http.request() error.
243 * @param {String|RegExp} url, request url path.
244 * @param {String|Error} reqError, request error.
245 * @param {String|Error} resError, response error.
246 * @param {Number} [delay], request error delay time, default is 0.
247 */
2481exports.http.requestError = function (url, reqError, resError, delay) {
2494 _requestError.call(this, http, url, reqError, resError, delay);
250};
251
252/**
253 * Mock https.request() error.
254 * @param {String|RegExp} url, request url path.
255 * @param {String|Error} reqError, request error.
256 * @param {String|Error} resError, response error.
257 * @param {Number} [delay], request error delay time, default is 0.
258 */
2591exports.https.requestError = function (url, reqError, resError, delay) {
2604 _requestError.call(this, https, url, reqError, resError, delay);
261};
262
2631function _requestError(mod, url, reqError, resError, delay) {
2648 if (delay) {
2654 delay = parseInt(delay, 10);
266 }
2678 delay = delay || 0;
2688 if (reqError && typeof reqError === 'string') {
2692 reqError = new Error(reqError);
2702 reqError.name = 'MockHttpRequestError';
271 }
2728 if (resError && typeof resError === 'string') {
2736 resError = new Error(resError);
2746 resError.name = 'MockHttpResponseError';
275 }
2768 mod.request = function (options, callback) {
27710 var match = matchURL(options, url);
27810 if (!match) {
2792 return mod.__sourceRequest(options, callback);
280 }
281
2828 var req = mockRequest();
283
2848 if (callback) {
2858 req.on('response', callback);
286 }
287
2888 setTimeout(function () {
2896 if (reqError) {
2902 return req.emit('error', reqError);
291 }
292
2934 var res = new EventEmitter();
2944 res.statusCode = 200;
2954 res.headers = {
296 server: 'MockMateServer'
297 };
2984 process.nextTick(function () {
2994 if (!req._aborted) {
3004 req.emit('error', resError);
301 }
302 });
3034 if (!req._aborted) {
3044 req.emit('response', res);
305 }
306 }, delay);
307
3088 return req;
309 };
3108 return this;
311}
312
313/**
314 * mock child_process spawn
315 * @param {Integer} code exit code
316 * @param {String} stdout
317 * @param {String} stderr
318 * @param {Integer} timeout stdout/stderr/close event emit timeout
319 */
3201exports.spawn = function (code, stdout, stderr, timeout) {
3211 var evt = new EventEmitter();
3221 muk(cp, 'spawn', function () {
3231 return evt;
324 });
3251 setTimeout(function () {
3261 stdout && evt.emit('stdout', stdout);
3271 stderr && evt.emit('stderr', stderr);
3281 evt.emit('close', code);
3291 evt.emit('exit', code);
330 }, timeout);
331};
332
333/**
334 * remove all mock effects.
335 */
3361exports.restore = function () {
33737 http.request = http.__sourceRequest;
33837 https.request = https.__sourceRequest;
33937 muk.restore();
34037 return this;
341};