@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,66 @@
|
|
|
1
|
+
<p>Let's create a text-based imposter with multiple stubs. Binary imposters
|
|
2
|
+
cannot use the <code>matches</code> predicate.</p>
|
|
3
|
+
|
|
4
|
+
<testScenario name='tcp matches example'>
|
|
5
|
+
<step type='http'>
|
|
6
|
+
<pre><code>POST /imposters HTTP/1.1
|
|
7
|
+
Host: localhost:<%= port %>
|
|
8
|
+
Accept: application/json
|
|
9
|
+
Content-Type: application/json
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
"port": 4550,
|
|
13
|
+
"protocol": "tcp",
|
|
14
|
+
"mode": "text",
|
|
15
|
+
"stubs": [<strong class='highlight1'>
|
|
16
|
+
{
|
|
17
|
+
"responses": [{ "is": { "data": "first response" } }],
|
|
18
|
+
"predicates": [{
|
|
19
|
+
"matches": { "data": "^first\\Wsecond" },
|
|
20
|
+
"caseSensitive": true
|
|
21
|
+
}]
|
|
22
|
+
}</strong>,<strong class='highlight2'>
|
|
23
|
+
{
|
|
24
|
+
"responses": [{ "is": { "data": "second response" } }],
|
|
25
|
+
"predicates": [{ "matches": { "data": "second\\s+request" } }]
|
|
26
|
+
}</strong>,<strong class='highlight3'>
|
|
27
|
+
{
|
|
28
|
+
"responses": [{ "is": { "data": "third response" } }],
|
|
29
|
+
"predicates": [{ "matches": { "data": "second\\s+request" } }]
|
|
30
|
+
}</strong>
|
|
31
|
+
]
|
|
32
|
+
}</code></pre>
|
|
33
|
+
</step>
|
|
34
|
+
|
|
35
|
+
<p>The first stub requires a case-sensitive match on a string starting with
|
|
36
|
+
"first", followed by a non-word character, followed by "second":</p>
|
|
37
|
+
|
|
38
|
+
<step type='exec'>
|
|
39
|
+
<pre><code>echo '<strong class='highlight1'>first second</strong>' | nc localhost 4550</code></pre>
|
|
40
|
+
|
|
41
|
+
<assertResponse>
|
|
42
|
+
<pre><code><strong class='highlight1'>first response</strong></code></pre>
|
|
43
|
+
</assertResponse>
|
|
44
|
+
</step>
|
|
45
|
+
|
|
46
|
+
<p>The second stub is not case-sensitive.</p>
|
|
47
|
+
|
|
48
|
+
<step type='exec'>
|
|
49
|
+
<pre><code>echo '<strong class='highlight2'>Second Request</strong>' | nc localhost 4550</code></pre>
|
|
50
|
+
|
|
51
|
+
<assertResponse>
|
|
52
|
+
<pre><code><strong class='highlight2'>second response</strong></code></pre>
|
|
53
|
+
</assertResponse>
|
|
54
|
+
</step>
|
|
55
|
+
|
|
56
|
+
<p>The third stub will never run, since it matches the same requests as the
|
|
57
|
+
second stub. mountebank always chooses the first stub that matches based on
|
|
58
|
+
the order you add them to the <code>stubs</code> array when creating the
|
|
59
|
+
imposter.</p>
|
|
60
|
+
|
|
61
|
+
<step type='http'>
|
|
62
|
+
<code class='hidden'>DELETE /imposters/4550 HTTP/1.1
|
|
63
|
+
Host: localhost:<%= port %>
|
|
64
|
+
Accept: application/json</code>
|
|
65
|
+
</step>
|
|
66
|
+
</testScenario>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<p>The <code>not</code> predicate negates its child predicate.</p>
|
|
2
|
+
|
|
3
|
+
<testScenario name='tcp not example'>
|
|
4
|
+
<step type='http'>
|
|
5
|
+
<pre><code>POST /imposters HTTP/1.1
|
|
6
|
+
Host: localhost:<%= port %>
|
|
7
|
+
Accept: application/json
|
|
8
|
+
Content-Type: application/json
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
"port": 4552,
|
|
12
|
+
"protocol": "tcp",
|
|
13
|
+
"mode": "text",
|
|
14
|
+
"stubs": [<strong class='highlight1'>
|
|
15
|
+
{
|
|
16
|
+
"responses": [{ "is": { "data": "not test" } }],
|
|
17
|
+
"predicates": [{ "not": { "equals": { "data": "test\n" } } }]
|
|
18
|
+
}</strong>,<strong class='highlight2'>
|
|
19
|
+
{
|
|
20
|
+
"responses": [{ "is": { "data": "test" } }],
|
|
21
|
+
"predicates": [{ "equals": { "data": "test\n" } }]
|
|
22
|
+
}</strong>
|
|
23
|
+
]
|
|
24
|
+
}</code></pre>
|
|
25
|
+
</step>
|
|
26
|
+
|
|
27
|
+
<p>The first stub matches if the <code>is</code> sub-predicate does not match:</p>
|
|
28
|
+
|
|
29
|
+
<step type='exec'>
|
|
30
|
+
<pre><code>echo '<strong class='highlight1'>production</strong>' | nc localhost 4552</code></pre>
|
|
31
|
+
|
|
32
|
+
<assertResponse>
|
|
33
|
+
<pre><code><strong class='highlight1'>not test</strong></code></pre>
|
|
34
|
+
</assertResponse>
|
|
35
|
+
</step>
|
|
36
|
+
|
|
37
|
+
<p>As expected, the second stub matches if the <code>is</code> sub-predicate does match:</p>
|
|
38
|
+
|
|
39
|
+
<step type='exec'>
|
|
40
|
+
<pre><code>echo '<strong class='highlight1'>test</strong>' | nc localhost 4552</code></pre>
|
|
41
|
+
|
|
42
|
+
<assertResponse>
|
|
43
|
+
<pre><code><strong class='highlight1'>test</strong></code></pre>
|
|
44
|
+
</assertResponse>
|
|
45
|
+
</step>
|
|
46
|
+
|
|
47
|
+
<step type='http'>
|
|
48
|
+
<code class='hidden'>DELETE /imposters/4552 HTTP/1.1
|
|
49
|
+
Host: localhost:<%= port %>
|
|
50
|
+
Accept: application/json</code>
|
|
51
|
+
</step>
|
|
52
|
+
</testScenario>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<p>The <code>or</code> predicate matches if any of its sub-predicates match.</p>
|
|
2
|
+
|
|
3
|
+
<testScenario name='tcp or example'>
|
|
4
|
+
<step type='http'>
|
|
5
|
+
<pre><code>POST /imposters HTTP/1.1
|
|
6
|
+
Host: localhost:<%= port %>
|
|
7
|
+
Accept: application/json
|
|
8
|
+
Content-Type: application/json
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
"port": 4553,
|
|
12
|
+
"protocol": "tcp",
|
|
13
|
+
"mode": "text",
|
|
14
|
+
"stubs": [<strong class='highlight1'>
|
|
15
|
+
{
|
|
16
|
+
"responses": [{ "is": { "data": "matches" } }],
|
|
17
|
+
"predicates": [
|
|
18
|
+
{
|
|
19
|
+
"or": [
|
|
20
|
+
{ "startsWith": { "data": "start" } },
|
|
21
|
+
{ "endsWith": { "data": "end\n" } },
|
|
22
|
+
{ "contains": { "data": "middle" } }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}</strong>
|
|
27
|
+
]
|
|
28
|
+
}</code></pre>
|
|
29
|
+
</step>
|
|
30
|
+
|
|
31
|
+
<p>The stub matches if the first sub-predicate matches:</p>
|
|
32
|
+
|
|
33
|
+
<step type='exec'>
|
|
34
|
+
<pre><code>echo '<strong class='highlight1'>start data transmission</strong>' | nc localhost 4553</code></pre>
|
|
35
|
+
|
|
36
|
+
<assertResponse>
|
|
37
|
+
<pre><code><strong class='highlight1'>matches</strong></code></pre>
|
|
38
|
+
</assertResponse>
|
|
39
|
+
</step>
|
|
40
|
+
|
|
41
|
+
<p>The stub matches if the second sub-predicate matches:</p>
|
|
42
|
+
|
|
43
|
+
<step type='exec'>
|
|
44
|
+
<pre><code>echo '<strong class='highlight1'>data transmission end</strong>' | nc localhost 4553</code></pre>
|
|
45
|
+
|
|
46
|
+
<assertResponse>
|
|
47
|
+
<pre><code><strong class='highlight1'>matches</strong></code></pre>
|
|
48
|
+
</assertResponse>
|
|
49
|
+
</step>
|
|
50
|
+
|
|
51
|
+
<p>The stub matches if the last sub-predicate matches:</p>
|
|
52
|
+
|
|
53
|
+
<step type='exec'>
|
|
54
|
+
<pre><code>echo '<strong class='highlight1'>data middle transmission</strong>' | nc localhost 4553</code></pre>
|
|
55
|
+
|
|
56
|
+
<assertResponse>
|
|
57
|
+
<pre><code><strong class='highlight1'>matches</strong></code></pre>
|
|
58
|
+
</assertResponse>
|
|
59
|
+
</step>
|
|
60
|
+
|
|
61
|
+
<p>The stub does not match none of the sub-predicates match...</p>
|
|
62
|
+
|
|
63
|
+
<step type='exec'>
|
|
64
|
+
<pre><code>echo '<strong class='highlight1'>data transmission</strong>' | nc localhost 4553</code></pre>
|
|
65
|
+
|
|
66
|
+
<p>...which yields an empty response:</p>
|
|
67
|
+
|
|
68
|
+
<assertResponse>
|
|
69
|
+
<pre><code>
|
|
70
|
+
</code></pre>
|
|
71
|
+
</assertResponse>
|
|
72
|
+
</step>
|
|
73
|
+
|
|
74
|
+
<step type='http'>
|
|
75
|
+
<code class='hidden'>DELETE /imposters/4553 HTTP/1.1
|
|
76
|
+
Host: localhost:<%= port %>
|
|
77
|
+
Accept: application/json</code>
|
|
78
|
+
</step>
|
|
79
|
+
</testScenario>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<p>Let's create a text-based imposter with multiple stubs. Binary imposters won't
|
|
2
|
+
see any interesting behavior difference with only <code>startsWith</code> predicates:</p>
|
|
3
|
+
|
|
4
|
+
<testScenario name='tcp startsWith example'>
|
|
5
|
+
<step type='http'>
|
|
6
|
+
<pre><code>POST /imposters HTTP/1.1
|
|
7
|
+
Host: localhost:<%= port %>
|
|
8
|
+
Accept: application/json
|
|
9
|
+
Content-Type: application/json
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
"port": 4548,
|
|
13
|
+
"protocol": "tcp",
|
|
14
|
+
"mode": "text",
|
|
15
|
+
"stubs": [<strong class='highlight1'>
|
|
16
|
+
{
|
|
17
|
+
"responses": [{ "is": { "data": "first response" } }],
|
|
18
|
+
"predicates": [{ "startsWith": { "data": "first" } }]
|
|
19
|
+
}</strong>,<strong class='highlight2'>
|
|
20
|
+
{
|
|
21
|
+
"responses": [{ "is": { "data": "second response" } }],
|
|
22
|
+
"predicates": [{ "startsWith": { "data": "second" } }]
|
|
23
|
+
}</strong>,<strong class='highlight3'>
|
|
24
|
+
{
|
|
25
|
+
"responses": [{ "is": { "data": "third response" } }],
|
|
26
|
+
"predicates": [{ "startsWith": { "data": "second" } }]
|
|
27
|
+
}</strong>
|
|
28
|
+
]
|
|
29
|
+
}</code></pre>
|
|
30
|
+
</step>
|
|
31
|
+
|
|
32
|
+
<p>The match is not case-sensitive:</p>
|
|
33
|
+
|
|
34
|
+
<step type='exec'>
|
|
35
|
+
<pre><code>echo '<strong class='highlight1'>FIRST REQUEST</strong>' | nc localhost 4548</code></pre>
|
|
36
|
+
|
|
37
|
+
<assertResponse>
|
|
38
|
+
<pre><code><strong class='highlight1'>first response</strong></code></pre>
|
|
39
|
+
</assertResponse>
|
|
40
|
+
</step>
|
|
41
|
+
|
|
42
|
+
<p>The same is true for the second stub.</p>
|
|
43
|
+
|
|
44
|
+
<step type='exec'>
|
|
45
|
+
<pre><code>echo '<strong class='highlight2'>Second Request</strong>' | nc localhost 4548</code></pre>
|
|
46
|
+
|
|
47
|
+
<assertResponse>
|
|
48
|
+
<pre><code><strong class='highlight2'>second response</strong></code></pre>
|
|
49
|
+
</assertResponse>
|
|
50
|
+
</step>
|
|
51
|
+
|
|
52
|
+
<p>The third stub will never run, since it matches the same requests as the
|
|
53
|
+
second stub. mountebank always chooses the first stub that matches based on
|
|
54
|
+
the order you add them to the <code>stubs</code> array when creating the
|
|
55
|
+
imposter.</p>
|
|
56
|
+
|
|
57
|
+
<step type='http'>
|
|
58
|
+
<code class='hidden'>DELETE /imposters/4548 HTTP/1.1
|
|
59
|
+
Host: localhost:<%= port %>
|
|
60
|
+
Accept: application/json</code>
|
|
61
|
+
</step>
|
|
62
|
+
</testScenario>
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
<%
|
|
2
|
+
title = 'predicates'
|
|
3
|
+
description = 'The predicates supported by mountebank'
|
|
4
|
+
%>
|
|
5
|
+
|
|
6
|
+
<%- include('../../_header') -%>
|
|
7
|
+
|
|
8
|
+
<h1>Predicates</h1>
|
|
9
|
+
|
|
10
|
+
<p>In the absence of a predicate, a stub always matches, and there's never a reason to
|
|
11
|
+
add more than one stub to an imposter. Predicates allow imposters to have much richer
|
|
12
|
+
behavior by defining whether or not a stub matches a request. When multiple stubs are
|
|
13
|
+
created on an imposter, the first stub that matches is selected.</p>
|
|
14
|
+
|
|
15
|
+
<p>Each predicate object contains one or more of the request fields as keys. Predicates
|
|
16
|
+
are added to a stub in an array, and all predicates are AND'd together. The following
|
|
17
|
+
predicate operators are allowed:</p>
|
|
18
|
+
|
|
19
|
+
<table>
|
|
20
|
+
<tr>
|
|
21
|
+
<th>Operator</th>
|
|
22
|
+
<th>Description</th>
|
|
23
|
+
</tr>
|
|
24
|
+
<tr>
|
|
25
|
+
<td><code>equals</code></td>
|
|
26
|
+
<td>The request field matches the predicate</td>
|
|
27
|
+
</tr>
|
|
28
|
+
<tr>
|
|
29
|
+
<td><code>deepEquals</code></td>
|
|
30
|
+
<td>Performs nested set equality on the request field, useful when
|
|
31
|
+
the request field is an object (e.g. the <code>query</code> field in http)</td>
|
|
32
|
+
</tr>
|
|
33
|
+
<tr>
|
|
34
|
+
<td><code>contains</code></td>
|
|
35
|
+
<td>The request field contains the predicate</td>
|
|
36
|
+
</tr>
|
|
37
|
+
<tr>
|
|
38
|
+
<td><code>startsWith</code></td>
|
|
39
|
+
<td>The request field starts with the predicate</td>
|
|
40
|
+
</tr>
|
|
41
|
+
<tr>
|
|
42
|
+
<td><code>endsWith</code></td>
|
|
43
|
+
<td>The request field ends with the predicate</td>
|
|
44
|
+
</tr>
|
|
45
|
+
<tr>
|
|
46
|
+
<td><code>matches</code></td>
|
|
47
|
+
<td>The request field matches the JavaScript regular expression defined
|
|
48
|
+
with the predicate.</td>
|
|
49
|
+
</tr>
|
|
50
|
+
<tr>
|
|
51
|
+
<td><code>exists</code></td>
|
|
52
|
+
<td>If <code>true</code>, the request field must exist. If <code>false</code>,
|
|
53
|
+
the request field must not exist.</td>
|
|
54
|
+
</tr>
|
|
55
|
+
<tr>
|
|
56
|
+
<td><code>not</code></td>
|
|
57
|
+
<td>Inverts a predicate</td>
|
|
58
|
+
</tr>
|
|
59
|
+
<tr>
|
|
60
|
+
<td><code>or</code></td>
|
|
61
|
+
<td>Logically or's two predicates together</td>
|
|
62
|
+
</tr>
|
|
63
|
+
<tr>
|
|
64
|
+
<td><code>and</code></td>
|
|
65
|
+
<td>Logically and's two predicates together</td>
|
|
66
|
+
</tr>
|
|
67
|
+
<tr>
|
|
68
|
+
<td><code>inject</code></td>
|
|
69
|
+
<td>Injects JavaScript to decide whether the request matches or not.
|
|
70
|
+
See the <a href='/docs/api/injection'>injection</a> page for more details.</td>
|
|
71
|
+
</tr>
|
|
72
|
+
</table>
|
|
73
|
+
|
|
74
|
+
<p>Predicates can be parameterized. mountebank accepts the following
|
|
75
|
+
predicate parameters:</p>
|
|
76
|
+
|
|
77
|
+
<table>
|
|
78
|
+
<tr>
|
|
79
|
+
<th>Parameter</th>
|
|
80
|
+
<th>Default</th>
|
|
81
|
+
<th>Description</th>
|
|
82
|
+
</tr>
|
|
83
|
+
<tr>
|
|
84
|
+
<td><code>caseSensitive</code></td>
|
|
85
|
+
<td><code>false</code></td>
|
|
86
|
+
<td>Determines if the match is case sensitive or not. This includes keys
|
|
87
|
+
for objects such as query parameters.</td>
|
|
88
|
+
</tr>
|
|
89
|
+
<tr>
|
|
90
|
+
<td><code>except</code></td>
|
|
91
|
+
<td><code>""</code></td>
|
|
92
|
+
<td>Defines a regular expression that is stripped out of the request field
|
|
93
|
+
before matching.</td>
|
|
94
|
+
</tr>
|
|
95
|
+
<tr>
|
|
96
|
+
<td><code>xpath</code></td>
|
|
97
|
+
<td><code>null</code></td>
|
|
98
|
+
<td>Defines an object containing a <code>selector</code> string and, optionally, an
|
|
99
|
+
<code>ns</code> object field that defines a namespace map. The predicate's
|
|
100
|
+
scope is limited to the selected value in the request field.</td>
|
|
101
|
+
</tr>
|
|
102
|
+
<tr>
|
|
103
|
+
<td><code>jsonpath</code></td>
|
|
104
|
+
<td><code>null</code></td>
|
|
105
|
+
<td>Defines an object containing a <code>selector</code> string. The predicate's
|
|
106
|
+
scope is limited to the selected value in the request field.</td>
|
|
107
|
+
</tr>
|
|
108
|
+
</table>
|
|
109
|
+
|
|
110
|
+
<p>See the <code>equals</code> example below to see the <code>caseSensitive</code> and
|
|
111
|
+
<code>except</code> parameters in action. See the <a href='/docs/api/xpath'>xpath page</a>
|
|
112
|
+
for <code>xpath</code> examples. See the <a href='/docs/api/jsonpath'>jsonpath page</a>
|
|
113
|
+
for <code>jsonpath</code> examples.</p>
|
|
114
|
+
|
|
115
|
+
<p>Almost all predicates are scoped to a request field; see the protocol pages linked to from
|
|
116
|
+
the sidebar to see the request fields. <code>inject</code> is the sole exception. It takes
|
|
117
|
+
a string function that accepts the entire request. See the
|
|
118
|
+
<a href='/docs/api/injection'>injection</a> page for details.</p>
|
|
119
|
+
|
|
120
|
+
<p>The predicates work intuitively for base64-encoded binary data as well by internally
|
|
121
|
+
converting the base64-encoded string to a JSON string representing the byte array. For example,
|
|
122
|
+
sending "AQIDBA==" will get translated to "[1,2,3,4]", and predicates expecting "AgM=" will
|
|
123
|
+
get translated to "[2,3]". Even though "AQIDBA==" does not contain "AgM=", a <code>contains</code>
|
|
124
|
+
predicate will match, because "[1,2,3,4]" does contain "[2,3]". This works well for everything
|
|
125
|
+
but <code>matches</code>, because any regular expression operators get encoded as binary.
|
|
126
|
+
mountebank recommends that you stay away from <code>matches</code> if you're dealing in binary.
|
|
127
|
+
In mountebank's experience, <code>contains</code> is the most useful predicate for binary
|
|
128
|
+
imposters, as even binary RPC data generally contains strings representing method names.</p>
|
|
129
|
+
|
|
130
|
+
<h2 id='array-match'>Matching arrays</h2>
|
|
131
|
+
|
|
132
|
+
<p>On occasion you may encounter multi-valued keys. This can be the case with querystrings
|
|
133
|
+
and HTTP headers that have repeating keys, for example <code>?key=first&key=second</code>.
|
|
134
|
+
In those cases, <code>deepEquals</code> will require all the values (in any order) to match.
|
|
135
|
+
All other predicates will match if any value matches, so an <code>equals</code> predicate
|
|
136
|
+
will match with the value of <code>second</code> in the example above.</p>
|
|
137
|
+
|
|
138
|
+
<p><a href='/docs/api/json'>JSON predicates</a> are also allowed, for example, to match HTTP
|
|
139
|
+
bodies. When the body contains an array, the logic above still applies: <code>deepEquals</code>
|
|
140
|
+
requires all values to match, and other predicates will match if any value in the array
|
|
141
|
+
matches.</p>
|
|
142
|
+
|
|
143
|
+
<p>You also have the option of specifying an array in the predicate definition. If you do so,
|
|
144
|
+
then <em>all</em> fields in the predicate array must match (in any order). Most predicates
|
|
145
|
+
will match even if there are additional fields in the actual request array; <code>deepEquals</code>
|
|
146
|
+
requires the array lengths to be equal. The following example shows this with a querystring:</p>
|
|
147
|
+
|
|
148
|
+
<testScenario name='array'>
|
|
149
|
+
<step type='http'>
|
|
150
|
+
<pre><code>POST /imposters HTTP/1.1
|
|
151
|
+
Host: localhost:<%= port %>
|
|
152
|
+
Accept: application/json
|
|
153
|
+
Content-Type: application/json
|
|
154
|
+
|
|
155
|
+
{
|
|
156
|
+
"port": 3333,
|
|
157
|
+
"protocol": "http",
|
|
158
|
+
"stubs": [
|
|
159
|
+
{
|
|
160
|
+
"predicates": [{
|
|
161
|
+
"<strong class='highlight1'>deepEquals</strong>": {
|
|
162
|
+
"query": { "key": <strong class='highlight1'>["first", "second"]</strong> }
|
|
163
|
+
}
|
|
164
|
+
}],
|
|
165
|
+
"responses": [{
|
|
166
|
+
"is": {
|
|
167
|
+
"body": <strong class='highlight1'>"Entire array matched"</strong>
|
|
168
|
+
}
|
|
169
|
+
}]
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"predicates": [{
|
|
173
|
+
"<strong class='highlight2'>equals</strong>": {
|
|
174
|
+
"query": { "key": <strong class='highlight2'>["first", "second"]</strong> }
|
|
175
|
+
}
|
|
176
|
+
}],
|
|
177
|
+
"responses": [{
|
|
178
|
+
"is": {
|
|
179
|
+
"body": <strong class='highlight2'>"Subset of array matched"</strong>
|
|
180
|
+
}
|
|
181
|
+
}]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"predicates": [{
|
|
185
|
+
"equals": {
|
|
186
|
+
"query": { "key": <strong class='highlight3'>"first"</strong> }
|
|
187
|
+
}
|
|
188
|
+
}],
|
|
189
|
+
"responses": [{
|
|
190
|
+
"is": {
|
|
191
|
+
"body": <strong class='highlight3'>"A field in the array matched"</strong>
|
|
192
|
+
}
|
|
193
|
+
}]
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}</code></pre>
|
|
197
|
+
</step>
|
|
198
|
+
|
|
199
|
+
<p>First let's call the imposter matching both keys in the <code>deepEquals</code> predicate. For it
|
|
200
|
+
to match, no other keys must be present, although the order does not matter.</p>
|
|
201
|
+
|
|
202
|
+
<step type='http'>
|
|
203
|
+
<pre><code>GET /path<strong class='highlight1'>?key=second&key=first</strong> HTTP/1.1
|
|
204
|
+
Host: localhost:3333</code></pre>
|
|
205
|
+
|
|
206
|
+
<p>Since both keys match and there are no extraneous keys, we get our expected response from the first stub.</p>
|
|
207
|
+
|
|
208
|
+
<assertResponse>
|
|
209
|
+
<pre><code>HTTP/1.1 200 OK
|
|
210
|
+
Connection: close
|
|
211
|
+
Date: <volatile>Sat, 06 May 2017 02:30:31 GMT</volatile>
|
|
212
|
+
Transfer-Encoding: chunked
|
|
213
|
+
|
|
214
|
+
<strong class='highlight1'>Entire array matched</strong></code></pre>
|
|
215
|
+
</assertResponse>
|
|
216
|
+
</step>
|
|
217
|
+
|
|
218
|
+
<p>If we add a third key not specified by the predicate, it no longer matches the <code>deepEquals</code>
|
|
219
|
+
predicate. It does, however, match the <code>equals</code> predicate, which supports matching a subset of arrays.</p>
|
|
220
|
+
|
|
221
|
+
<step type='http'>
|
|
222
|
+
<pre><code>GET /path<strong class='highlight2'>?key=second&key=first</strong>&key=third HTTP/1.1
|
|
223
|
+
Host: localhost:3333</code></pre>
|
|
224
|
+
|
|
225
|
+
<p>Since both keys match, we get our expected response from the first stub.</p>
|
|
226
|
+
|
|
227
|
+
<assertResponse>
|
|
228
|
+
<pre><code>HTTP/1.1 200 OK
|
|
229
|
+
Connection: close
|
|
230
|
+
Date: <volatile>Sat, 06 May 2017 02:30:31 GMT</volatile>
|
|
231
|
+
Transfer-Encoding: chunked
|
|
232
|
+
|
|
233
|
+
<strong class='highlight2'>Subset of array matched</strong></code></pre>
|
|
234
|
+
</assertResponse>
|
|
235
|
+
</step>
|
|
236
|
+
|
|
237
|
+
<p>If the request is missing either array value specified in the predicate, it no longer matches.</p>
|
|
238
|
+
|
|
239
|
+
<step type='http'>
|
|
240
|
+
<pre><code>GET /path?<strong class='highlight3'>key=first</strong>&key=third HTTP/1.1
|
|
241
|
+
Host: localhost:3333</code></pre>
|
|
242
|
+
|
|
243
|
+
<p>In this case, our third stub matches, because it does <em>not</em> use an array predicate, and
|
|
244
|
+
one of the actual array values in the request matches the predicate definition</p>
|
|
245
|
+
|
|
246
|
+
<assertResponse>
|
|
247
|
+
<pre><code>HTTP/1.1 200 OK
|
|
248
|
+
Connection: close
|
|
249
|
+
Date: <volatile>Sat, 06 May 2017 02:30:31 GMT</volatile>
|
|
250
|
+
Transfer-Encoding: chunked
|
|
251
|
+
|
|
252
|
+
<strong class='highlight3'>A field in the array matched</strong></code></pre>
|
|
253
|
+
</assertResponse>
|
|
254
|
+
</step>
|
|
255
|
+
|
|
256
|
+
<step type='http'>
|
|
257
|
+
<code class='hidden'>DELETE /imposters/3333 HTTP/1.1
|
|
258
|
+
Host: localhost:<%= port %>
|
|
259
|
+
Accept: application/json</code>
|
|
260
|
+
</step>
|
|
261
|
+
</testScenario>
|
|
262
|
+
|
|
263
|
+
<h2>Matching XML or JSON</h2>
|
|
264
|
+
|
|
265
|
+
<p>mountebank has special support for matching XML and JSON request fields, such as in an <code>http body</code>
|
|
266
|
+
or <code>tcp data</code> field. Where XML or JSON predicates are used against <code>string</code> fields,
|
|
267
|
+
mountebank will attempt to parse the field as XML or JSON and apply the given predicate. If he is unable to
|
|
268
|
+
parse the field, the predicate will fail; otherwise it will pass or fail according to the selected value.</p>
|
|
269
|
+
|
|
270
|
+
<p>See the <a href='/docs/api/xpath'>xpath page</a> for <code>xpath</code> examples.</p>
|
|
271
|
+
<p>See the <a href='/docs/api/json'>json page</a> for <code>json</code> examples.</p>
|
|
272
|
+
|
|
273
|
+
<h2>Examples</h2>
|
|
274
|
+
|
|
275
|
+
<p>The examples below use both HTTP and TCP imposters. The TCP examples use netcat (<code>nc</code>)
|
|
276
|
+
to send TCP data over a socket, which is like <code>telnet</code>, but makes the output easier to script.
|
|
277
|
+
The examples for binary imposters use the <code>base64</code> command line tool to decode base64
|
|
278
|
+
to binary before sending to the socket.</p>
|
|
279
|
+
|
|
280
|
+
<section class='accordion'>
|
|
281
|
+
<div>
|
|
282
|
+
<a class='section-toggler'
|
|
283
|
+
id='predicates-equals' name='predicates-equals' href='#predicates-equals'>
|
|
284
|
+
equals
|
|
285
|
+
</a>
|
|
286
|
+
<section>
|
|
287
|
+
<%- include('predicates/equals') -%>
|
|
288
|
+
</section>
|
|
289
|
+
</div>
|
|
290
|
+
<div>
|
|
291
|
+
<a class='section-toggler'
|
|
292
|
+
id='predicates-deepEquals' name='predicates-deepEquals' href='#predicates-deepEquals'>
|
|
293
|
+
deepEquals
|
|
294
|
+
</a>
|
|
295
|
+
<section>
|
|
296
|
+
<%- include('predicates/deepEquals') -%>
|
|
297
|
+
</section>
|
|
298
|
+
</div>
|
|
299
|
+
<div>
|
|
300
|
+
<a class='section-toggler'
|
|
301
|
+
id='predicates-contains' name='predicates-contains' href='#predicates-contains'>
|
|
302
|
+
contains
|
|
303
|
+
</a>
|
|
304
|
+
<section>
|
|
305
|
+
<%- include('predicates/contains') -%>
|
|
306
|
+
</section>
|
|
307
|
+
</div>
|
|
308
|
+
<div>
|
|
309
|
+
<a class='section-toggler'
|
|
310
|
+
id='predicates-startsWith' name='predicates-startsWith' href='#predicates-startsWith'>
|
|
311
|
+
startsWith
|
|
312
|
+
</a>
|
|
313
|
+
<section>
|
|
314
|
+
<%- include('predicates/startsWith') -%>
|
|
315
|
+
</section>
|
|
316
|
+
</div>
|
|
317
|
+
<div>
|
|
318
|
+
<a class='section-toggler'
|
|
319
|
+
id='predicates-endsWith' name='predicates-endsWith' href='#predicates-endsWith'>
|
|
320
|
+
endsWith
|
|
321
|
+
</a>
|
|
322
|
+
<section>
|
|
323
|
+
<%- include('predicates/endsWith') -%>
|
|
324
|
+
</section>
|
|
325
|
+
</div>
|
|
326
|
+
<div>
|
|
327
|
+
<a class='section-toggler'
|
|
328
|
+
id='predicates-matches' name='predicates-matches' href='#predicates-matches'>
|
|
329
|
+
matches
|
|
330
|
+
</a>
|
|
331
|
+
<section>
|
|
332
|
+
<%- include('predicates/matches') -%>
|
|
333
|
+
</section>
|
|
334
|
+
</div>
|
|
335
|
+
<div>
|
|
336
|
+
<a class='section-toggler'
|
|
337
|
+
id='predicates-exists' name='predicates-exists' href='#predicates-exists'>
|
|
338
|
+
exists
|
|
339
|
+
</a>
|
|
340
|
+
<section>
|
|
341
|
+
<%- include('predicates/exists') -%>
|
|
342
|
+
</section>
|
|
343
|
+
</div>
|
|
344
|
+
<div>
|
|
345
|
+
<a class='section-toggler'
|
|
346
|
+
id='predicates-not' name='predicates-not' href='#predicates-not'>
|
|
347
|
+
not
|
|
348
|
+
</a>
|
|
349
|
+
<section>
|
|
350
|
+
<%- include('predicates/not') -%>
|
|
351
|
+
</section>
|
|
352
|
+
</div>
|
|
353
|
+
<div>
|
|
354
|
+
<a class='section-toggler'
|
|
355
|
+
id='predicates-or' name='predicates-or' href='#predicates-or'>
|
|
356
|
+
or
|
|
357
|
+
</a>
|
|
358
|
+
<section>
|
|
359
|
+
<%- include('predicates/or') -%>
|
|
360
|
+
</section>
|
|
361
|
+
</div>
|
|
362
|
+
<div>
|
|
363
|
+
<a class='section-toggler'
|
|
364
|
+
id='predicates-and' name='predicates-and' href='#predicates-and'>
|
|
365
|
+
and
|
|
366
|
+
</a>
|
|
367
|
+
<section>
|
|
368
|
+
<%- include('predicates/and') -%>
|
|
369
|
+
</section>
|
|
370
|
+
</div>
|
|
371
|
+
<div>
|
|
372
|
+
<a class='section-toggler'
|
|
373
|
+
id='predicates-inject' name='predicates-inject' href='#predicates-inject'>
|
|
374
|
+
inject
|
|
375
|
+
</a>
|
|
376
|
+
<section>
|
|
377
|
+
<%- include('predicates/inject') -%>
|
|
378
|
+
</section>
|
|
379
|
+
</div>
|
|
380
|
+
</section>
|
|
381
|
+
|
|
382
|
+
<%- include('../../_footer') -%>
|