@mbtest/mountebank 2.9.2-beta.9050
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/bin/mb +136 -0
- package/package.json +71 -0
- package/releases.json +52 -0
- package/src/cli/api.js +112 -0
- package/src/cli/cli.js +420 -0
- package/src/controllers/configController.js +64 -0
- package/src/controllers/feedController.js +115 -0
- package/src/controllers/homeController.js +58 -0
- package/src/controllers/imposterController.js +328 -0
- package/src/controllers/impostersController.js +215 -0
- package/src/controllers/logsController.js +52 -0
- package/src/models/behaviors.js +553 -0
- package/src/models/behaviorsValidator.js +186 -0
- package/src/models/compatibility.js +133 -0
- package/src/models/dryRunValidator.js +261 -0
- package/src/models/filesystemBackedImpostersRepository.js +908 -0
- package/src/models/http/baseHttpServer.js +207 -0
- package/src/models/http/headersMap.js +87 -0
- package/src/models/http/httpProxy.js +230 -0
- package/src/models/http/httpRequest.js +82 -0
- package/src/models/http/httpServer.js +18 -0
- package/src/models/http/index.js +18 -0
- package/src/models/https/cert/mb-cert.pem +20 -0
- package/src/models/https/cert/mb-csr.pem +16 -0
- package/src/models/https/cert/mb-key.pem +27 -0
- package/src/models/https/httpsServer.js +42 -0
- package/src/models/https/index.js +18 -0
- package/src/models/imposter.js +243 -0
- package/src/models/imposterPrinter.js +120 -0
- package/src/models/impostersRepository.js +49 -0
- package/src/models/inMemoryImpostersRepository.js +418 -0
- package/src/models/jsonpath.js +44 -0
- package/src/models/mbConnection.js +107 -0
- package/src/models/predicates.js +438 -0
- package/src/models/protocols.js +242 -0
- package/src/models/responseResolver.js +398 -0
- package/src/models/smtp/index.js +16 -0
- package/src/models/smtp/smtpRequest.js +60 -0
- package/src/models/smtp/smtpServer.js +109 -0
- package/src/models/tcp/index.js +18 -0
- package/src/models/tcp/tcpProxy.js +110 -0
- package/src/models/tcp/tcpRequest.js +23 -0
- package/src/models/tcp/tcpServer.js +156 -0
- package/src/models/tcp/tcpValidator.js +19 -0
- package/src/models/xpath.js +95 -0
- package/src/mountebank.js +245 -0
- package/src/public/images/arrow_down.png +0 -0
- package/src/public/images/arrow_up.png +0 -0
- package/src/public/images/book.jpg +0 -0
- package/src/public/images/dataflow.png +0 -0
- package/src/public/images/favicon.ico +0 -0
- package/src/public/images/forkme_right_orange_ff7600.png +0 -0
- package/src/public/images/mountebank.png +0 -0
- package/src/public/images/overview.gif +0 -0
- package/src/public/images/quote.png +0 -0
- package/src/public/images/tw-logo.png +0 -0
- package/src/public/scripts/jquery/jquery-3.6.1.min.js +2 -0
- package/src/public/scripts/urlHashHandler.js +31 -0
- package/src/public/stylesheets/application.css +424 -0
- package/src/public/stylesheets/ie.css +14 -0
- package/src/public/stylesheets/imposters.css +121 -0
- package/src/public/stylesheets/jqueryui/1.10.4/themes/smoothness/jquery-ui.css +1178 -0
- package/src/util/combinators.js +68 -0
- package/src/util/date.js +51 -0
- package/src/util/errors.js +55 -0
- package/src/util/helpers.js +131 -0
- package/src/util/inherit.js +28 -0
- package/src/util/ip.js +54 -0
- package/src/util/logger.js +83 -0
- package/src/util/middleware.js +256 -0
- package/src/util/scopedLogger.js +47 -0
- package/src/views/_footer.ejs +20 -0
- package/src/views/_header.ejs +113 -0
- package/src/views/_imposter.ejs +8 -0
- package/src/views/config.ejs +71 -0
- package/src/views/docs/api/behaviors/copy.ejs +427 -0
- package/src/views/docs/api/behaviors/decorate.ejs +182 -0
- package/src/views/docs/api/behaviors/lookup.ejs +220 -0
- package/src/views/docs/api/behaviors/shellTransform.ejs +153 -0
- package/src/views/docs/api/behaviors/wait.ejs +121 -0
- package/src/views/docs/api/behaviors.ejs +141 -0
- package/src/views/docs/api/contracts/addStub-description.ejs +10 -0
- package/src/views/docs/api/contracts/addStub.ejs +10 -0
- package/src/views/docs/api/contracts/config-description.ejs +32 -0
- package/src/views/docs/api/contracts/config.ejs +23 -0
- package/src/views/docs/api/contracts/home-description.ejs +18 -0
- package/src/views/docs/api/contracts/home.ejs +13 -0
- package/src/views/docs/api/contracts/imposter-description.ejs +439 -0
- package/src/views/docs/api/contracts/imposter.ejs +182 -0
- package/src/views/docs/api/contracts/imposters-description.ejs +13 -0
- package/src/views/docs/api/contracts/imposters.ejs +13 -0
- package/src/views/docs/api/contracts/logs-description.ejs +3 -0
- package/src/views/docs/api/contracts/logs.ejs +14 -0
- package/src/views/docs/api/contracts/stub-description.ejs +4 -0
- package/src/views/docs/api/contracts/stub.ejs +7 -0
- package/src/views/docs/api/contracts/stubs-description.ejs +4 -0
- package/src/views/docs/api/contracts/stubs.ejs +11 -0
- package/src/views/docs/api/contracts.ejs +133 -0
- package/src/views/docs/api/errors.ejs +64 -0
- package/src/views/docs/api/fault/connectionReset.ejs +31 -0
- package/src/views/docs/api/fault/randomDataThenClose.ejs +31 -0
- package/src/views/docs/api/faults.ejs +57 -0
- package/src/views/docs/api/injection.ejs +426 -0
- package/src/views/docs/api/json.ejs +205 -0
- package/src/views/docs/api/jsonpath.ejs +210 -0
- package/src/views/docs/api/mocks.ejs +130 -0
- package/src/views/docs/api/overview.ejs +968 -0
- package/src/views/docs/api/predicates/and.ejs +62 -0
- package/src/views/docs/api/predicates/contains.ejs +64 -0
- package/src/views/docs/api/predicates/deepEquals.ejs +114 -0
- package/src/views/docs/api/predicates/endsWith.ejs +66 -0
- package/src/views/docs/api/predicates/equals.ejs +125 -0
- package/src/views/docs/api/predicates/exists.ejs +118 -0
- package/src/views/docs/api/predicates/inject.ejs +67 -0
- package/src/views/docs/api/predicates/matches.ejs +66 -0
- package/src/views/docs/api/predicates/not.ejs +52 -0
- package/src/views/docs/api/predicates/or.ejs +79 -0
- package/src/views/docs/api/predicates/startsWith.ejs +62 -0
- package/src/views/docs/api/predicates.ejs +382 -0
- package/src/views/docs/api/proxies.ejs +191 -0
- package/src/views/docs/api/proxy/addDecorateBehavior.ejs +115 -0
- package/src/views/docs/api/proxy/addWaitBehavior.ejs +96 -0
- package/src/views/docs/api/proxy/injectHeaders.ejs +91 -0
- package/src/views/docs/api/proxy/predicateGenerators.ejs +600 -0
- package/src/views/docs/api/proxy/proxyModes.ejs +495 -0
- package/src/views/docs/api/stubs.ejs +391 -0
- package/src/views/docs/api/xpath.ejs +281 -0
- package/src/views/docs/cli/configFiles.ejs +133 -0
- package/src/views/docs/cli/customFormatters.ejs +53 -0
- package/src/views/docs/cli/help.ejs +6 -0
- package/src/views/docs/cli/replay.ejs +42 -0
- package/src/views/docs/cli/restart.ejs +10 -0
- package/src/views/docs/cli/save.ejs +68 -0
- package/src/views/docs/cli/start.ejs +234 -0
- package/src/views/docs/cli/stop.ejs +32 -0
- package/src/views/docs/commandLine.ejs +93 -0
- package/src/views/docs/communityExtensions.ejs +233 -0
- package/src/views/docs/gettingStarted.ejs +146 -0
- package/src/views/docs/mentalModel.ejs +51 -0
- package/src/views/docs/protocols/custom.ejs +231 -0
- package/src/views/docs/protocols/http.ejs +238 -0
- package/src/views/docs/protocols/https.ejs +246 -0
- package/src/views/docs/protocols/smtp.ejs +142 -0
- package/src/views/docs/protocols/tcp.ejs +431 -0
- package/src/views/docs/security.ejs +38 -0
- package/src/views/faqs.ejs +65 -0
- package/src/views/feed.ejs +33 -0
- package/src/views/imposter.ejs +22 -0
- package/src/views/imposters.ejs +33 -0
- package/src/views/index.ejs +89 -0
- package/src/views/license.ejs +30 -0
- package/src/views/logs.ejs +77 -0
- package/src/views/releases/v1.1.0.ejs +55 -0
- package/src/views/releases/v1.1.36.ejs +84 -0
- package/src/views/releases/v1.1.72.ejs +92 -0
- package/src/views/releases/v1.10.0.ejs +108 -0
- package/src/views/releases/v1.11.0.ejs +109 -0
- package/src/views/releases/v1.12.0.ejs +96 -0
- package/src/views/releases/v1.13.0.ejs +118 -0
- package/src/views/releases/v1.14.0.ejs +107 -0
- package/src/views/releases/v1.14.1.ejs +94 -0
- package/src/views/releases/v1.15.0.ejs +113 -0
- package/src/views/releases/v1.16.0.ejs +104 -0
- package/src/views/releases/v1.2.0.ejs +78 -0
- package/src/views/releases/v1.2.103.ejs +86 -0
- package/src/views/releases/v1.2.122.ejs +86 -0
- package/src/views/releases/v1.2.30.ejs +84 -0
- package/src/views/releases/v1.2.45.ejs +84 -0
- package/src/views/releases/v1.2.56.ejs +79 -0
- package/src/views/releases/v1.3.0.ejs +86 -0
- package/src/views/releases/v1.3.1.ejs +100 -0
- package/src/views/releases/v1.4.0.ejs +96 -0
- package/src/views/releases/v1.4.1.ejs +103 -0
- package/src/views/releases/v1.4.2.ejs +100 -0
- package/src/views/releases/v1.4.3.ejs +113 -0
- package/src/views/releases/v1.5.0.ejs +104 -0
- package/src/views/releases/v1.5.1.ejs +91 -0
- package/src/views/releases/v1.6.0.ejs +109 -0
- package/src/views/releases/v1.7.0.ejs +113 -0
- package/src/views/releases/v1.7.1.ejs +90 -0
- package/src/views/releases/v1.7.2.ejs +96 -0
- package/src/views/releases/v1.8.0.ejs +121 -0
- package/src/views/releases/v1.9.0.ejs +111 -0
- package/src/views/releases/v2.0.0.ejs +159 -0
- package/src/views/releases/v2.1.0.ejs +121 -0
- package/src/views/releases/v2.1.1.ejs +106 -0
- package/src/views/releases/v2.1.2.ejs +84 -0
- package/src/views/releases/v2.2.0.ejs +115 -0
- package/src/views/releases/v2.2.1.ejs +102 -0
- package/src/views/releases/v2.3.0.ejs +121 -0
- package/src/views/releases/v2.3.1.ejs +100 -0
- package/src/views/releases/v2.3.2.ejs +102 -0
- package/src/views/releases/v2.3.3.ejs +97 -0
- package/src/views/releases/v2.4.0.ejs +114 -0
- package/src/views/releases/v2.5.0.ejs +51 -0
- package/src/views/releases/v2.6.0.ejs +35 -0
- package/src/views/releases/v2.7.0.ejs +32 -0
- package/src/views/releases/v2.8.0.ejs +36 -0
- package/src/views/releases/v2.8.1.ejs +7 -0
- package/src/views/releases/v2.8.2.ejs +26 -0
- package/src/views/releases/v2.9.0.ejs +32 -0
- package/src/views/releases/v2.9.1.ejs +10 -0
- package/src/views/releases.ejs +26 -0
- package/src/views/sitemap.ejs +36 -0
- package/src/views/support.ejs +14 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const helpers = require('../util/helpers.js'),
|
|
4
|
+
errors = require('../util/errors.js');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An abstraction for loading imposters from in-memory
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
function repeatsFor (response) {
|
|
12
|
+
return response.repeat || 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function repeatTransform (responses) {
|
|
16
|
+
const result = [];
|
|
17
|
+
let response, repeats;
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < responses.length; i += 1) {
|
|
20
|
+
response = responses[i];
|
|
21
|
+
repeats = repeatsFor(response);
|
|
22
|
+
for (let j = 0; j < repeats; j += 1) {
|
|
23
|
+
result.push(response);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function createResponse (responseConfig, stubIndexFn) {
|
|
30
|
+
const cloned = helpers.clone(responseConfig || { is: {} });
|
|
31
|
+
|
|
32
|
+
cloned.stubIndex = stubIndexFn ? stubIndexFn : () => Promise.resolve(0);
|
|
33
|
+
|
|
34
|
+
return cloned;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function wrap (stub = {}) {
|
|
38
|
+
const cloned = helpers.clone(stub),
|
|
39
|
+
statefulResponses = repeatTransform(cloned.responses || []);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Adds a new response to the stub (e.g. during proxying)
|
|
43
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
44
|
+
* @param {Object} response - the response to add
|
|
45
|
+
* @returns {Object} - the promise
|
|
46
|
+
*/
|
|
47
|
+
cloned.addResponse = async response => {
|
|
48
|
+
cloned.responses = cloned.responses || [];
|
|
49
|
+
cloned.responses.push(response);
|
|
50
|
+
statefulResponses.push(response);
|
|
51
|
+
return response;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Selects the next response from the stub, including repeat behavior and circling back to the beginning
|
|
56
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
57
|
+
* @returns {Object} - the response
|
|
58
|
+
* @returns {Object} - the promise
|
|
59
|
+
*/
|
|
60
|
+
cloned.nextResponse = async () => {
|
|
61
|
+
const responseConfig = statefulResponses.shift();
|
|
62
|
+
|
|
63
|
+
if (responseConfig) {
|
|
64
|
+
statefulResponses.push(responseConfig);
|
|
65
|
+
return createResponse(responseConfig, cloned.stubIndex);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return createResponse();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Records a match for debugging purposes
|
|
74
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
75
|
+
* @param {Object} request - the request
|
|
76
|
+
* @param {Object} response - the response
|
|
77
|
+
* @param {Object} responseConfig - the config that generated the response
|
|
78
|
+
* @param {Number} processingTime - the time to match the predicate and generate the full response
|
|
79
|
+
* @returns {Object} - the promise
|
|
80
|
+
*/
|
|
81
|
+
cloned.recordMatch = async (request, response, responseConfig, processingTime) => {
|
|
82
|
+
cloned.matches = cloned.matches || [];
|
|
83
|
+
cloned.matches.push({
|
|
84
|
+
timestamp: new Date().toJSON(),
|
|
85
|
+
request,
|
|
86
|
+
response,
|
|
87
|
+
responseConfig,
|
|
88
|
+
processingTime
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return cloned;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates the stubs repository for a single imposter
|
|
97
|
+
* @returns {Object}
|
|
98
|
+
*/
|
|
99
|
+
function createStubsRepository () {
|
|
100
|
+
const stubs = [];
|
|
101
|
+
let requests = [];
|
|
102
|
+
|
|
103
|
+
function reindex () {
|
|
104
|
+
// stubIndex() is used to find the right spot to insert recorded
|
|
105
|
+
// proxy responses. We reindex after every state change
|
|
106
|
+
stubs.forEach((stub, index) => {
|
|
107
|
+
stub.stubIndex = async () => index;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns the first stub whose predicates match the filter, or a default one if none match
|
|
113
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
114
|
+
* @param {Function} filter - the filter function
|
|
115
|
+
* @param {Number} startIndex - the index to to start searching
|
|
116
|
+
* @returns {Object}
|
|
117
|
+
*/
|
|
118
|
+
async function first (filter, startIndex = 0) {
|
|
119
|
+
for (let i = startIndex; i < stubs.length; i += 1) {
|
|
120
|
+
if (filter(stubs[i].predicates || [])) {
|
|
121
|
+
return { success: true, stub: stubs[i] };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { success: false, stub: wrap() };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Adds a new stub
|
|
129
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
130
|
+
* @param {Object} stub - the stub to add
|
|
131
|
+
* @returns {Object} - the promise
|
|
132
|
+
*/
|
|
133
|
+
async function add (stub) {
|
|
134
|
+
stubs.push(wrap(stub));
|
|
135
|
+
reindex();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Inserts a new stub at the given index
|
|
140
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
141
|
+
* @param {Object} stub - the stub to insert
|
|
142
|
+
* @param {Number} index - the index to add the stub at
|
|
143
|
+
* @returns {Object} - the promise
|
|
144
|
+
*/
|
|
145
|
+
async function insertAtIndex (stub, index) {
|
|
146
|
+
stubs.splice(index, 0, wrap(stub));
|
|
147
|
+
reindex();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Overwrites the list of stubs with a new list
|
|
152
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
153
|
+
* @param {Object} newStubs - the new list of stubs
|
|
154
|
+
* @returns {Object} - the promise
|
|
155
|
+
*/
|
|
156
|
+
async function overwriteAll (newStubs) {
|
|
157
|
+
while (stubs.length > 0) {
|
|
158
|
+
stubs.pop();
|
|
159
|
+
}
|
|
160
|
+
newStubs.forEach(stub => add(stub));
|
|
161
|
+
reindex();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Overwrites the stub at the given index with the new stub
|
|
166
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
167
|
+
* @param {Object} newStub - the new stub
|
|
168
|
+
* @param {Number} index - the index of the old stuib
|
|
169
|
+
* @returns {Object} - the promise
|
|
170
|
+
*/
|
|
171
|
+
async function overwriteAtIndex (newStub, index) {
|
|
172
|
+
|
|
173
|
+
if (typeof stubs[index] === 'undefined') {
|
|
174
|
+
throw errors.MissingResourceError(`no stub at index ${index}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
stubs[index] = wrap(newStub);
|
|
178
|
+
reindex();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Deletes the stub at the given index
|
|
183
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
184
|
+
* @param {Number} index - the index of the stub to delete
|
|
185
|
+
* @returns {Object} - the promise
|
|
186
|
+
*/
|
|
187
|
+
async function deleteAtIndex (index) {
|
|
188
|
+
if (typeof stubs[index] === 'undefined') {
|
|
189
|
+
throw errors.MissingResourceError(`no stub at index ${index}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
stubs.splice(index, 1);
|
|
193
|
+
reindex();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Returns a JSON-convertible representation
|
|
198
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
199
|
+
* @param {Object} options - The formatting options
|
|
200
|
+
* @param {Boolean} options.debug - If true, includes debug information
|
|
201
|
+
* @returns {Object} - the promise resolving to the JSON object
|
|
202
|
+
*/
|
|
203
|
+
async function toJSON (options = {}) {
|
|
204
|
+
const cloned = helpers.clone(stubs);
|
|
205
|
+
|
|
206
|
+
cloned.forEach(stub => {
|
|
207
|
+
if (!options.debug) {
|
|
208
|
+
delete stub.matches;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return cloned;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function isRecordedResponse (response) {
|
|
216
|
+
return response.is && typeof response.is._proxyResponseTime === 'number';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Removes the saved proxy responses
|
|
221
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
222
|
+
* @returns {Object} - Promise
|
|
223
|
+
*/
|
|
224
|
+
async function deleteSavedProxyResponses () {
|
|
225
|
+
const allStubs = await toJSON();
|
|
226
|
+
allStubs.forEach(stub => {
|
|
227
|
+
stub.responses = stub.responses.filter(response => !isRecordedResponse(response));
|
|
228
|
+
});
|
|
229
|
+
const nonProxyStubs = allStubs.filter(stub => stub.responses.length > 0);
|
|
230
|
+
await overwriteAll(nonProxyStubs);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Adds a request for the imposter
|
|
235
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
236
|
+
* @param {Object} request - the request
|
|
237
|
+
* @returns {Object} - the promise
|
|
238
|
+
*/
|
|
239
|
+
async function addRequest (request) {
|
|
240
|
+
const recordedRequest = helpers.clone(request);
|
|
241
|
+
recordedRequest.timestamp = new Date().toJSON();
|
|
242
|
+
requests.push(recordedRequest);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Returns the saved requests for the imposter
|
|
247
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
248
|
+
* @returns {Object} - the promise resolving to the array of requests
|
|
249
|
+
*/
|
|
250
|
+
async function loadRequests () {
|
|
251
|
+
return requests;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Clears the saved requests list
|
|
256
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
257
|
+
* @param {Object} request - the request
|
|
258
|
+
* @returns {Object} - Promise
|
|
259
|
+
*/
|
|
260
|
+
async function deleteSavedRequests () {
|
|
261
|
+
requests = [];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
count: () => stubs.length,
|
|
266
|
+
first,
|
|
267
|
+
add,
|
|
268
|
+
insertAtIndex,
|
|
269
|
+
overwriteAll,
|
|
270
|
+
overwriteAtIndex,
|
|
271
|
+
deleteAtIndex,
|
|
272
|
+
toJSON,
|
|
273
|
+
deleteSavedProxyResponses,
|
|
274
|
+
addRequest,
|
|
275
|
+
loadRequests,
|
|
276
|
+
deleteSavedRequests
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Creates the repository
|
|
282
|
+
* @returns {Object}
|
|
283
|
+
*/
|
|
284
|
+
function create () {
|
|
285
|
+
const imposters = {},
|
|
286
|
+
stubRepos = {};
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Adds a new imposter
|
|
290
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
291
|
+
* @param {Object} imposter - the imposter to add
|
|
292
|
+
* @returns {Object} - the promise
|
|
293
|
+
*/
|
|
294
|
+
async function add (imposter) {
|
|
295
|
+
if (!imposter.stubs) {
|
|
296
|
+
imposter.stubs = [];
|
|
297
|
+
}
|
|
298
|
+
imposters[String(imposter.port)] = imposter;
|
|
299
|
+
|
|
300
|
+
const promises = (imposter.creationRequest.stubs || []).map(stubsFor(imposter.port).add);
|
|
301
|
+
await Promise.all(promises);
|
|
302
|
+
return imposter;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Gets the imposter by id
|
|
307
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
308
|
+
* @param {Number} id - the id of the imposter (e.g. the port)
|
|
309
|
+
* @returns {Object} - the imposter
|
|
310
|
+
*/
|
|
311
|
+
async function get (id) {
|
|
312
|
+
return imposters[String(id)] || null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Gets all imposters
|
|
317
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
318
|
+
* @returns {Object} - all imposters keyed by port
|
|
319
|
+
*/
|
|
320
|
+
async function all () {
|
|
321
|
+
return Promise.all(Object.keys(imposters).map(get));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Returns whether an imposter at the given id exists or not
|
|
326
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
327
|
+
* @param {Number} id - the id (e.g. the port)
|
|
328
|
+
* @returns {boolean}
|
|
329
|
+
*/
|
|
330
|
+
async function exists (id) {
|
|
331
|
+
return typeof imposters[String(id)] !== 'undefined';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Deletes the imposter at the given id
|
|
336
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
337
|
+
* @param {Number} id - the id (e.g. the port)
|
|
338
|
+
* @returns {Object} - the deletion promise
|
|
339
|
+
*/
|
|
340
|
+
async function del (id) {
|
|
341
|
+
const result = imposters[String(id)] || null;
|
|
342
|
+
delete imposters[String(id)];
|
|
343
|
+
delete stubRepos[String(id)];
|
|
344
|
+
|
|
345
|
+
if (result) {
|
|
346
|
+
await result.stop();
|
|
347
|
+
}
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Deletes all imposters synchronously; used during shutdown
|
|
353
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
354
|
+
*/
|
|
355
|
+
function stopAllSync () {
|
|
356
|
+
Object.keys(imposters).forEach(id => {
|
|
357
|
+
imposters[id].stop();
|
|
358
|
+
delete imposters[id];
|
|
359
|
+
delete stubRepos[id];
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Deletes all imposters
|
|
365
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
366
|
+
* @returns {Object} - the deletion promise
|
|
367
|
+
*/
|
|
368
|
+
async function deleteAll () {
|
|
369
|
+
const ids = Object.keys(imposters),
|
|
370
|
+
promises = ids.map(id => imposters[id].stop());
|
|
371
|
+
|
|
372
|
+
ids.forEach(id => {
|
|
373
|
+
delete imposters[id];
|
|
374
|
+
delete stubRepos[id];
|
|
375
|
+
});
|
|
376
|
+
await Promise.all(promises);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Returns the stub repository for the given id
|
|
381
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
382
|
+
* @param {Number} id - the imposter's id
|
|
383
|
+
* @returns {Object} - the stub repository
|
|
384
|
+
*/
|
|
385
|
+
function stubsFor (id) {
|
|
386
|
+
// In practice, the stubsFor call occurs before the imposter is actually added...
|
|
387
|
+
if (!stubRepos[String(id)]) {
|
|
388
|
+
stubRepos[String(id)] = createStubsRepository();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return stubRepos[String(id)];
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Called at startup to load saved imposters.
|
|
396
|
+
* Does nothing for in memory repository
|
|
397
|
+
* @memberOf module:models/inMemoryImpostersRepository#
|
|
398
|
+
* @returns {Object} - a promise
|
|
399
|
+
*/
|
|
400
|
+
async function loadAll () {
|
|
401
|
+
return Promise.resolve();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
add,
|
|
406
|
+
get,
|
|
407
|
+
all,
|
|
408
|
+
exists,
|
|
409
|
+
del,
|
|
410
|
+
stopAllSync,
|
|
411
|
+
deleteAll,
|
|
412
|
+
stubsFor,
|
|
413
|
+
createStubsRepository,
|
|
414
|
+
loadAll
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
module.exports = { create };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const jsonPathPlus = require('jsonpath-plus'),
|
|
4
|
+
helpers = require('../util/helpers.js');
|
|
5
|
+
|
|
6
|
+
const { JSONPath } = jsonPathPlus;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Shared logic for xpath selector
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns xpath value(s) from given xml
|
|
15
|
+
* @param {String} selector - The xpath selector
|
|
16
|
+
* @param {String} possibleJSON - the JSON string
|
|
17
|
+
* @param {Logger} logger - Optional, used to log JSON parsing errors
|
|
18
|
+
* @returns {Object}
|
|
19
|
+
*/
|
|
20
|
+
function select (selector, possibleJSON, logger) {
|
|
21
|
+
const isObject = helpers.isObject;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const json = isObject(possibleJSON) ? possibleJSON : JSON.parse(possibleJSON),
|
|
25
|
+
result = JSONPath(selector, json);
|
|
26
|
+
if (typeof result === 'string') {
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
else if (result.length === 0) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
if (logger) {
|
|
38
|
+
logger.warn(`Cannot parse as JSON: ${JSON.stringify(possibleJSON)}`);
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { select };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const errors = require('../util/errors.js'),
|
|
4
|
+
util = require('util');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Helper functions to navigate the mountebank API for out of process implementations.
|
|
8
|
+
* Used to adapt the built-in (in-process) protocols to out of process.
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
function createLogger (loglevel) {
|
|
13
|
+
const result = {},
|
|
14
|
+
levels = ['debug', 'info', 'warn', 'error'];
|
|
15
|
+
|
|
16
|
+
levels.forEach((level, index) => {
|
|
17
|
+
if (index < levels.indexOf(loglevel)) {
|
|
18
|
+
result[level] = () => {};
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
result[level] = function () {
|
|
22
|
+
const args = Array.prototype.slice.call(arguments),
|
|
23
|
+
message = util.format.apply(this, args);
|
|
24
|
+
|
|
25
|
+
console.log(`${level} ${message}`);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function postJSON (what, where) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const parts = new URL(where),
|
|
35
|
+
driver = require(parts.protocol.replace(':', '')),
|
|
36
|
+
options = {
|
|
37
|
+
hostname: parts.hostname,
|
|
38
|
+
port: parts.port,
|
|
39
|
+
path: parts.pathname,
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: { 'Content-Type': 'application/json' }
|
|
42
|
+
},
|
|
43
|
+
request = driver.request(options, response => {
|
|
44
|
+
const packets = [];
|
|
45
|
+
|
|
46
|
+
response.on('data', chunk => packets.push(chunk));
|
|
47
|
+
|
|
48
|
+
response.on('end', () => {
|
|
49
|
+
const buffer = Buffer.concat(packets),
|
|
50
|
+
body = buffer.toString('utf8');
|
|
51
|
+
|
|
52
|
+
if (response.statusCode !== 200) {
|
|
53
|
+
reject(errors.CommunicationError({
|
|
54
|
+
statusCode: response.statusCode,
|
|
55
|
+
body: body
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
resolve(JSON.parse(body));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
request.on('error', reject);
|
|
65
|
+
request.write(JSON.stringify(what));
|
|
66
|
+
request.end();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function create (config) {
|
|
71
|
+
let callbackURL,
|
|
72
|
+
proxy;
|
|
73
|
+
|
|
74
|
+
function setPort (port) {
|
|
75
|
+
callbackURL = config.callbackURLTemplate.replace(':port', port);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function setProxy (value) {
|
|
79
|
+
proxy = value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function logger () {
|
|
83
|
+
return createLogger(config.loglevel);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function getProxyResponse (proxyConfig, request, proxyCallbackURL, requestDetails) {
|
|
87
|
+
const response = await proxy.to(proxyConfig.to, request, proxyConfig, requestDetails);
|
|
88
|
+
return postJSON({ proxyResponse: response }, proxyCallbackURL);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function getResponse (request, requestDetails) {
|
|
92
|
+
const mbResponse = await postJSON({ request, requestDetails }, callbackURL);
|
|
93
|
+
if (mbResponse.proxy) {
|
|
94
|
+
return getProxyResponse(mbResponse.proxy, mbResponse.request, mbResponse.callbackURL, requestDetails);
|
|
95
|
+
}
|
|
96
|
+
else if (mbResponse.response) {
|
|
97
|
+
return mbResponse.response;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return mbResponse;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { getResponse, setPort, setProxy, logger };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { create };
|