mockserver-client 6.1.0 → 7.1.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 +104 -0
- data/lib/mockserver/binary_launcher.rb +634 -0
- data/lib/mockserver/client.rb +263 -6
- data/lib/mockserver/forward_chain_expectation.rb +95 -0
- data/lib/mockserver/models.rb +589 -38
- data/lib/mockserver/version.rb +1 -1
- data/lib/mockserver/websocket_client.rb +129 -2
- data/lib/mockserver-client.rb +1 -0
- metadata +3 -2
data/lib/mockserver/version.rb
CHANGED
|
@@ -8,6 +8,7 @@ require 'timeout'
|
|
|
8
8
|
|
|
9
9
|
module MockServer
|
|
10
10
|
WEB_SOCKET_CORRELATION_ID_HEADER_NAME = 'WebSocketCorrelationId'
|
|
11
|
+
BREAKPOINT_ID_HEADER_NAME = 'X-MockServer-BreakpointId'
|
|
11
12
|
CLIENT_REGISTRATION_ID_HEADER = 'X-CLIENT-REGISTRATION-ID'
|
|
12
13
|
|
|
13
14
|
WEBSOCKET_PATH = '/_mockserver_callback_websocket'
|
|
@@ -17,6 +18,8 @@ module MockServer
|
|
|
17
18
|
TYPE_HTTP_REQUEST_AND_RESPONSE = 'org.mockserver.model.HttpRequestAndHttpResponse'
|
|
18
19
|
TYPE_CLIENT_ID_DTO = 'org.mockserver.serialization.model.WebSocketClientIdDTO'
|
|
19
20
|
TYPE_ERROR_DTO = 'org.mockserver.serialization.model.WebSocketErrorDTO'
|
|
21
|
+
TYPE_PAUSED_STREAM_FRAME_DTO = 'org.mockserver.serialization.model.PausedStreamFrameDTO'
|
|
22
|
+
TYPE_STREAM_FRAME_DECISION_DTO = 'org.mockserver.serialization.model.StreamFrameDecisionDTO'
|
|
20
23
|
|
|
21
24
|
MAX_RECONNECT_ATTEMPTS = 3
|
|
22
25
|
REGISTRATION_TIMEOUT = 10
|
|
@@ -32,6 +35,17 @@ module MockServer
|
|
|
32
35
|
nil
|
|
33
36
|
end
|
|
34
37
|
|
|
38
|
+
def self.extract_breakpoint_id(request)
|
|
39
|
+
return nil if request.headers.nil?
|
|
40
|
+
|
|
41
|
+
request.headers.each do |header|
|
|
42
|
+
if header.name == BREAKPOINT_ID_HEADER_NAME
|
|
43
|
+
return header.values.first if header.values && !header.values.empty?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
35
49
|
def self.add_correlation_id_header(message, correlation_id)
|
|
36
50
|
message.headers ||= []
|
|
37
51
|
message.headers.each do |header|
|
|
@@ -94,6 +108,10 @@ module MockServer
|
|
|
94
108
|
@logger.progname = 'MockServer::WebSocketClient'
|
|
95
109
|
@logger.level = Logger::WARN
|
|
96
110
|
@registration_queue = nil
|
|
111
|
+
# Per-breakpoint-id handlers for matcher-driven breakpoints
|
|
112
|
+
@breakpoint_request_handlers = {}
|
|
113
|
+
@breakpoint_response_handlers = {}
|
|
114
|
+
@breakpoint_stream_frame_handlers = {}
|
|
97
115
|
end
|
|
98
116
|
|
|
99
117
|
def connected?
|
|
@@ -124,6 +142,37 @@ module MockServer
|
|
|
124
142
|
@forward_response_callback = response_fn
|
|
125
143
|
end
|
|
126
144
|
|
|
145
|
+
# Register a REQUEST-phase breakpoint handler keyed by breakpoint id.
|
|
146
|
+
def set_breakpoint_request_handler(breakpoint_id, handler)
|
|
147
|
+
@breakpoint_request_handlers[breakpoint_id] = handler if breakpoint_id && handler
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Register a RESPONSE-phase breakpoint handler keyed by breakpoint id.
|
|
151
|
+
def set_breakpoint_response_handler(breakpoint_id, handler)
|
|
152
|
+
@breakpoint_response_handlers[breakpoint_id] = handler if breakpoint_id && handler
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Register a stream-frame breakpoint handler keyed by breakpoint id.
|
|
156
|
+
def set_breakpoint_stream_frame_handler(breakpoint_id, handler)
|
|
157
|
+
@breakpoint_stream_frame_handlers[breakpoint_id] = handler if breakpoint_id && handler
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Remove all handlers for the given breakpoint id.
|
|
161
|
+
def remove_breakpoint_handlers(breakpoint_id)
|
|
162
|
+
return unless breakpoint_id
|
|
163
|
+
|
|
164
|
+
@breakpoint_request_handlers.delete(breakpoint_id)
|
|
165
|
+
@breakpoint_response_handlers.delete(breakpoint_id)
|
|
166
|
+
@breakpoint_stream_frame_handlers.delete(breakpoint_id)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Remove all breakpoint handlers.
|
|
170
|
+
def clear_breakpoint_handlers
|
|
171
|
+
@breakpoint_request_handlers.clear
|
|
172
|
+
@breakpoint_response_handlers.clear
|
|
173
|
+
@breakpoint_stream_frame_handlers.clear
|
|
174
|
+
end
|
|
175
|
+
|
|
127
176
|
def listen
|
|
128
177
|
@listen_thread = Thread.new { listen_loop }
|
|
129
178
|
@listen_thread.abort_on_exception = false
|
|
@@ -270,8 +319,12 @@ module MockServer
|
|
|
270
319
|
if msg_type == TYPE_HTTP_REQUEST
|
|
271
320
|
request = HttpRequest.from_hash(JSON.parse(msg_value))
|
|
272
321
|
correlation_id = MockServer.extract_correlation_id(request)
|
|
322
|
+
breakpoint_id = MockServer.extract_breakpoint_id(request)
|
|
273
323
|
|
|
274
|
-
|
|
324
|
+
bp_handler = breakpoint_id ? @breakpoint_request_handlers[breakpoint_id] : nil
|
|
325
|
+
if bp_handler
|
|
326
|
+
handle_breakpoint_request(request, correlation_id, bp_handler)
|
|
327
|
+
elsif @forward_callback
|
|
275
328
|
handle_forward_request(request, correlation_id)
|
|
276
329
|
elsif @response_callback
|
|
277
330
|
handle_response_request(request, correlation_id)
|
|
@@ -284,8 +337,12 @@ module MockServer
|
|
|
284
337
|
if msg_type == TYPE_HTTP_REQUEST_AND_RESPONSE
|
|
285
338
|
req_and_resp = HttpRequestAndHttpResponse.from_hash(JSON.parse(msg_value))
|
|
286
339
|
correlation_id = MockServer.extract_correlation_id(req_and_resp.http_request)
|
|
340
|
+
breakpoint_id = MockServer.extract_breakpoint_id(req_and_resp.http_request)
|
|
287
341
|
|
|
288
|
-
|
|
342
|
+
bp_handler = breakpoint_id ? @breakpoint_response_handlers[breakpoint_id] : nil
|
|
343
|
+
if bp_handler
|
|
344
|
+
handle_breakpoint_response(req_and_resp, correlation_id, bp_handler)
|
|
345
|
+
elsif @forward_response_callback
|
|
289
346
|
handle_forward_response(req_and_resp, correlation_id)
|
|
290
347
|
else
|
|
291
348
|
@logger.warn("Received HttpRequestAndHttpResponse callback but no forward_response_callback registered")
|
|
@@ -293,6 +350,12 @@ module MockServer
|
|
|
293
350
|
return
|
|
294
351
|
end
|
|
295
352
|
|
|
353
|
+
if msg_type == TYPE_PAUSED_STREAM_FRAME_DTO
|
|
354
|
+
paused_frame = JSON.parse(msg_value)
|
|
355
|
+
handle_breakpoint_stream_frame(paused_frame)
|
|
356
|
+
return
|
|
357
|
+
end
|
|
358
|
+
|
|
296
359
|
@logger.warn("Received unhandled WebSocket message type: #{msg_type}")
|
|
297
360
|
end
|
|
298
361
|
|
|
@@ -349,5 +412,69 @@ module MockServer
|
|
|
349
412
|
@ws.send(error_msg)
|
|
350
413
|
end
|
|
351
414
|
end
|
|
415
|
+
|
|
416
|
+
def handle_breakpoint_request(request, correlation_id, handler)
|
|
417
|
+
result = handler.call(request)
|
|
418
|
+
result = request if result.nil? # auto-continue
|
|
419
|
+
|
|
420
|
+
MockServer.add_correlation_id_header(result, correlation_id) if correlation_id
|
|
421
|
+
|
|
422
|
+
type_name = result.is_a?(HttpResponse) ? TYPE_HTTP_RESPONSE : TYPE_HTTP_REQUEST
|
|
423
|
+
msg = MockServer.build_ws_message(type_name, result.to_h)
|
|
424
|
+
@ws.send(msg)
|
|
425
|
+
rescue StandardError => exc
|
|
426
|
+
@logger.error("Error in breakpoint request handler, auto-continuing: #{exc.message}")
|
|
427
|
+
MockServer.add_correlation_id_header(request, correlation_id) if correlation_id
|
|
428
|
+
msg = MockServer.build_ws_message(TYPE_HTTP_REQUEST, request.to_h)
|
|
429
|
+
@ws.send(msg)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def handle_breakpoint_response(req_and_resp, correlation_id, handler)
|
|
433
|
+
result = handler.call(req_and_resp.http_request, req_and_resp.http_response)
|
|
434
|
+
result = req_and_resp.http_response if result.nil? # auto-continue
|
|
435
|
+
|
|
436
|
+
unless result.is_a?(HttpResponse)
|
|
437
|
+
raise CallbackError, "Breakpoint response handler must return HttpResponse, got #{result.class}"
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
MockServer.add_correlation_id_header(result, correlation_id) if correlation_id
|
|
441
|
+
msg = MockServer.build_ws_message(TYPE_HTTP_RESPONSE, result.to_h)
|
|
442
|
+
@ws.send(msg)
|
|
443
|
+
rescue StandardError => exc
|
|
444
|
+
@logger.error("Error in breakpoint response handler, auto-continuing: #{exc.message}")
|
|
445
|
+
resp = req_and_resp.http_response || HttpResponse.new
|
|
446
|
+
MockServer.add_correlation_id_header(resp, correlation_id) if correlation_id
|
|
447
|
+
msg = MockServer.build_ws_message(TYPE_HTTP_RESPONSE, resp.to_h)
|
|
448
|
+
@ws.send(msg)
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def handle_breakpoint_stream_frame(paused_frame)
|
|
452
|
+
breakpoint_id = paused_frame['breakpointId']
|
|
453
|
+
correlation_id = paused_frame['correlationId'] || ''
|
|
454
|
+
handler = breakpoint_id ? @breakpoint_stream_frame_handlers[breakpoint_id] : nil
|
|
455
|
+
|
|
456
|
+
decision = if handler
|
|
457
|
+
begin
|
|
458
|
+
result = handler.call(paused_frame)
|
|
459
|
+
if result.nil?
|
|
460
|
+
{ 'correlationId' => correlation_id, 'action' => 'CONTINUE' }
|
|
461
|
+
else
|
|
462
|
+
result['correlationId'] = correlation_id # ensure echoed
|
|
463
|
+
result
|
|
464
|
+
end
|
|
465
|
+
rescue StandardError => exc
|
|
466
|
+
@logger.error("Error in breakpoint stream frame handler, auto-continuing: #{exc.message}")
|
|
467
|
+
{ 'correlationId' => correlation_id, 'action' => 'CONTINUE' }
|
|
468
|
+
end
|
|
469
|
+
else
|
|
470
|
+
{ 'correlationId' => correlation_id, 'action' => 'CONTINUE' }
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
msg = JSON.generate({
|
|
474
|
+
'type' => TYPE_STREAM_FRAME_DECISION_DTO,
|
|
475
|
+
'value' => JSON.generate(decision)
|
|
476
|
+
})
|
|
477
|
+
@ws.send(msg)
|
|
478
|
+
end
|
|
352
479
|
end
|
|
353
480
|
end
|
data/lib/mockserver-client.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mockserver-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 7.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Bloom
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: logger
|
|
@@ -91,6 +91,7 @@ files:
|
|
|
91
91
|
- Gemfile
|
|
92
92
|
- README.md
|
|
93
93
|
- lib/mockserver-client.rb
|
|
94
|
+
- lib/mockserver/binary_launcher.rb
|
|
94
95
|
- lib/mockserver/client.rb
|
|
95
96
|
- lib/mockserver/errors.rb
|
|
96
97
|
- lib/mockserver/forward_chain_expectation.rb
|