mockserver-client 1.0.8.pre → 6.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.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +2 -1
  3. data/README.md +79 -227
  4. data/lib/mockserver/client.rb +518 -0
  5. data/lib/mockserver/errors.rb +18 -0
  6. data/lib/mockserver/forward_chain_expectation.rb +117 -0
  7. data/lib/mockserver/models.rb +1507 -0
  8. data/lib/mockserver/version.rb +3 -3
  9. data/lib/mockserver/websocket_client.rb +353 -0
  10. data/lib/mockserver-client.rb +7 -16
  11. data/mockserver-client.gemspec +26 -27
  12. metadata +54 -206
  13. data/.gitignore +0 -21
  14. data/.rubocop.yml +0 -7
  15. data/Rakefile +0 -10
  16. data/bin/mockserver +0 -9
  17. data/lib/cli.rb +0 -146
  18. data/lib/mockserver/abstract_client.rb +0 -111
  19. data/lib/mockserver/mock_server_client.rb +0 -46
  20. data/lib/mockserver/model/array_of.rb +0 -85
  21. data/lib/mockserver/model/body.rb +0 -56
  22. data/lib/mockserver/model/cookie.rb +0 -36
  23. data/lib/mockserver/model/delay.rb +0 -34
  24. data/lib/mockserver/model/enum.rb +0 -47
  25. data/lib/mockserver/model/expectation.rb +0 -139
  26. data/lib/mockserver/model/forward.rb +0 -41
  27. data/lib/mockserver/model/header.rb +0 -43
  28. data/lib/mockserver/model/parameter.rb +0 -43
  29. data/lib/mockserver/model/request.rb +0 -81
  30. data/lib/mockserver/model/response.rb +0 -45
  31. data/lib/mockserver/model/times.rb +0 -61
  32. data/lib/mockserver/proxy_client.rb +0 -9
  33. data/lib/mockserver/utility_methods.rb +0 -59
  34. data/pom.xml +0 -118
  35. data/spec/fixtures/forward_mockserver.json +0 -7
  36. data/spec/fixtures/incorrect_login_response.json +0 -20
  37. data/spec/fixtures/post_login_request.json +0 -22
  38. data/spec/fixtures/register_expectation.json +0 -50
  39. data/spec/fixtures/retrieved_request.json +0 -22
  40. data/spec/fixtures/search_request.json +0 -6
  41. data/spec/fixtures/times_once.json +0 -6
  42. data/spec/integration/mock_client_integration_spec.rb +0 -82
  43. data/spec/mockserver/builder_spec.rb +0 -90
  44. data/spec/mockserver/mock_client_spec.rb +0 -80
  45. data/spec/mockserver/proxy_client_spec.rb +0 -38
  46. data/spec/spec_helper.rb +0 -61
