@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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/bin/mb +136 -0
  4. package/package.json +71 -0
  5. package/releases.json +52 -0
  6. package/src/cli/api.js +112 -0
  7. package/src/cli/cli.js +420 -0
  8. package/src/controllers/configController.js +64 -0
  9. package/src/controllers/feedController.js +115 -0
  10. package/src/controllers/homeController.js +58 -0
  11. package/src/controllers/imposterController.js +328 -0
  12. package/src/controllers/impostersController.js +215 -0
  13. package/src/controllers/logsController.js +52 -0
  14. package/src/models/behaviors.js +553 -0
  15. package/src/models/behaviorsValidator.js +186 -0
  16. package/src/models/compatibility.js +133 -0
  17. package/src/models/dryRunValidator.js +261 -0
  18. package/src/models/filesystemBackedImpostersRepository.js +908 -0
  19. package/src/models/http/baseHttpServer.js +207 -0
  20. package/src/models/http/headersMap.js +87 -0
  21. package/src/models/http/httpProxy.js +230 -0
  22. package/src/models/http/httpRequest.js +82 -0
  23. package/src/models/http/httpServer.js +18 -0
  24. package/src/models/http/index.js +18 -0
  25. package/src/models/https/cert/mb-cert.pem +20 -0
  26. package/src/models/https/cert/mb-csr.pem +16 -0
  27. package/src/models/https/cert/mb-key.pem +27 -0
  28. package/src/models/https/httpsServer.js +42 -0
  29. package/src/models/https/index.js +18 -0
  30. package/src/models/imposter.js +243 -0
  31. package/src/models/imposterPrinter.js +120 -0
  32. package/src/models/impostersRepository.js +49 -0
  33. package/src/models/inMemoryImpostersRepository.js +418 -0
  34. package/src/models/jsonpath.js +44 -0
  35. package/src/models/mbConnection.js +107 -0
  36. package/src/models/predicates.js +438 -0
  37. package/src/models/protocols.js +242 -0
  38. package/src/models/responseResolver.js +398 -0
  39. package/src/models/smtp/index.js +16 -0
  40. package/src/models/smtp/smtpRequest.js +60 -0
  41. package/src/models/smtp/smtpServer.js +109 -0
  42. package/src/models/tcp/index.js +18 -0
  43. package/src/models/tcp/tcpProxy.js +110 -0
  44. package/src/models/tcp/tcpRequest.js +23 -0
  45. package/src/models/tcp/tcpServer.js +156 -0
  46. package/src/models/tcp/tcpValidator.js +19 -0
  47. package/src/models/xpath.js +95 -0
  48. package/src/mountebank.js +245 -0
  49. package/src/public/images/arrow_down.png +0 -0
  50. package/src/public/images/arrow_up.png +0 -0
  51. package/src/public/images/book.jpg +0 -0
  52. package/src/public/images/dataflow.png +0 -0
  53. package/src/public/images/favicon.ico +0 -0
  54. package/src/public/images/forkme_right_orange_ff7600.png +0 -0
  55. package/src/public/images/mountebank.png +0 -0
  56. package/src/public/images/overview.gif +0 -0
  57. package/src/public/images/quote.png +0 -0
  58. package/src/public/images/tw-logo.png +0 -0
  59. package/src/public/scripts/jquery/jquery-3.6.1.min.js +2 -0
  60. package/src/public/scripts/urlHashHandler.js +31 -0
  61. package/src/public/stylesheets/application.css +424 -0
  62. package/src/public/stylesheets/ie.css +14 -0
  63. package/src/public/stylesheets/imposters.css +121 -0
  64. package/src/public/stylesheets/jqueryui/1.10.4/themes/smoothness/jquery-ui.css +1178 -0
  65. package/src/util/combinators.js +68 -0
  66. package/src/util/date.js +51 -0
  67. package/src/util/errors.js +55 -0
  68. package/src/util/helpers.js +131 -0
  69. package/src/util/inherit.js +28 -0
  70. package/src/util/ip.js +54 -0
  71. package/src/util/logger.js +83 -0
  72. package/src/util/middleware.js +256 -0
  73. package/src/util/scopedLogger.js +47 -0
  74. package/src/views/_footer.ejs +20 -0
  75. package/src/views/_header.ejs +113 -0
  76. package/src/views/_imposter.ejs +8 -0
  77. package/src/views/config.ejs +71 -0
  78. package/src/views/docs/api/behaviors/copy.ejs +427 -0
  79. package/src/views/docs/api/behaviors/decorate.ejs +182 -0
  80. package/src/views/docs/api/behaviors/lookup.ejs +220 -0
  81. package/src/views/docs/api/behaviors/shellTransform.ejs +153 -0
  82. package/src/views/docs/api/behaviors/wait.ejs +121 -0
  83. package/src/views/docs/api/behaviors.ejs +141 -0
  84. package/src/views/docs/api/contracts/addStub-description.ejs +10 -0
  85. package/src/views/docs/api/contracts/addStub.ejs +10 -0
  86. package/src/views/docs/api/contracts/config-description.ejs +32 -0
  87. package/src/views/docs/api/contracts/config.ejs +23 -0
  88. package/src/views/docs/api/contracts/home-description.ejs +18 -0
  89. package/src/views/docs/api/contracts/home.ejs +13 -0
  90. package/src/views/docs/api/contracts/imposter-description.ejs +439 -0
  91. package/src/views/docs/api/contracts/imposter.ejs +182 -0
  92. package/src/views/docs/api/contracts/imposters-description.ejs +13 -0
  93. package/src/views/docs/api/contracts/imposters.ejs +13 -0
  94. package/src/views/docs/api/contracts/logs-description.ejs +3 -0
  95. package/src/views/docs/api/contracts/logs.ejs +14 -0
  96. package/src/views/docs/api/contracts/stub-description.ejs +4 -0
  97. package/src/views/docs/api/contracts/stub.ejs +7 -0
  98. package/src/views/docs/api/contracts/stubs-description.ejs +4 -0
  99. package/src/views/docs/api/contracts/stubs.ejs +11 -0
  100. package/src/views/docs/api/contracts.ejs +133 -0
  101. package/src/views/docs/api/errors.ejs +64 -0
  102. package/src/views/docs/api/fault/connectionReset.ejs +31 -0
  103. package/src/views/docs/api/fault/randomDataThenClose.ejs +31 -0
  104. package/src/views/docs/api/faults.ejs +57 -0
  105. package/src/views/docs/api/injection.ejs +426 -0
  106. package/src/views/docs/api/json.ejs +205 -0
  107. package/src/views/docs/api/jsonpath.ejs +210 -0
  108. package/src/views/docs/api/mocks.ejs +130 -0
  109. package/src/views/docs/api/overview.ejs +968 -0
  110. package/src/views/docs/api/predicates/and.ejs +62 -0
  111. package/src/views/docs/api/predicates/contains.ejs +64 -0
  112. package/src/views/docs/api/predicates/deepEquals.ejs +114 -0
  113. package/src/views/docs/api/predicates/endsWith.ejs +66 -0
  114. package/src/views/docs/api/predicates/equals.ejs +125 -0
  115. package/src/views/docs/api/predicates/exists.ejs +118 -0
  116. package/src/views/docs/api/predicates/inject.ejs +67 -0
  117. package/src/views/docs/api/predicates/matches.ejs +66 -0
  118. package/src/views/docs/api/predicates/not.ejs +52 -0
  119. package/src/views/docs/api/predicates/or.ejs +79 -0
  120. package/src/views/docs/api/predicates/startsWith.ejs +62 -0
  121. package/src/views/docs/api/predicates.ejs +382 -0
  122. package/src/views/docs/api/proxies.ejs +191 -0
  123. package/src/views/docs/api/proxy/addDecorateBehavior.ejs +115 -0
  124. package/src/views/docs/api/proxy/addWaitBehavior.ejs +96 -0
  125. package/src/views/docs/api/proxy/injectHeaders.ejs +91 -0
  126. package/src/views/docs/api/proxy/predicateGenerators.ejs +600 -0
  127. package/src/views/docs/api/proxy/proxyModes.ejs +495 -0
  128. package/src/views/docs/api/stubs.ejs +391 -0
  129. package/src/views/docs/api/xpath.ejs +281 -0
  130. package/src/views/docs/cli/configFiles.ejs +133 -0
  131. package/src/views/docs/cli/customFormatters.ejs +53 -0
  132. package/src/views/docs/cli/help.ejs +6 -0
  133. package/src/views/docs/cli/replay.ejs +42 -0
  134. package/src/views/docs/cli/restart.ejs +10 -0
  135. package/src/views/docs/cli/save.ejs +68 -0
  136. package/src/views/docs/cli/start.ejs +234 -0
  137. package/src/views/docs/cli/stop.ejs +32 -0
  138. package/src/views/docs/commandLine.ejs +93 -0
  139. package/src/views/docs/communityExtensions.ejs +233 -0
  140. package/src/views/docs/gettingStarted.ejs +146 -0
  141. package/src/views/docs/mentalModel.ejs +51 -0
  142. package/src/views/docs/protocols/custom.ejs +231 -0
  143. package/src/views/docs/protocols/http.ejs +238 -0
  144. package/src/views/docs/protocols/https.ejs +246 -0
  145. package/src/views/docs/protocols/smtp.ejs +142 -0
  146. package/src/views/docs/protocols/tcp.ejs +431 -0
  147. package/src/views/docs/security.ejs +38 -0
  148. package/src/views/faqs.ejs +65 -0
  149. package/src/views/feed.ejs +33 -0
  150. package/src/views/imposter.ejs +22 -0
  151. package/src/views/imposters.ejs +33 -0
  152. package/src/views/index.ejs +89 -0
  153. package/src/views/license.ejs +30 -0
  154. package/src/views/logs.ejs +77 -0
  155. package/src/views/releases/v1.1.0.ejs +55 -0
  156. package/src/views/releases/v1.1.36.ejs +84 -0
  157. package/src/views/releases/v1.1.72.ejs +92 -0
  158. package/src/views/releases/v1.10.0.ejs +108 -0
  159. package/src/views/releases/v1.11.0.ejs +109 -0
  160. package/src/views/releases/v1.12.0.ejs +96 -0
  161. package/src/views/releases/v1.13.0.ejs +118 -0
  162. package/src/views/releases/v1.14.0.ejs +107 -0
  163. package/src/views/releases/v1.14.1.ejs +94 -0
  164. package/src/views/releases/v1.15.0.ejs +113 -0
  165. package/src/views/releases/v1.16.0.ejs +104 -0
  166. package/src/views/releases/v1.2.0.ejs +78 -0
  167. package/src/views/releases/v1.2.103.ejs +86 -0
  168. package/src/views/releases/v1.2.122.ejs +86 -0
  169. package/src/views/releases/v1.2.30.ejs +84 -0
  170. package/src/views/releases/v1.2.45.ejs +84 -0
  171. package/src/views/releases/v1.2.56.ejs +79 -0
  172. package/src/views/releases/v1.3.0.ejs +86 -0
  173. package/src/views/releases/v1.3.1.ejs +100 -0
  174. package/src/views/releases/v1.4.0.ejs +96 -0
  175. package/src/views/releases/v1.4.1.ejs +103 -0
  176. package/src/views/releases/v1.4.2.ejs +100 -0
  177. package/src/views/releases/v1.4.3.ejs +113 -0
  178. package/src/views/releases/v1.5.0.ejs +104 -0
  179. package/src/views/releases/v1.5.1.ejs +91 -0
  180. package/src/views/releases/v1.6.0.ejs +109 -0
  181. package/src/views/releases/v1.7.0.ejs +113 -0
  182. package/src/views/releases/v1.7.1.ejs +90 -0
  183. package/src/views/releases/v1.7.2.ejs +96 -0
  184. package/src/views/releases/v1.8.0.ejs +121 -0
  185. package/src/views/releases/v1.9.0.ejs +111 -0
  186. package/src/views/releases/v2.0.0.ejs +159 -0
  187. package/src/views/releases/v2.1.0.ejs +121 -0
  188. package/src/views/releases/v2.1.1.ejs +106 -0
  189. package/src/views/releases/v2.1.2.ejs +84 -0
  190. package/src/views/releases/v2.2.0.ejs +115 -0
  191. package/src/views/releases/v2.2.1.ejs +102 -0
  192. package/src/views/releases/v2.3.0.ejs +121 -0
  193. package/src/views/releases/v2.3.1.ejs +100 -0
  194. package/src/views/releases/v2.3.2.ejs +102 -0
  195. package/src/views/releases/v2.3.3.ejs +97 -0
  196. package/src/views/releases/v2.4.0.ejs +114 -0
  197. package/src/views/releases/v2.5.0.ejs +51 -0
  198. package/src/views/releases/v2.6.0.ejs +35 -0
  199. package/src/views/releases/v2.7.0.ejs +32 -0
  200. package/src/views/releases/v2.8.0.ejs +36 -0
  201. package/src/views/releases/v2.8.1.ejs +7 -0
  202. package/src/views/releases/v2.8.2.ejs +26 -0
  203. package/src/views/releases/v2.9.0.ejs +32 -0
  204. package/src/views/releases/v2.9.1.ejs +10 -0
  205. package/src/views/releases.ejs +26 -0
  206. package/src/views/sitemap.ejs +36 -0
  207. 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&amp;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') -%>