mockserver-client 9.1.0 → 9.2.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 +102 -1
- data/lib/mockserver/client.rb +131 -13
- data/lib/mockserver/forward_chain_expectation.rb +8 -0
- data/lib/mockserver/models.rb +543 -43
- data/lib/mockserver/version.rb +1 -1
- data/lib/mockserver/websocket_client.rb +5 -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: 02d995291d2bc1c0c4b875711f568d604c4e6af009952512109b44ff49fa2fed
|
|
4
|
+
data.tar.gz: 455d49792d3fec33d41dd66eade0ca64e5ee4049306566c411da1008a431b30a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71d509505576e64dd73a68b79352bbdd0065519b5775767025340798032832bccffa5806b5bac0cda568286b0202baf7af48f84155a095df301df3c60e6be500
|
|
7
|
+
data.tar.gz: 5d6c9b72e335529472b51013bfbaad1d521be2784267d95a48284d2a777577a19ccd87b5f68eedae3bab6195e774d80880e4712f4f289993384d84309433cd92
|
data/README.md
CHANGED
|
@@ -56,6 +56,15 @@ end
|
|
|
56
56
|
# Client is automatically closed when the block exits
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
## Control-Plane Basic Authentication
|
|
60
|
+
|
|
61
|
+
When MockServer accepts control-plane Basic authentication, pass `control_plane_basic_auth: [username, password]` with `secure: true` (MockServer refuses Basic credentials without TLS). They are sent on every control-plane request and on the callback and breakpoint WebSockets.
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
client = MockServer::Client.new('localhost', 1080, secure: true,
|
|
65
|
+
control_plane_basic_auth: ['admin', ENV.fetch('MOCKSERVER_PASSWORD')])
|
|
66
|
+
```
|
|
67
|
+
|
|
59
68
|
## WebSocket Callbacks
|
|
60
69
|
|
|
61
70
|
```ruby
|
|
@@ -99,6 +108,7 @@ All 27 domain model classes are available under the `MockServer` module:
|
|
|
99
108
|
- `Ports`
|
|
100
109
|
- `RequestDefinition` (alias for `HttpRequest`)
|
|
101
110
|
- `Jwt` (JWT / bearer-token request matcher)
|
|
111
|
+
- `DateTimeMatcher` (date-time matcher for header, query, path-parameter and cookie values)
|
|
102
112
|
|
|
103
113
|
Every model can also be built from the expectation JSON with `from_hash`. Keys
|
|
104
114
|
may be strings (`'httpRequest' => {...}`, as `JSON.parse` returns them) or
|
|
@@ -129,6 +139,41 @@ MockServer::HttpRequest.new(
|
|
|
129
139
|
)
|
|
130
140
|
```
|
|
131
141
|
|
|
142
|
+
A header, query parameter, path parameter or cookie value can also be a
|
|
143
|
+
date-time matcher. It compares the request's value as a date-time with
|
|
144
|
+
`before`, `after` or `equal_to_date_time`, where the expected value is `now`,
|
|
145
|
+
`now` with an offset such as `now -1 hours`, or a date-time such as
|
|
146
|
+
`2026-10-21T00:00:00Z`. A plain string such as `'before:2026-01-01'` is still
|
|
147
|
+
matched literally.
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
MockServer::HttpRequest.new(path: '/orders')
|
|
151
|
+
.with_header('X-Issued-At', MockServer::DateTimeMatcher.after('now -1 hours'))
|
|
152
|
+
.with_header('If-Modified-Since',
|
|
153
|
+
MockServer::DateTimeMatcher.before('2026-10-21T00:00:00Z').with_actual_format('rfc1123'))
|
|
154
|
+
.with_cookie('seen', MockServer::DateTimeMatcher.equal_to_date_time('now')
|
|
155
|
+
.with_truncate_expected('startOfDay')
|
|
156
|
+
.with_truncate_actual('startOfDay')
|
|
157
|
+
.with_zone('Europe/London'))
|
|
158
|
+
# headers => [{"name"=>"X-Issued-At", "values"=>[{"after"=>"now -1 hours"}]}, ...]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Each `with_*` option (`with_actual_format`, `with_truncate_expected`,
|
|
162
|
+
`with_truncate_actual`, `with_apply_truncation_last`, `with_zone`, `with_not`)
|
|
163
|
+
returns a new matcher. `from_hash` reads an object holding `before`, `after` or
|
|
164
|
+
`equalToDateTime` as a `DateTimeMatcher`.
|
|
165
|
+
|
|
166
|
+
A JSONPath or XPath body matcher takes a `date_time:` too: the body matches when
|
|
167
|
+
a value the JSONPath selects, or the text of a node the XPath selects, is a
|
|
168
|
+
matching date-time.
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
MockServer::HttpRequest.new(path: '/orders',
|
|
172
|
+
body: MockServer::Body.json_path('$.order.created',
|
|
173
|
+
date_time: MockServer::DateTimeMatcher.after('now -1 days')))
|
|
174
|
+
# body => {"type"=>"JSON_PATH", "jsonPath"=>"$.order.created", "dateTime"=>{"after"=>"now -1 days"}}
|
|
175
|
+
```
|
|
176
|
+
|
|
132
177
|
An `httpRequest` holding `specUrlOrPayload` is read as an `OpenAPIDefinition`
|
|
133
178
|
matcher, and `HttpOverrideForwardedRequest` carries `request_override`,
|
|
134
179
|
`response_override` and `response_template`. These need a client released
|
|
@@ -185,6 +230,28 @@ body = MockServer::Body.all_of(
|
|
|
185
230
|
request = MockServer::HttpRequest.new(method: 'POST', path: '/api').with_body(body)
|
|
186
231
|
```
|
|
187
232
|
|
|
233
|
+
## Client IP matcher
|
|
234
|
+
|
|
235
|
+
Match requests by the client IP address or CIDR range, IPv4 or IPv6. The client address is the TCP peer of the connection, not `X-Forwarded-For`; prefix with `!` to match every other address.
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
MockServer::HttpRequest.new(path: '/internal').with_client_ip('10.0.0.0/8')
|
|
239
|
+
# or MockServer::HttpRequest.new(path: '/internal', client_ip: '!10.0.0.0/8')
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Any-of (logical OR) matcher
|
|
243
|
+
|
|
244
|
+
`MockServer::AnyOfRequestDefinition` matches a request when any one of its request matchers does, tried in order. Entries may be `HttpRequest`, `OpenAPIDefinition`, `ConditionalRequestDefinition` or a nested `AnyOfRequestDefinition`; `not_any_of: true` matches requests that match none of them. `ExplainedExpectation#any_of` says which entry came closest when none matches.
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
client.when(
|
|
248
|
+
MockServer::AnyOfRequestDefinition.new(
|
|
249
|
+
any_of: [MockServer::HttpRequest.new(method: 'GET', path: '/pets'),
|
|
250
|
+
MockServer::HttpRequest.new(method: 'GET', path: '/animals')]
|
|
251
|
+
)
|
|
252
|
+
).respond(MockServer::HttpResponse.new(body: 'pets or animals'))
|
|
253
|
+
```
|
|
254
|
+
|
|
188
255
|
## LLM Mocking
|
|
189
256
|
|
|
190
257
|
Fluent builders under `MockServer::LLM` mock LLM provider endpoints. They produce
|
|
@@ -300,6 +367,40 @@ JSON-RPC task response to the caller *and* POSTs the completed task to the
|
|
|
300
367
|
webhook URL. As with MCP mocking, `build` returns the raw expectation Hashes if
|
|
301
368
|
you prefer to register them yourself.
|
|
302
369
|
|
|
370
|
+
## Expectation Metadata
|
|
371
|
+
|
|
372
|
+
Label expectations with `metadata` (String keys, any JSON value), then retrieve or clear them by
|
|
373
|
+
metadata, find the ones that never matched a request, and page large lists:
|
|
374
|
+
|
|
375
|
+
```ruby
|
|
376
|
+
client.when(MockServer::HttpRequest.new(path: '/checkout'))
|
|
377
|
+
.with_metadata('owner' => 'team-a', 'ticket' => 4211, 'tags' => ['smoke'])
|
|
378
|
+
.respond(MockServer::HttpResponse.new(status_code: 200))
|
|
379
|
+
|
|
380
|
+
client.retrieve_active_expectations_by_metadata('owner', 'team-a')
|
|
381
|
+
client.retrieve_active_expectations_by_metadata_key('ticket')
|
|
382
|
+
client.retrieve_unmatched_expectations
|
|
383
|
+
client.retrieve_active_expectations(
|
|
384
|
+
filter: MockServer::ExpectationFilter.new.with_metadata_json_path("$.tags[?(@ == 'smoke')]").with_limit(10)
|
|
385
|
+
)
|
|
386
|
+
client.retrieve_recorded_expectations(offset: 0, limit: 50)
|
|
387
|
+
# replay changing responses in order, keep only some request headers, move large bodies into files
|
|
388
|
+
client.retrieve_recorded_expectations(
|
|
389
|
+
options: MockServer::RecordedExpectationsOptions.new.with_repeats_as_scenarios
|
|
390
|
+
.with_capture_headers('X-Tenant')
|
|
391
|
+
.with_extract_bodies_larger_than(1024)
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
client.clear_by_metadata('owner', 'team-a')
|
|
395
|
+
client.clear_by_metadata_key('ticket')
|
|
396
|
+
client.clear_unmatched_expectations
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
A String value is matched as text; any other value (`42`, `true`, an Array or Hash) is matched as
|
|
400
|
+
JSON, and `MockServer::ExpectationFilter::JSON_NULL` selects a stored JSON null. A blank key or
|
|
401
|
+
JSONPath, a `nil` value, negative paging, paging on clear, or a clear with no selection raises
|
|
402
|
+
`ArgumentError` before anything is sent, so a mistake can never clear every expectation.
|
|
403
|
+
|
|
303
404
|
## SRE Control Plane (SLO + Chaos)
|
|
304
405
|
|
|
305
406
|
Verify service-level objectives and drive scheduled chaos experiments against
|
|
@@ -417,7 +518,7 @@ launcher_path = MockServer::BinaryLauncher.ensure_launcher
|
|
|
417
518
|
### Specify a version
|
|
418
519
|
|
|
419
520
|
```ruby
|
|
420
|
-
handle = MockServer::BinaryLauncher.start(port: 1080, version: '9.
|
|
521
|
+
handle = MockServer::BinaryLauncher.start(port: 1080, version: '9.2.0')
|
|
421
522
|
```
|
|
422
523
|
|
|
423
524
|
### API reference
|
data/lib/mockserver/client.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'net/http'
|
|
|
4
4
|
require 'uri'
|
|
5
5
|
require 'json'
|
|
6
6
|
require 'openssl'
|
|
7
|
+
require 'base64'
|
|
7
8
|
|
|
8
9
|
module MockServer
|
|
9
10
|
# Synchronous MockServer client.
|
|
@@ -28,14 +29,28 @@ module MockServer
|
|
|
28
29
|
class Client
|
|
29
30
|
HTTP_TIMEOUT = 60 # seconds, matching Python client
|
|
30
31
|
|
|
32
|
+
# Default for clear's +filter:+ so an explicit nil filter is rejected
|
|
33
|
+
# instead of silently clearing everything.
|
|
34
|
+
NO_FILTER = Object.new.freeze
|
|
35
|
+
private_constant :NO_FILTER
|
|
36
|
+
|
|
31
37
|
# @param host [String]
|
|
32
38
|
# @param port [Integer]
|
|
33
39
|
# @param context_path [String]
|
|
34
40
|
# @param secure [Boolean]
|
|
35
41
|
# @param ca_cert_path [String, nil]
|
|
36
42
|
# @param tls_verify [Boolean]
|
|
43
|
+
# @param control_plane_basic_auth [Array(String, String), nil] control-plane HTTP Basic
|
|
44
|
+
# +[username, password]+, sent on every control-plane request and on the callback and
|
|
45
|
+
# breakpoint WebSockets; MockServer refuses Basic credentials without TLS, so use +secure: true+
|
|
37
46
|
def initialize(host, port, context_path: '', secure: false,
|
|
38
|
-
ca_cert_path: nil, tls_verify: true)
|
|
47
|
+
ca_cert_path: nil, tls_verify: true, control_plane_basic_auth: nil)
|
|
48
|
+
@control_plane_authorization = basic_authorization(control_plane_basic_auth)
|
|
49
|
+
if @control_plane_authorization && !secure
|
|
50
|
+
warn 'MockServer::Client: control-plane Basic credentials are configured without secure: true, so the password ' \
|
|
51
|
+
'can be read on the network, and MockServer refuses them unless controlPlaneBasicAuthenticationAllowInsecure ' \
|
|
52
|
+
'is set (tests only)'
|
|
53
|
+
end
|
|
39
54
|
@host = host
|
|
40
55
|
@port = port
|
|
41
56
|
@context_path = context_path
|
|
@@ -100,10 +115,19 @@ module MockServer
|
|
|
100
115
|
end
|
|
101
116
|
|
|
102
117
|
# Clear expectations and/or logs.
|
|
118
|
+
#
|
|
119
|
+
# With +filter:+ only the expectations matching both +request+ (when
|
|
120
|
+
# given) and the filter's metadata / unmatched selection are cleared; the
|
|
121
|
+
# event log is left untouched. A filter must select something and must not
|
|
122
|
+
# page, otherwise ArgumentError is raised and nothing is sent.
|
|
123
|
+
#
|
|
103
124
|
# @param request [HttpRequest, nil]
|
|
104
125
|
# @param type [String, nil] "EXPECTATIONS", "LOG", or "ALL"
|
|
126
|
+
# @param filter [ExpectationFilter] selection of expectations to clear
|
|
105
127
|
# @return [nil]
|
|
106
|
-
def clear(request = nil, type: nil)
|
|
128
|
+
def clear(request = nil, type: nil, filter: NO_FILTER)
|
|
129
|
+
return clear_with_filter(request, type, filter) unless filter.equal?(NO_FILTER)
|
|
130
|
+
|
|
107
131
|
query_params = {}
|
|
108
132
|
query_params['type'] = type if type
|
|
109
133
|
body = request ? JSON.generate(request.to_h) : ''
|
|
@@ -117,6 +141,25 @@ module MockServer
|
|
|
117
141
|
nil
|
|
118
142
|
end
|
|
119
143
|
|
|
144
|
+
# Clear the expectations whose metadata has +key+ with a value equal to
|
|
145
|
+
# +value+ (see {ExpectationFilter#with_metadata}).
|
|
146
|
+
# @return [nil]
|
|
147
|
+
def clear_by_metadata(key, value)
|
|
148
|
+
clear(filter: ExpectationFilter.new.with_metadata(key, value))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Clear the expectations whose metadata has +key+, with any value.
|
|
152
|
+
# @return [nil]
|
|
153
|
+
def clear_by_metadata_key(key)
|
|
154
|
+
clear(filter: ExpectationFilter.new.with_metadata_key(key))
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Clear the expectations that have not matched any request.
|
|
158
|
+
# @return [nil]
|
|
159
|
+
def clear_unmatched_expectations
|
|
160
|
+
clear(filter: ExpectationFilter.new.with_unmatched(true))
|
|
161
|
+
end
|
|
162
|
+
|
|
120
163
|
# Clear by expectation ID.
|
|
121
164
|
# @param expectation_id [String]
|
|
122
165
|
# @param type [String, nil]
|
|
@@ -265,6 +308,9 @@ module MockServer
|
|
|
265
308
|
#
|
|
266
309
|
# +config_json+ is a ConfigurationDTO JSON document; only the fields present
|
|
267
310
|
# are applied (partial update). A nil value is sent as an empty body.
|
|
311
|
+
# Properties fixed at startup, such as forwardProxyBlockPrivateNetworks,
|
|
312
|
+
# forwardProxyAllowedHosts and forwardProxyDeniedHosts, are ignored and the
|
|
313
|
+
# server logs a warning.
|
|
268
314
|
#
|
|
269
315
|
# @param config_json [String, nil] the ConfigurationDTO JSON
|
|
270
316
|
# @return [String] the serialized updated configuration JSON
|
|
@@ -1174,13 +1220,17 @@ module MockServer
|
|
|
1174
1220
|
|
|
1175
1221
|
# Retrieve active expectations.
|
|
1176
1222
|
# @param request [HttpRequest, nil]
|
|
1223
|
+
# @param filter [ExpectationFilter, nil] metadata / unmatched selection and paging
|
|
1177
1224
|
# @return [Array<Expectation>]
|
|
1178
|
-
def retrieve_active_expectations(request: nil)
|
|
1225
|
+
def retrieve_active_expectations(request: nil, filter: nil)
|
|
1226
|
+
unless filter.nil? || filter.is_a?(ExpectationFilter)
|
|
1227
|
+
raise ArgumentError, "filter must be a MockServer::ExpectationFilter, got #{filter.class}"
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1179
1230
|
body = request ? JSON.generate(request.to_h) : ''
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
)
|
|
1231
|
+
query_params = { 'type' => 'ACTIVE_EXPECTATIONS', 'format' => 'JSON' }
|
|
1232
|
+
query_params.merge!(filter.to_query_params) if filter
|
|
1233
|
+
status, response_body = do_request('PUT', '/mockserver/retrieve', body, query_params)
|
|
1184
1234
|
if status >= 400
|
|
1185
1235
|
raise Error, "Failed to retrieve active expectations (status=#{status}): #{response_body}"
|
|
1186
1236
|
end
|
|
@@ -1192,14 +1242,47 @@ module MockServer
|
|
|
1192
1242
|
[]
|
|
1193
1243
|
end
|
|
1194
1244
|
|
|
1195
|
-
# Retrieve
|
|
1245
|
+
# Retrieve active expectations whose metadata has +key+ with a value
|
|
1246
|
+
# equal to +value+ (see {ExpectationFilter#with_metadata}).
|
|
1247
|
+
# @return [Array<Expectation>]
|
|
1248
|
+
def retrieve_active_expectations_by_metadata(key, value)
|
|
1249
|
+
retrieve_active_expectations(filter: ExpectationFilter.new.with_metadata(key, value))
|
|
1250
|
+
end
|
|
1251
|
+
|
|
1252
|
+
# Retrieve active expectations whose metadata has +key+, with any value.
|
|
1253
|
+
# @return [Array<Expectation>]
|
|
1254
|
+
def retrieve_active_expectations_by_metadata_key(key)
|
|
1255
|
+
retrieve_active_expectations(filter: ExpectationFilter.new.with_metadata_key(key))
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
# Retrieve the active expectations that have not matched any request.
|
|
1259
|
+
# @return [Array<Expectation>]
|
|
1260
|
+
def retrieve_unmatched_expectations
|
|
1261
|
+
retrieve_active_expectations(filter: ExpectationFilter.new.with_unmatched(true))
|
|
1262
|
+
end
|
|
1263
|
+
|
|
1264
|
+
# Retrieve recorded expectations, optionally one page of them in the
|
|
1265
|
+
# order the requests were received.
|
|
1196
1266
|
# @param request [HttpRequest, nil]
|
|
1267
|
+
# @param offset [Integer, nil] number to skip, zero or more
|
|
1268
|
+
# @param limit [Integer, nil] maximum number to return, zero or more
|
|
1269
|
+
# @param options [RecordedExpectationsOptions, nil] shapes the returned
|
|
1270
|
+
# expectations, for example replaying a request recorded with different
|
|
1271
|
+
# responses in order
|
|
1197
1272
|
# @return [Array<Expectation>]
|
|
1198
|
-
def retrieve_recorded_expectations(request: nil)
|
|
1273
|
+
def retrieve_recorded_expectations(request: nil, offset: nil, limit: nil, options: nil)
|
|
1274
|
+
unless options.nil? || options.is_a?(RecordedExpectationsOptions)
|
|
1275
|
+
raise ArgumentError, "options must be a RecordedExpectationsOptions, got #{options.inspect}"
|
|
1276
|
+
end
|
|
1277
|
+
|
|
1278
|
+
paging = ExpectationFilter.new
|
|
1279
|
+
paging.with_offset(offset) unless offset.nil?
|
|
1280
|
+
paging.with_limit(limit) unless limit.nil?
|
|
1199
1281
|
body = request ? JSON.generate(request.to_h) : ''
|
|
1282
|
+
query_params = { 'type' => 'RECORDED_EXPECTATIONS', 'format' => 'JSON' }
|
|
1283
|
+
query_params.merge!(options.to_query_params) unless options.nil?
|
|
1200
1284
|
status, response_body = do_request(
|
|
1201
|
-
'PUT', '/mockserver/retrieve', body,
|
|
1202
|
-
{ 'type' => 'RECORDED_EXPECTATIONS', 'format' => 'JSON' }
|
|
1285
|
+
'PUT', '/mockserver/retrieve', body, query_params.merge(paging.to_query_params)
|
|
1203
1286
|
)
|
|
1204
1287
|
if status >= 400
|
|
1205
1288
|
raise Error, "Failed to retrieve recorded expectations (status=#{status}): #{response_body}"
|
|
@@ -1656,7 +1739,8 @@ module MockServer
|
|
|
1656
1739
|
context_path: @context_path,
|
|
1657
1740
|
secure: @secure,
|
|
1658
1741
|
ca_cert_path: @ca_cert_path,
|
|
1659
|
-
tls_verify: @tls_verify
|
|
1742
|
+
tls_verify: @tls_verify,
|
|
1743
|
+
authorization: @control_plane_authorization
|
|
1660
1744
|
)
|
|
1661
1745
|
ws_client.instance_variable_set(:@is_breakpoint_ws, true)
|
|
1662
1746
|
ws_client.listen
|
|
@@ -1673,7 +1757,8 @@ module MockServer
|
|
|
1673
1757
|
context_path: @context_path,
|
|
1674
1758
|
secure: @secure,
|
|
1675
1759
|
ca_cert_path: @ca_cert_path,
|
|
1676
|
-
tls_verify: @tls_verify
|
|
1760
|
+
tls_verify: @tls_verify,
|
|
1761
|
+
authorization: @control_plane_authorization
|
|
1677
1762
|
)
|
|
1678
1763
|
|
|
1679
1764
|
case callback_type
|
|
@@ -1688,6 +1773,26 @@ module MockServer
|
|
|
1688
1773
|
ws_client.client_id
|
|
1689
1774
|
end
|
|
1690
1775
|
|
|
1776
|
+
# @api private
|
|
1777
|
+
def clear_with_filter(request, type, filter)
|
|
1778
|
+
unless filter.is_a?(ExpectationFilter) && filter.selection?
|
|
1779
|
+
raise ArgumentError, 'clear with a filter requires a metadata or unmatched selection; ' \
|
|
1780
|
+
'use clear(request) to clear without one'
|
|
1781
|
+
end
|
|
1782
|
+
raise ArgumentError, 'offset and limit are not supported for clear' if filter.paging?
|
|
1783
|
+
unless type.nil? || type.to_s.casecmp?('expectations')
|
|
1784
|
+
raise ArgumentError, "clear with a filter only clears expectations, not type #{type.inspect}"
|
|
1785
|
+
end
|
|
1786
|
+
|
|
1787
|
+
body = request ? JSON.generate(request.to_h) : ''
|
|
1788
|
+
status, response_body = do_request(
|
|
1789
|
+
'PUT', '/mockserver/clear', body, { 'type' => 'expectations' }.merge(filter.to_query_params)
|
|
1790
|
+
)
|
|
1791
|
+
raise Error, "Failed to clear (status=#{status}): #{response_body}" if status >= 400
|
|
1792
|
+
|
|
1793
|
+
nil
|
|
1794
|
+
end
|
|
1795
|
+
|
|
1691
1796
|
# Perform an HTTP request with optional query parameters.
|
|
1692
1797
|
# @api private
|
|
1693
1798
|
def do_request(method, path, body = nil, query_params = nil,
|
|
@@ -1785,10 +1890,23 @@ module MockServer
|
|
|
1785
1890
|
req = Net::HTTP::Put.new(request_path)
|
|
1786
1891
|
end
|
|
1787
1892
|
req['Content-Type'] = content_type
|
|
1893
|
+
req['Authorization'] = @control_plane_authorization if @control_plane_authorization
|
|
1788
1894
|
req.body = body if body
|
|
1789
1895
|
req
|
|
1790
1896
|
end
|
|
1791
1897
|
|
|
1898
|
+
# @api private
|
|
1899
|
+
def basic_authorization(credentials)
|
|
1900
|
+
return nil if credentials.nil?
|
|
1901
|
+
|
|
1902
|
+
username, password = credentials
|
|
1903
|
+
if username.nil? || username.to_s.empty? || username.to_s.include?(':') || password.nil?
|
|
1904
|
+
raise ArgumentError, 'control_plane_basic_auth needs [username, password] with a username without ":"'
|
|
1905
|
+
end
|
|
1906
|
+
|
|
1907
|
+
"Basic #{Base64.strict_encode64("#{username}:#{password}".encode('UTF-8'))}"
|
|
1908
|
+
end
|
|
1909
|
+
|
|
1792
1910
|
# @api private
|
|
1793
1911
|
def execute_request(http, req)
|
|
1794
1912
|
# Use the block form of #start so the underlying TCP/TLS connection is
|
|
@@ -37,6 +37,14 @@ module MockServer
|
|
|
37
37
|
self
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# Set the expectation metadata, used to retrieve or clear it by metadata.
|
|
41
|
+
# @param metadata [Hash] String (or Symbol) keys to any JSON value
|
|
42
|
+
# @return [self]
|
|
43
|
+
def with_metadata(metadata)
|
|
44
|
+
@expectation.metadata = metadata
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
40
48
|
# Set the response action. Accepts an HttpResponse, HttpTemplate, or
|
|
41
49
|
# a Proc/lambda callback.
|
|
42
50
|
# @param response_or_callback [HttpResponse, HttpTemplate, Proc]
|