@@ -0,0 +1,518 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'openssl'
7
+
8
+ module MockServer
9
+ # Synchronous MockServer client.
10
+ #
11
+ # Provides the full MockServer REST API plus a fluent builder DSL and
12
+ # WebSocket-based object callback support.
13
+ #
14
+ # @example Basic usage
15
+ # client = MockServer::Client.new('localhost', 1080)
16
+ # client.when(
17
+ # HttpRequest.request(path: '/hello')
18
+ # ).respond(
19
+ # HttpResponse.response(body: 'world')
20
+ # )
21
+ # client.close
22
+ #
23
+ # @example Block form (auto-close)
24
+ # MockServer::Client.new('localhost', 1080) do |c|
25
+ # c.when(HttpRequest.request(path: '/hello'))
26
+ # .respond(HttpResponse.response(body: 'world'))
27
+ # end
28
+ class Client
29
+ HTTP_TIMEOUT = 60 # seconds, matching Python client
30
+
31
+ # @param host [String]
32
+ # @param port [Integer]
33
+ # @param context_path [String]
34
+ # @param secure [Boolean]
35
+ # @param ca_cert_path [String, nil]
36
+ # @param tls_verify [Boolean]
37
+ def initialize(host, port, context_path: '', secure: false,
38
+ ca_cert_path: nil, tls_verify: true)
39
+ @host = host
40
+ @port = port
41
+ @context_path = context_path
42
+ @secure = secure
43
+ @ca_cert_path = ca_cert_path
44
+ @tls_verify = tls_verify
45
+ @websocket_clients = []
46
+ @websocket_mutex = Mutex.new
47
+
48
+ scheme = secure ? 'https' : 'http'
49
+ ctx_path = ''
50
+ if context_path && !context_path.empty?
51
+ ctx_path = context_path.start_with?('/') ? context_path : "/#{context_path}"
52
+ end
53
+ @base_url = "#{scheme}://#{host}:#{port}#{ctx_path}"
54
+
55
+ if block_given?
56
+ begin
57
+ yield self
58
+ ensure
59
+ close
60
+ end
61
+ end
62
+ end
63
+
64
+ # -------------------------------------------------------------------
65
+ # REST API methods
66
+ # -------------------------------------------------------------------
67
+
68
+ # Create or update expectations.
69
+ # @param expectations [Array<Expectation>]
70
+ # @return [Array<Expectation>]
71
+ def upsert(*expectations)
72
+ body = JSON.generate(expectations.map(&:to_h))
73
+ status, response_body = request('PUT', '/mockserver/expectation', body)
74
+ if status == 400
75
+ raise Error, "Invalid expectation: #{response_body}"
76
+ end
77
+
78
+ if status >= 400
79
+ raise Error, "Failed to upsert expectations (status=#{status}): #{response_body}"
80
+ end
81
+
82
+ if response_body && !response_body.empty?
83
+ parsed = JSON.parse(response_body)
84
+ return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
85
+ end
86
+ expectations.to_a
87
+ end
88
+
89
+ # Create an OpenAPI expectation.
90
+ # @param expectation [OpenAPIExpectation]
91
+ # @return [nil]
92
+ def open_api_expectation(expectation)
93
+ body = JSON.generate(expectation.to_h)
94
+ status, response_body = request('PUT', '/mockserver/openapi', body)
95
+ if status >= 400
96
+ raise Error, "Failed to create OpenAPI expectation (status=#{status}): #{response_body}"
97
+ end
98
+
99
+ nil
100
+ end
101
+
102
+ # Clear expectations and/or logs.
103
+ # @param request [HttpRequest, nil]
104
+ # @param type [String, nil] "EXPECTATIONS", "LOG", or "ALL"
105
+ # @return [nil]
106
+ def clear(request = nil, type: nil)
107
+ query_params = {}
108
+ query_params['type'] = type if type
109
+ body = request ? JSON.generate(request.to_h) : ''
110
+ status, response_body = do_request(
111
+ 'PUT', '/mockserver/clear', body, query_params.empty? ? nil : query_params
112
+ )
113
+ if status >= 400
114
+ raise Error, "Failed to clear (status=#{status}): #{response_body}"
115
+ end
116
+
117
+ nil
118
+ end
119
+
120
+ # Clear by expectation ID.
121
+ # @param expectation_id [String]
122
+ # @param type [String, nil]
123
+ # @return [nil]
124
+ def clear_by_id(expectation_id, type: nil)
125
+ query_params = {}
126
+ query_params['type'] = type if type
127
+ body = JSON.generate({ 'id' => expectation_id })
128
+ status, response_body = do_request(
129
+ 'PUT', '/mockserver/clear', body, query_params.empty? ? nil : query_params
130
+ )
131
+ if status >= 400
132
+ raise Error, "Failed to clear by id (status=#{status}): #{response_body}"
133
+ end
134
+
135
+ nil
136
+ end
137
+
138
+ # Reset all expectations and logs.
139
+ # @return [nil]
140
+ def reset
141
+ status, response_body = request('PUT', '/mockserver/reset')
142
+ if status >= 400
143
+ raise Error, "Failed to reset (status=#{status}): #{response_body}"
144
+ end
145
+
146
+ nil
147
+ ensure
148
+ close
149
+ end
150
+
151
+ # Verify that a request was received.
152
+ # @param request [HttpRequest]
153
+ # @param times [VerificationTimes, nil]
154
+ # @return [nil]
155
+ # @raise [VerificationError] if verification fails (HTTP 406)
156
+ def verify(request, times: nil)
157
+ verification = Verification.new(http_request: request, times: times)
158
+ body = JSON.generate(verification.to_h)
159
+ status, response_body = do_request('PUT', '/mockserver/verify', body)
160
+ if status == 406
161
+ raise VerificationError, response_body
162
+ end
163
+
164
+ if status >= 400
165
+ raise Error, "Failed to verify (status=#{status}): #{response_body}"
166
+ end
167
+
168
+ nil
169
+ end
170
+
171
+ # Verify that requests were received in sequence.
172
+ # @param requests [Array<HttpRequest>]
173
+ # @return [nil]
174
+ # @raise [VerificationError] if verification fails (HTTP 406)
175
+ def verify_sequence(*requests)
176
+ verification = VerificationSequence.new(http_requests: requests.to_a)
177
+ body = JSON.generate(verification.to_h)
178
+ status, response_body = request('PUT', '/mockserver/verifySequence', body)
179
+ if status == 406
180
+ raise VerificationError, response_body
181
+ end
182
+
183
+ if status >= 400
184
+ raise Error, "Failed to verify sequence (status=#{status}): #{response_body}"
185
+ end
186
+
187
+ nil
188
+ end
189
+
190
+ # Verify zero interactions.
191
+ # @return [nil]
192
+ def verify_zero_interactions
193
+ verify(HttpRequest.new, times: VerificationTimes.new(at_most: 0))
194
+ end
195
+
196
+ # Retrieve recorded requests.
197
+ # @param request [HttpRequest, nil]
198
+ # @return [Array<HttpRequest>]
199
+ def retrieve_recorded_requests(request: nil)
200
+ body = request ? JSON.generate(request.to_h) : ''
201
+ status, response_body = do_request(
202
+ 'PUT', '/mockserver/retrieve', body,
203
+ { 'type' => 'REQUESTS', 'format' => 'JSON' }
204
+ )
205
+ if status >= 400
206
+ raise Error, "Failed to retrieve recorded requests (status=#{status}): #{response_body}"
207
+ end
208
+
209
+ if response_body && !response_body.empty?
210
+ parsed = JSON.parse(response_body)
211
+ return parsed.map { |r| HttpRequest.from_hash(r) } if parsed.is_a?(Array)
212
+ end
213
+ []
214
+ end
215
+
216
+ # Retrieve active expectations.
217
+ # @param request [HttpRequest, nil]
218
+ # @return [Array<Expectation>]
219
+ def retrieve_active_expectations(request: nil)
220
+ body = request ? JSON.generate(request.to_h) : ''
221
+ status, response_body = do_request(
222
+ 'PUT', '/mockserver/retrieve', body,
223
+ { 'type' => 'ACTIVE_EXPECTATIONS', 'format' => 'JSON' }
224
+ )
225
+ if status >= 400
226
+ raise Error, "Failed to retrieve active expectations (status=#{status}): #{response_body}"
227
+ end
228
+
229
+ if response_body && !response_body.empty?
230
+ parsed = JSON.parse(response_body)
231
+ return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
232
+ end
233
+ []
234
+ end
235
+
236
+ # Retrieve recorded expectations.
237
+ # @param request [HttpRequest, nil]
238
+ # @return [Array<Expectation>]
239
+ def retrieve_recorded_expectations(request: nil)
240
+ body = request ? JSON.generate(request.to_h) : ''
241
+ status, response_body = do_request(
242
+ 'PUT', '/mockserver/retrieve', body,
243
+ { 'type' => 'RECORDED_EXPECTATIONS', 'format' => 'JSON' }
244
+ )
245
+ if status >= 400
246
+ raise Error, "Failed to retrieve recorded expectations (status=#{status}): #{response_body}"
247
+ end
248
+
249
+ if response_body && !response_body.empty?
250
+ parsed = JSON.parse(response_body)
251
+ return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
252
+ end
253
+ []
254
+ end
255
+
256
+ # Retrieve recorded requests and responses.
257
+ # @param request [HttpRequest, nil]
258
+ # @return [Array<HttpRequestAndHttpResponse>]
259
+ def retrieve_recorded_requests_and_responses(request: nil)
260
+ body = request ? JSON.generate(request.to_h) : ''
261
+ status, response_body = do_request(
262
+ 'PUT', '/mockserver/retrieve', body,
263
+ { 'type' => 'REQUEST_RESPONSES', 'format' => 'JSON' }
264
+ )
265
+ if status >= 400
266
+ raise Error, "Failed to retrieve request/responses (status=#{status}): #{response_body}"
267
+ end
268
+
269
+ if response_body && !response_body.empty?
270
+ parsed = JSON.parse(response_body)
271
+ return parsed.map { |rr| HttpRequestAndHttpResponse.from_hash(rr) } if parsed.is_a?(Array)
272
+ end
273
+ []
274
+ end
275
+
276
+ # Retrieve log messages.
277
+ # @param request [HttpRequest, nil]
278
+ # @return [Array<String>]
279
+ def retrieve_log_messages(request: nil)
280
+ body = request ? JSON.generate(request.to_h) : ''
281
+ status, response_body = do_request(
282
+ 'PUT', '/mockserver/retrieve', body,
283
+ { 'type' => 'LOGS' }
284
+ )
285
+ if status >= 400
286
+ raise Error, "Failed to retrieve log messages (status=#{status}): #{response_body}"
287
+ end
288
+
289
+ if response_body && !response_body.empty?
290
+ begin
291
+ parsed = JSON.parse(response_body)
292
+ return parsed if parsed.is_a?(Array)
293
+ rescue JSON::ParserError
294
+ return response_body.split("------------------------------------\n")
295
+ end
296
+ end
297
+ []
298
+ end
299
+
300
+ # Bind additional ports.
301
+ # @param ports [Array<Integer>]
302
+ # @return [Array<Integer>]
303
+ def bind(*ports)
304
+ body = JSON.generate(Ports.new(ports: ports.flatten).to_h)
305
+ status, response_body = request('PUT', '/mockserver/bind', body)
306
+ if status >= 400
307
+ raise Error, "Failed to bind ports (status=#{status}): #{response_body}"
308
+ end
309
+
310
+ if response_body && !response_body.empty?
311
+ parsed = JSON.parse(response_body)
312
+ return Ports.from_hash(parsed).ports
313
+ end
314
+ []
315
+ end
316
+
317
+ # Stop the MockServer instance.
318
+ # @return [nil]
319
+ def stop
320
+ request('PUT', '/mockserver/stop')
321
+ nil
322
+ rescue ConnectionError
323
+ nil
324
+ ensure
325
+ close
326
+ end
327
+
328
+ # Check if MockServer has started.
329
+ # @param attempts [Integer]
330
+ # @param timeout [Float] seconds between attempts
331
+ # @return [Boolean]
332
+ def has_started?(attempts: 10, timeout: 0.5)
333
+ attempts.times do |i|
334
+ begin
335
+ status, = request('PUT', '/mockserver/status')
336
+ return true if status == 200
337
+ rescue ConnectionError
338
+ # not yet started
339
+ end
340
+ sleep(timeout) if i < attempts - 1
341
+ end
342
+ false
343
+ end
344
+
345
+ alias has_started has_started?
346
+
347
+ # -------------------------------------------------------------------
348
+ # Fluent API
349
+ # -------------------------------------------------------------------
350
+
351
+ # Begin building an expectation via the fluent API.
352
+ # @param request [HttpRequest]
353
+ # @param times [Times, nil]
354
+ # @param time_to_live [TimeToLive, nil]
355
+ # @param priority [Integer, nil]
356
+ # @return [ForwardChainExpectation]
357
+ def when(request, times: nil, time_to_live: nil, priority: nil)
358
+ expectation = Expectation.new(
359
+ http_request: request,
360
+ times: times,
361
+ time_to_live: time_to_live,
362
+ priority: priority
363
+ )
364
+ ForwardChainExpectation.new(self, expectation)
365
+ end
366
+
367
+ # -------------------------------------------------------------------
368
+ # Callback methods
369
+ # -------------------------------------------------------------------
370
+
371
+ # Register a response callback via WebSocket.
372
+ # @param request [HttpRequest]
373
+ # @param callback [Proc]
374
+ # @param times [Times, nil]
375
+ # @param time_to_live [TimeToLive, nil]
376
+ # @return [Array<Expectation>]
377
+ def mock_with_callback(request, callback, times: nil, time_to_live: nil)
378
+ client_id = register_websocket_callback('response', callback)
379
+ expectation = Expectation.new(
380
+ http_request: request,
381
+ http_response_object_callback: HttpObjectCallback.new(client_id: client_id),
382
+ times: times,
383
+ time_to_live: time_to_live
384
+ )
385
+ upsert(expectation)
386
+ end
387
+
388
+ # Register a forward callback via WebSocket.
389
+ # @param request [HttpRequest]
390
+ # @param forward_callback [Proc]
391
+ # @param response_callback [Proc, nil]
392
+ # @param times [Times, nil]
393
+ # @param time_to_live [TimeToLive, nil]
394
+ # @return [Array<Expectation>]
395
+ def mock_with_forward_callback(request, forward_callback, response_callback = nil,
396
+ times: nil, time_to_live: nil)
397
+ client_id = register_websocket_callback('forward', forward_callback, response_callback)
398
+ obj_callback = HttpObjectCallback.new(client_id: client_id)
399
+ obj_callback.response_callback = true if response_callback
400
+ expectation = Expectation.new(
401
+ http_request: request,
402
+ http_forward_object_callback: obj_callback,
403
+ times: times,
404
+ time_to_live: time_to_live
405
+ )
406
+ upsert(expectation)
407
+ end
408
+
409
+ # Close all WebSocket connections.
410
+ # @return [nil]
411
+ def close
412
+ @websocket_mutex.synchronize do
413
+ @websocket_clients.each(&:close)
414
+ @websocket_clients.clear
415
+ end
416
+ nil
417
+ end
418
+
419
+ private
420
+
421
+ # @api private
422
+ def register_websocket_callback(callback_type, callback_fn, forward_response_fn = nil)
423
+ ws_client = WebSocketClient.new
424
+ ws_client.connect(
425
+ @host, @port,
426
+ context_path: @context_path,
427
+ secure: @secure,
428
+ ca_cert_path: @ca_cert_path,
429
+ tls_verify: @tls_verify
430
+ )
431
+
432
+ case callback_type
433
+ when 'response'
434
+ ws_client.register_response_callback(callback_fn)
435
+ when 'forward'
436
+ ws_client.register_forward_callback(callback_fn, forward_response_fn)
437
+ end
438
+
439
+ ws_client.listen
440
+ @websocket_mutex.synchronize { @websocket_clients << ws_client }
441
+ ws_client.client_id
442
+ end
443
+
444
+ # Perform an HTTP request with optional query parameters.
445
+ # @api private
446
+ def do_request(method, path, body = nil, query_params = nil)
447
+ url = "#{@base_url}#{path}"
448
+ if query_params && !query_params.empty?
449
+ url = "#{url}?#{URI.encode_www_form(query_params)}"
450
+ end
451
+
452
+ uri = URI.parse(url)
453
+ http = build_http(uri)
454
+
455
+ req = build_request(method, uri, body)
456
+ execute_request(http, req)
457
+ end
458
+
459
+ # Perform an HTTP request (no query params).
460
+ # @api private
461
+ def request(method, path, body = nil)
462
+ do_request(method, path, body, nil)
463
+ end
464
+
465
+ # @api private
466
+ def build_http(uri)
467
+ http = Net::HTTP.new(uri.host, uri.port)
468
+ http.read_timeout = HTTP_TIMEOUT
469
+ http.open_timeout = HTTP_TIMEOUT
470
+
471
+ if @secure
472
+ http.use_ssl = true
473
+ if @ca_cert_path
474
+ http.ca_file = @ca_cert_path
475
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
476
+ elsif !@tls_verify
477
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
478
+ else
479
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
480
+ end
481
+ end
482
+
483
+ http
484
+ end
485
+
486
+ # @api private
487
+ def build_request(method, uri, body)
488
+ request_path = uri.request_uri
489
+ case method.upcase
490
+ when 'PUT'
491
+ req = Net::HTTP::Put.new(request_path)
492
+ when 'GET'
493
+ req = Net::HTTP::Get.new(request_path)
494
+ when 'POST'
495
+ req = Net::HTTP::Post.new(request_path)
496
+ when 'DELETE'
497
+ req = Net::HTTP::Delete.new(request_path)
498
+ else
499
+ req = Net::HTTP::Put.new(request_path)
500
+ end
501
+ req['Content-Type'] = 'application/json; charset=utf-8'
502
+ req.body = body if body
503
+ req
504
+ end
505
+
506
+ # @api private
507
+ def execute_request(http, req)
508
+ response = http.request(req)
509
+ [response.code.to_i, response.body || '']
510
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
511
+ raise ConnectionError, "Request to MockServer at #{@base_url} timed out: #{e.message}"
512
+ rescue OpenSSL::SSL::SSLError => e
513
+ raise ConnectionError, "TLS error connecting to MockServer at #{@base_url}: #{e.message}"
514
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, SocketError, IOError => e
515
+ raise ConnectionError, "Failed to connect to MockServer at #{@base_url}: #{e.message}"
516
+ end
517
+ end
518
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MockServer
4
+ # Base error class for all MockServer client errors.
5
+ class Error < StandardError; end
6
+
7
+ # Raised when the client cannot connect to the MockServer instance.
8
+ class ConnectionError < Error; end
9
+
10
+ # Raised when a verification request fails (HTTP 406).
11
+ class VerificationError < Error; end
12
+
13
+ # Raised when a WebSocket callback produces an invalid result.
14
+ class CallbackError < Error; end
15
+
16
+ # Raised for WebSocket protocol-level errors (connection, registration, etc.).
17
+ class WebSocketError < Error; end
18
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MockServer
4
+ # Fluent API for building expectations via the +when+ method.
5
+ #
6
+ # Returned by {Client#when} to allow chaining:
7
+ # client.when(request).respond(response)
8
+ # client.when(request).forward(forward)
9
+ # client.when(request).error(error)
10
+ class ForwardChainExpectation
11
+ def initialize(client, expectation)
12
+ @client = client
13
+ @expectation = expectation
14
+ end
15
+
16
+ # Set the expectation ID.
17
+ # @param id [String]
18
+ # @return [self]
19
+ def with_id(id)
20
+ @expectation.id = id
21
+ self
22
+ end
23
+
24
+ # Set the expectation priority.
25
+ # @param priority [Integer]
26
+ # @return [self]
27
+ def with_priority(priority)
28
+ @expectation.priority = priority
29
+ self
30
+ end
31
+
32
+ # Set the response action. Accepts an HttpResponse, HttpTemplate, or
33
+ # a Proc/lambda callback.
34
+ # @param response_or_callback [HttpResponse, HttpTemplate, Proc]
35
+ # @return [Array<Expectation>]
36
+ def respond(response_or_callback)
37
+ if response_or_callback.respond_to?(:call)
38
+ client_id = @client.send(:register_websocket_callback, 'response', response_or_callback)
39
+ @expectation.http_response_object_callback = HttpObjectCallback.new(client_id: client_id)
40
+ elsif response_or_callback.is_a?(HttpResponse)
41
+ @expectation.http_response = response_or_callback
42
+ elsif response_or_callback.is_a?(HttpTemplate)
43
+ @expectation.http_response_template = response_or_callback
44
+ else
45
+ raise TypeError,
46
+ "Expected HttpResponse, HttpTemplate, or callable, got #{response_or_callback.class.name}"
47
+ end
48
+ @client.upsert(@expectation)
49
+ end
50
+
51
+ # Set the response action with a delay.
52
+ # @param response [HttpResponse]
53
+ # @param delay [Delay]
54
+ # @return [Array<Expectation>]
55
+ def respond_with_delay(response, delay)
56
+ response.delay = delay
57
+ @expectation.http_response = response
58
+ @client.upsert(@expectation)
59
+ end
60
+
61
+ # Set the forward action. Accepts an HttpForward, HttpOverrideForwardedRequest,
62
+ # HttpTemplate, or a Proc/lambda callback.
63
+ # @param forward_or_callback [HttpForward, HttpOverrideForwardedRequest, HttpTemplate, Proc]
64
+ # @param response_callback [Proc, nil] optional response transform callback
65
+ # @return [Array<Expectation>]
66
+ def forward(forward_or_callback, response_callback = nil)
67
+ if forward_or_callback.respond_to?(:call)
68
+ client_id = @client.send(
69
+ :register_websocket_callback,
70
+ 'forward', forward_or_callback, response_callback
71
+ )
72
+ obj_callback = HttpObjectCallback.new(client_id: client_id)
73
+ obj_callback.response_callback = true if response_callback
74
+ @expectation.http_forward_object_callback = obj_callback
75
+ elsif forward_or_callback.is_a?(HttpForward)
76
+ @expectation.http_forward = forward_or_callback
77
+ elsif forward_or_callback.is_a?(HttpOverrideForwardedRequest)
78
+ @expectation.http_override_forwarded_request = forward_or_callback
79
+ elsif forward_or_callback.is_a?(HttpTemplate)
80
+ @expectation.http_forward_template = forward_or_callback
81
+ else
82
+ raise TypeError,
83
+ "Expected HttpForward, HttpOverrideForwardedRequest, HttpTemplate, or callable, " \
84
+ "got #{forward_or_callback.class.name}"
85
+ end
86
+ @client.upsert(@expectation)
87
+ end
88
+
89
+ # Set the forward action with a delay.
90
+ # @param forward [HttpForward]
91
+ # @param delay [Delay]
92
+ # @return [Array<Expectation>]
93
+ def forward_with_delay(forward, delay)
94
+ forward.delay = delay
95
+ @expectation.http_forward = forward
96
+ @client.upsert(@expectation)
97
+ end
98
+
99
+ # Set the error action.
100
+ # @param error [HttpError]
101
+ # @return [Array<Expectation>]
102
+ def error(error)
103
+ @expectation.http_error = error
104
+ @client.upsert(@expectation)
105
+ end
106
+
107
+ def respond_with_sse(sse_response)
108
+ @expectation.http_sse_response = sse_response
109
+ @client.upsert(@expectation)
110
+ end
111
+
112
+ def respond_with_websocket(websocket_response)
113
+ @expectation.http_websocket_response = websocket_response
114
+ @client.upsert(@expectation)
115
+ end
116
+ end
117
+ end