mockserver-client 7.6.0 → 9.0.0
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.
- checksums.yaml +4 -4
- data/README.md +69 -3
- data/lib/mockserver/binary_launcher.rb +48 -3
- data/lib/mockserver/client.rb +197 -5
- data/lib/mockserver/models.rb +857 -60
- data/lib/mockserver/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65c675ce8a2644ccefd09d08e036b68676dc481222d4e0e41f8d2a31f5ee4d9e
|
|
4
|
+
data.tar.gz: 5ce4a28531387f73bef657eac15879122b9bfce161c29b7ac03fe2e609fea74e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5b3e1a67574cfb21096ded9e3d651ef37f0f0963d077f87829d1ea43940c957f2404a6144772b2d8d72230a568073b8869fd81006e7dde367a046faa436cc83a
|
|
7
|
+
data.tar.gz: '09436cda59e502b5b89444e0ca7aa32aa8fdd0196445aebc99e7d984c5f21b3ce259dc86cc31b1e08b1949c15a06eb80300fd3d6eca84e921a8a5fb1cd73c791'
|
data/README.md
CHANGED
|
@@ -85,7 +85,7 @@ client.close
|
|
|
85
85
|
|
|
86
86
|
## Models
|
|
87
87
|
|
|
88
|
-
All
|
|
88
|
+
All 27 domain model classes are available under the `MockServer` module:
|
|
89
89
|
|
|
90
90
|
- `Delay`, `Times`, `TimeToLive`
|
|
91
91
|
- `KeyToMultiValue`, `Body`, `SocketAddress`
|
|
@@ -94,12 +94,61 @@ All 26 domain model classes are available under the `MockServer` module:
|
|
|
94
94
|
- `HttpOverrideForwardedRequest`, `HttpRequestAndHttpResponse`
|
|
95
95
|
- `ConnectionOptions`
|
|
96
96
|
- `Expectation`, `ExpectationId`
|
|
97
|
-
- `OpenAPIDefinition`, `OpenAPIExpectation`
|
|
97
|
+
- `OpenAPIDefinition`, `OpenAPIExpectation`, `ConditionalRequestDefinition`
|
|
98
98
|
- `Verification`, `VerificationSequence`, `VerificationTimes`
|
|
99
99
|
- `Ports`
|
|
100
100
|
- `RequestDefinition` (alias for `HttpRequest`)
|
|
101
101
|
- `Jwt` (JWT / bearer-token request matcher)
|
|
102
102
|
|
|
103
|
+
Every model can also be built from the expectation JSON with `from_hash`. Keys
|
|
104
|
+
may be strings (`'httpRequest' => {...}`, as `JSON.parse` returns them) or
|
|
105
|
+
symbols (`"httpRequest": {...}` or `httpRequest: {...}`):
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
client.upsert(MockServer::Expectation.from_hash({
|
|
109
|
+
"httpRequest": { "path": '/some/path' },
|
|
110
|
+
"httpResponse": { "statusCode": 200, "body": 'some_response_body' }
|
|
111
|
+
}))
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Symbol keys need a client released after 8.0.0; 8.0.0 and earlier read only <!-- version-ref:keep -->
|
|
115
|
+
string keys and build an empty expectation from a symbol-keyed hash.
|
|
116
|
+
|
|
117
|
+
Headers, query parameters, path parameters, trailers and cookies can be given
|
|
118
|
+
in the same shapes the JSON uses. A key match style sets whether at least one
|
|
119
|
+
value of each listed key must match (`SUB_SET`, the default) or every value of
|
|
120
|
+
it (`MATCHING_KEY`):
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
MockServer::HttpRequest.new(
|
|
124
|
+
path: '/view/cart',
|
|
125
|
+
headers: { 'Accept' => ['application/json'] },
|
|
126
|
+
headers_key_match_style: 'MATCHING_KEY',
|
|
127
|
+
query_string_parameters: [{ name: 'cartId', values: ['055CA455'] }],
|
|
128
|
+
cookies: { 'session' => '4930456C' }
|
|
129
|
+
)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
An `httpRequest` holding `specUrlOrPayload` is read as an `OpenAPIDefinition`
|
|
133
|
+
matcher, and `HttpOverrideForwardedRequest` carries `request_override`,
|
|
134
|
+
`response_override` and `response_template`. These need a client released
|
|
135
|
+
after 8.0.0. <!-- version-ref:keep -->
|
|
136
|
+
|
|
137
|
+
An `httpRequest` holding `if` is read as a `ConditionalRequestDefinition`: when
|
|
138
|
+
`if_request` matches, `then_request` must match too, otherwise `else_request`
|
|
139
|
+
must. A load scenario `LoadStep` takes `checks`, a list of `LoadCheck`
|
|
140
|
+
assertions on each step's response. These need a client released after 8.0.0. <!-- version-ref:keep -->
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
client.when(
|
|
144
|
+
MockServer::ConditionalRequestDefinition.new(
|
|
145
|
+
if_request: MockServer::HttpRequest.new(method: 'POST'),
|
|
146
|
+
then_request: MockServer::HttpRequest.new(path: '/orders'),
|
|
147
|
+
else_request: MockServer::HttpRequest.new(method: 'GET')
|
|
148
|
+
)
|
|
149
|
+
).respond(MockServer::HttpResponse.new(status_code: 200))
|
|
150
|
+
```
|
|
151
|
+
|
|
103
152
|
## JWT and combined body matchers
|
|
104
153
|
|
|
105
154
|
Match a request by the claims in its JWT (carried by default in the
|
|
@@ -280,6 +329,21 @@ client.start_chaos_experiment(
|
|
|
280
329
|
SLO tracking must be enabled on the server (`sloTrackingEnabled=true`) or
|
|
281
330
|
`verify_slo` raises `MockServer::Error` on HTTP 400.
|
|
282
331
|
|
|
332
|
+
## Diagnose Matching
|
|
333
|
+
|
|
334
|
+
`explain(correlation_id)` tells you which expectation would serve a recorded request now and why
|
|
335
|
+
each expectation ahead of it was passed over; `debug_mismatch(request)` compares a request with
|
|
336
|
+
every active expectation; `explain_unmatched(limit:, suggest_fixes:)` explains recent requests
|
|
337
|
+
that matched nothing. Each returns a typed result (`RequestExplanation`, `DebugMismatchResult`,
|
|
338
|
+
`ExplainUnmatchedResult`) and raises `MockServer::Error` with the server's reason on failure.
|
|
339
|
+
|
|
340
|
+
```ruby
|
|
341
|
+
explanation = client.explain('abc-123-def-456')
|
|
342
|
+
explanation.skipped_expectations.each do |skipped|
|
|
343
|
+
puts "#{skipped.expectation_id} #{skipped.reason}: #{skipped.explanation}"
|
|
344
|
+
end
|
|
345
|
+
```
|
|
346
|
+
|
|
283
347
|
## Interactive Breakpoints
|
|
284
348
|
|
|
285
349
|
The client supports matcher-driven interactive breakpoints over the callback WebSocket. Register a breakpoint matcher to pause forwarded/proxied exchanges at specific phases and inspect/modify/continue them via callback handlers.
|
|
@@ -353,7 +417,7 @@ launcher_path = MockServer::BinaryLauncher.ensure_launcher
|
|
|
353
417
|
### Specify a version
|
|
354
418
|
|
|
355
419
|
```ruby
|
|
356
|
-
handle = MockServer::BinaryLauncher.start(port: 1080, version: '
|
|
420
|
+
handle = MockServer::BinaryLauncher.start(port: 1080, version: '9.0.0')
|
|
357
421
|
```
|
|
358
422
|
|
|
359
423
|
### API reference
|
|
@@ -364,6 +428,8 @@ handle = MockServer::BinaryLauncher.start(port: 1080, version: '7.6.0')
|
|
|
364
428
|
| `MockServer::BinaryLauncher.start(port:, version:, extra_args:, log:)` | Ensure the binary and start MockServer. Returns a `ServerHandle`. |
|
|
365
429
|
| `MockServer::BinaryLauncher::ServerHandle` | Handle to the running process. Methods: `stop(timeout:)`, `running?`. Attributes: `pid`, `port`, `launcher`. |
|
|
366
430
|
|
|
431
|
+
On Windows the launcher runs `mockserver.bat` through `cmd.exe` with every argument quoted, so characters such as `^`, `!` and parentheses reach MockServer unchanged. `start` raises `MockServer::Error` if the launcher path or an `extra_args` entry contains `"`, `<`, `>`, `|`, `&`, a line break or NUL, or if together they contain more than one `%` (which `cmd.exe` would expand as a `%NAME%` variable); it also needs a native Windows Ruby (mswin or mingw, such as RubyInstaller). No shell is used on Linux or macOS.
|
|
432
|
+
|
|
367
433
|
### Supported platforms
|
|
368
434
|
|
|
369
435
|
| OS | Architecture |
|
|
@@ -48,6 +48,9 @@ module MockServer
|
|
|
48
48
|
# Strict pattern for version strings — blocks path separators and '..'.
|
|
49
49
|
VERSION_PATTERN = /\A[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?\z/
|
|
50
50
|
|
|
51
|
+
# Characters refused in the launcher path and arguments on Windows (see +windows_command_line+).
|
|
52
|
+
WINDOWS_UNSAFE = /["<>|&\r\n\0]/
|
|
53
|
+
|
|
51
54
|
class << self
|
|
52
55
|
# Resolve the current platform to the bundle naming tokens.
|
|
53
56
|
#
|
|
@@ -241,18 +244,53 @@ module MockServer
|
|
|
241
244
|
launcher = ensure_launcher(version: version, log: log)
|
|
242
245
|
args = ['-serverPort', port.to_s] + extra_args
|
|
243
246
|
|
|
244
|
-
# H4: On Windows, .bat files must be invoked via cmd.exe /c.
|
|
245
247
|
# H5: Drain stdout/stderr via :out/:err redirection to avoid pipe-buffer deadlock.
|
|
248
|
+
# On Unix the [command, argv0] form never goes through a shell, even with no args.
|
|
246
249
|
pid = if windows?
|
|
247
|
-
Process.spawn(
|
|
250
|
+
Process.spawn(windows_command_line(launcher, args),
|
|
248
251
|
out: File::NULL, err: File::NULL)
|
|
249
252
|
else
|
|
250
|
-
Process.spawn(launcher, *args,
|
|
253
|
+
Process.spawn([launcher, launcher], *args,
|
|
251
254
|
out: File::NULL, err: File::NULL)
|
|
252
255
|
end
|
|
253
256
|
ServerHandle.new(pid: pid, port: port, launcher: launcher)
|
|
254
257
|
end
|
|
255
258
|
|
|
259
|
+
# Build the cmd.exe command line that runs the +.bat+ launcher on Windows.
|
|
260
|
+
#
|
|
261
|
+
# Native Windows Ruby passes a single command string to CreateProcess
|
|
262
|
+
# unchanged unless it sees a redirection (+<+, +>+, +|+, +&+, a line break
|
|
263
|
+
# or +%NAME%+), so those are refused, which keeps the line away from a
|
|
264
|
+
# second shell. Each part is double-quoted so cmd.exe takes +^+, +(+, +)+
|
|
265
|
+
# and +!+ literally (+/v:off+), and +/d+ skips AutoRun. cmd.exe expands
|
|
266
|
+
# +%NAME%+ even inside quotes, so a line with two or more +%+ is refused.
|
|
267
|
+
#
|
|
268
|
+
# @param launcher [String] path of the +.bat+ launcher
|
|
269
|
+
# @param args [Array<String>] arguments for the launcher
|
|
270
|
+
# @return [String]
|
|
271
|
+
# @raise [Error] if a part cannot be passed to cmd.exe safely, or Ruby is not a native Windows build
|
|
272
|
+
def windows_command_line(launcher, args)
|
|
273
|
+
unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/i
|
|
274
|
+
raise Error, 'starting the Windows launcher needs a native Windows Ruby (mswin or mingw, ' \
|
|
275
|
+
"such as RubyInstaller); this Ruby is #{RbConfig::CONFIG['host_os']}"
|
|
276
|
+
end
|
|
277
|
+
# Messages name the part, never its value, which may be a secret.
|
|
278
|
+
parts = [launcher, *args].map(&:to_s)
|
|
279
|
+
parts.each_with_index do |part, i|
|
|
280
|
+
next unless part.match?(WINDOWS_UNSAFE)
|
|
281
|
+
|
|
282
|
+
name = i.zero? ? 'the launcher path' : "argument #{i - 1}"
|
|
283
|
+
raise Error, "#{name} cannot be passed through cmd.exe safely " \
|
|
284
|
+
'(contains ", <, >, |, &, a line break or NUL)'
|
|
285
|
+
end
|
|
286
|
+
line = parts.map { |part| quote_cmd_arg(part) }.join(' ')
|
|
287
|
+
if line.count('%') > 1
|
|
288
|
+
raise Error, 'the launcher path and arguments contain more than one %, which cmd.exe would expand ' \
|
|
289
|
+
'as an environment variable reference (%NAME%); remove the % characters to run on Windows'
|
|
290
|
+
end
|
|
291
|
+
%(cmd.exe /d /v:off /s /c "#{line}")
|
|
292
|
+
end
|
|
293
|
+
|
|
256
294
|
# Remove old version directories from the cache, keeping the current version
|
|
257
295
|
# and at most MAX_PREVIOUS_VERSIONS_TO_KEEP previous versions.
|
|
258
296
|
#
|
|
@@ -329,6 +367,13 @@ module MockServer
|
|
|
329
367
|
|
|
330
368
|
private
|
|
331
369
|
|
|
370
|
+
# Double-quote one cmd.exe argument, doubling a trailing run of backslashes
|
|
371
|
+
# so it cannot escape the closing quote when the program splits its command line.
|
|
372
|
+
def quote_cmd_arg(arg)
|
|
373
|
+
trailing = arg[/\\*\z/]
|
|
374
|
+
%("#{arg}#{trailing}")
|
|
375
|
+
end
|
|
376
|
+
|
|
332
377
|
# Build a clear, actionable error message for a missing release bundle.
|
|
333
378
|
#
|
|
334
379
|
# Explains that no downloadable bundle exists for +version+ and lists the
|
data/lib/mockserver/client.rb
CHANGED
|
@@ -862,8 +862,8 @@ module MockServer
|
|
|
862
862
|
# (PUT /mockserver/verifySLO).
|
|
863
863
|
#
|
|
864
864
|
# The server encodes the verdict in the HTTP status: 200 for PASS or
|
|
865
|
-
# INCONCLUSIVE, 406 for FAIL,
|
|
866
|
-
#
|
|
865
|
+
# INCONCLUSIVE, 406 for FAIL, 403 when SLO tracking is disabled
|
|
866
|
+
# (+sloTrackingEnabled=false+) and 400 for malformed criteria. The decoded verdict Hash carries
|
|
867
867
|
# the overall +result+ and the per-objective +objectiveResults+ so callers can
|
|
868
868
|
# inspect why an SLO failed.
|
|
869
869
|
#
|
|
@@ -875,7 +875,7 @@ module MockServer
|
|
|
875
875
|
# @param criteria [Hash, #to_h] the SLO criteria
|
|
876
876
|
# @return [Hash] the SLO verdict (result PASS or INCONCLUSIVE)
|
|
877
877
|
# @raise [VerificationError] if the verdict is FAIL (HTTP 406)
|
|
878
|
-
# @raise [Error] if
|
|
878
|
+
# @raise [Error] if SLO tracking is disabled (HTTP 403) or criteria are malformed (HTTP 400),
|
|
879
879
|
# or on any other failure
|
|
880
880
|
def verify_slo(criteria)
|
|
881
881
|
payload = criteria.respond_to?(:to_h) ? criteria.to_h : criteria
|
|
@@ -884,9 +884,12 @@ module MockServer
|
|
|
884
884
|
if status == 406
|
|
885
885
|
raise VerificationError, (response_body && !response_body.empty? ? response_body : 'SLO verdict: FAIL')
|
|
886
886
|
end
|
|
887
|
+
if status == 403
|
|
888
|
+
raise Error, 'SLO tracking is disabled — set sloTrackingEnabled=true ' \
|
|
889
|
+
"on the server: #{response_body}"
|
|
890
|
+
end
|
|
887
891
|
if status == 400
|
|
888
|
-
raise Error,
|
|
889
|
-
"sloTrackingEnabled=true on the server): #{response_body}"
|
|
892
|
+
raise Error, "Invalid SLO criteria: #{response_body}"
|
|
890
893
|
end
|
|
891
894
|
if status >= 400
|
|
892
895
|
raise Error, "Failed to verify SLO (status=#{status}): #{response_body}"
|
|
@@ -925,6 +928,85 @@ module MockServer
|
|
|
925
928
|
response_body && !response_body.empty? ? JSON.parse(response_body) : {}
|
|
926
929
|
end
|
|
927
930
|
|
|
931
|
+
# -------------------------------------------------------------------
|
|
932
|
+
# Diagnostics (explain / debug_mismatch / explain_unmatched)
|
|
933
|
+
# -------------------------------------------------------------------
|
|
934
|
+
|
|
935
|
+
# Explain how a recorded request would be served now
|
|
936
|
+
# (PUT /mockserver/explain?correlationId=<id>): re-checks it against the
|
|
937
|
+
# current expectations, in the order MockServer evaluates them, and returns
|
|
938
|
+
# the expectation that would serve it, why each expectation ahead of that
|
|
939
|
+
# one was passed over, and which lower-priority expectations it shadows.
|
|
940
|
+
# Read-only: it consumes no Times and moves no scenario.
|
|
941
|
+
#
|
|
942
|
+
# @param correlation_id [String] the correlation id of the request's log entries
|
|
943
|
+
# @return [RequestExplanation]
|
|
944
|
+
# @raise [ArgumentError] if +correlation_id+ is nil or blank
|
|
945
|
+
# @raise [Error] with the server's reason when no request is recorded with
|
|
946
|
+
# this id (never received, cleared or evicted) or it is not an HTTP request
|
|
947
|
+
def explain(correlation_id)
|
|
948
|
+
if correlation_id.nil? || correlation_id.to_s.strip.empty?
|
|
949
|
+
raise ArgumentError, 'explain requires a non blank correlation_id'
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
RequestExplanation.from_hash(
|
|
953
|
+
diagnostic_request('/mockserver/explain', nil, { 'correlationId' => correlation_id.to_s },
|
|
954
|
+
"explain failed for correlationId \"#{correlation_id}\"")
|
|
955
|
+
)
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
# Compare a request with every active expectation
|
|
959
|
+
# (PUT /mockserver/debugMismatch): for each, whether it matches and the
|
|
960
|
+
# per-field differences, plus the closest match. Only an HTTP request is
|
|
961
|
+
# supported.
|
|
962
|
+
#
|
|
963
|
+
# @param request [HttpRequest, Hash] the request to check
|
|
964
|
+
# @return [DebugMismatchResult]
|
|
965
|
+
# @raise [Error] with the server's reason for a non-HTTP request definition
|
|
966
|
+
def debug_mismatch(request)
|
|
967
|
+
payload = request.respond_to?(:to_h) ? request.to_h : request
|
|
968
|
+
DebugMismatchResult.from_hash(
|
|
969
|
+
diagnostic_request('/mockserver/debugMismatch', JSON.generate(payload), nil, 'debugMismatch failed')
|
|
970
|
+
)
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
# Explain why recent requests matched no expectation
|
|
974
|
+
# (PUT /mockserver/explainUnmatched): each recent unmatched request with its
|
|
975
|
+
# closest expectations, closest first.
|
|
976
|
+
#
|
|
977
|
+
# @param limit [Integer, nil] how many recent unmatched requests to analyse (server default 10)
|
|
978
|
+
# @param suggest_fixes [Boolean, nil] add a suggested JSON patch to each of
|
|
979
|
+
# the five closest non-matching expectations (nothing is applied)
|
|
980
|
+
# @return [ExplainUnmatchedResult]
|
|
981
|
+
def explain_unmatched(limit: nil, suggest_fixes: nil)
|
|
982
|
+
options = {}
|
|
983
|
+
options['limit'] = limit unless limit.nil?
|
|
984
|
+
options['suggestFixes'] = suggest_fixes unless suggest_fixes.nil?
|
|
985
|
+
ExplainUnmatchedResult.from_hash(
|
|
986
|
+
diagnostic_request('/mockserver/explainUnmatched', options.empty? ? nil : JSON.generate(options), nil,
|
|
987
|
+
'explainUnmatched failed')
|
|
988
|
+
)
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
# @api private
|
|
992
|
+
# PUT a diagnostic request and return the parsed 200 body; any other status,
|
|
993
|
+
# or a body that is not a JSON object, raises {Error} with the server's
|
|
994
|
+
# +error+ or the body.
|
|
995
|
+
def diagnostic_request(path, body, query_params, failure)
|
|
996
|
+
status, response_body = do_request('PUT', path, body, query_params)
|
|
997
|
+
parsed = begin
|
|
998
|
+
response_body.nil? || response_body.empty? ? {} : JSON.parse(response_body)
|
|
999
|
+
rescue JSON::ParserError
|
|
1000
|
+
nil
|
|
1001
|
+
end
|
|
1002
|
+
if status != 200 || !parsed.is_a?(Hash)
|
|
1003
|
+
reason = parsed.is_a?(Hash) ? parsed['error'] : nil
|
|
1004
|
+
raise Error, "#{failure} (status=#{status}): #{reason || response_body}"
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
parsed
|
|
1008
|
+
end
|
|
1009
|
+
|
|
928
1010
|
# -------------------------------------------------------------------
|
|
929
1011
|
# Stateful scenarios (state machine control plane)
|
|
930
1012
|
# -------------------------------------------------------------------
|
|
@@ -1129,6 +1211,116 @@ module MockServer
|
|
|
1129
1211
|
[]
|
|
1130
1212
|
end
|
|
1131
1213
|
|
|
1214
|
+
# Send a retrieve request filtered by expectation id.
|
|
1215
|
+
#
|
|
1216
|
+
# An expectation id may be sent in place of a request matcher. For
|
|
1217
|
+
# "ACTIVE_EXPECTATIONS" the server returns only the expectation with that id;
|
|
1218
|
+
# for every other type it returns the entries matching that expectation's
|
|
1219
|
+
# request, exactly as verify by expectation id matches them. An unknown id is
|
|
1220
|
+
# rejected with a 400.
|
|
1221
|
+
#
|
|
1222
|
+
# @param expectation_id [String]
|
|
1223
|
+
# @param query_params [Hash]
|
|
1224
|
+
# @return [Array(Integer, String)] the status and response body
|
|
1225
|
+
# @api private
|
|
1226
|
+
def retrieve_by_id(expectation_id, query_params)
|
|
1227
|
+
do_request(
|
|
1228
|
+
'PUT', '/mockserver/retrieve', JSON.generate({ 'id' => expectation_id }), query_params
|
|
1229
|
+
)
|
|
1230
|
+
end
|
|
1231
|
+
private :retrieve_by_id
|
|
1232
|
+
|
|
1233
|
+
# Retrieve the recorded requests matching the request of the expectation with the given id.
|
|
1234
|
+
# @param expectation_id [String]
|
|
1235
|
+
# @return [Array<HttpRequest>]
|
|
1236
|
+
def retrieve_recorded_requests_by_id(expectation_id)
|
|
1237
|
+
status, response_body = retrieve_by_id(
|
|
1238
|
+
expectation_id, { 'type' => 'REQUESTS', 'format' => 'JSON' }
|
|
1239
|
+
)
|
|
1240
|
+
if status >= 400
|
|
1241
|
+
raise Error, "Failed to retrieve recorded requests by id (status=#{status}): #{response_body}"
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1244
|
+
if response_body && !response_body.empty?
|
|
1245
|
+
parsed = JSON.parse(response_body)
|
|
1246
|
+
return parsed.map { |r| HttpRequest.from_hash(r) } if parsed.is_a?(Array)
|
|
1247
|
+
end
|
|
1248
|
+
[]
|
|
1249
|
+
end
|
|
1250
|
+
|
|
1251
|
+
# Retrieve the active expectation with the given id.
|
|
1252
|
+
# @param expectation_id [String]
|
|
1253
|
+
# @return [Array<Expectation>]
|
|
1254
|
+
def retrieve_active_expectations_by_id(expectation_id)
|
|
1255
|
+
status, response_body = retrieve_by_id(
|
|
1256
|
+
expectation_id, { 'type' => 'ACTIVE_EXPECTATIONS', 'format' => 'JSON' }
|
|
1257
|
+
)
|
|
1258
|
+
if status >= 400
|
|
1259
|
+
raise Error, "Failed to retrieve active expectations by id (status=#{status}): #{response_body}"
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
if response_body && !response_body.empty?
|
|
1263
|
+
parsed = JSON.parse(response_body)
|
|
1264
|
+
return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
|
|
1265
|
+
end
|
|
1266
|
+
[]
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
# Retrieve the recorded expectations matching the request of the expectation with the given id.
|
|
1270
|
+
# @param expectation_id [String]
|
|
1271
|
+
# @return [Array<Expectation>]
|
|
1272
|
+
def retrieve_recorded_expectations_by_id(expectation_id)
|
|
1273
|
+
status, response_body = retrieve_by_id(
|
|
1274
|
+
expectation_id, { 'type' => 'RECORDED_EXPECTATIONS', 'format' => 'JSON' }
|
|
1275
|
+
)
|
|
1276
|
+
if status >= 400
|
|
1277
|
+
raise Error, "Failed to retrieve recorded expectations by id (status=#{status}): #{response_body}"
|
|
1278
|
+
end
|
|
1279
|
+
|
|
1280
|
+
if response_body && !response_body.empty?
|
|
1281
|
+
parsed = JSON.parse(response_body)
|
|
1282
|
+
return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
|
|
1283
|
+
end
|
|
1284
|
+
[]
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1287
|
+
# Retrieve the recorded requests and responses matching the request of the expectation with the given id.
|
|
1288
|
+
# @param expectation_id [String]
|
|
1289
|
+
# @return [Array<HttpRequestAndHttpResponse>]
|
|
1290
|
+
def retrieve_recorded_requests_and_responses_by_id(expectation_id)
|
|
1291
|
+
status, response_body = retrieve_by_id(
|
|
1292
|
+
expectation_id, { 'type' => 'REQUEST_RESPONSES', 'format' => 'JSON' }
|
|
1293
|
+
)
|
|
1294
|
+
if status >= 400
|
|
1295
|
+
raise Error, "Failed to retrieve request/responses by id (status=#{status}): #{response_body}"
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
if response_body && !response_body.empty?
|
|
1299
|
+
parsed = JSON.parse(response_body)
|
|
1300
|
+
return parsed.map { |rr| HttpRequestAndHttpResponse.from_hash(rr) } if parsed.is_a?(Array)
|
|
1301
|
+
end
|
|
1302
|
+
[]
|
|
1303
|
+
end
|
|
1304
|
+
|
|
1305
|
+
# Retrieve the log messages matching the request of the expectation with the given id.
|
|
1306
|
+
# @param expectation_id [String]
|
|
1307
|
+
# @return [Array<String>]
|
|
1308
|
+
def retrieve_log_messages_by_id(expectation_id)
|
|
1309
|
+
status, response_body = retrieve_by_id(expectation_id, { 'type' => 'LOGS' })
|
|
1310
|
+
if status >= 400
|
|
1311
|
+
raise Error, "Failed to retrieve log messages by id (status=#{status}): #{response_body}"
|
|
1312
|
+
end
|
|
1313
|
+
|
|
1314
|
+
return [] unless response_body && !response_body.empty?
|
|
1315
|
+
|
|
1316
|
+
begin
|
|
1317
|
+
parsed = JSON.parse(response_body)
|
|
1318
|
+
parsed.is_a?(Array) ? parsed : []
|
|
1319
|
+
rescue JSON::ParserError
|
|
1320
|
+
response_body.split("------------------------------------\n")
|
|
1321
|
+
end
|
|
1322
|
+
end
|
|
1323
|
+
|
|
1132
1324
|
# Retrieve the active expectations as MockServer SDK setup code (the builder
|
|
1133
1325
|
# code that recreates the expectations) in the requested language.
|
|
1134
1326
|
# @param format [String] one of "java", "javascript", "python", "go",
|