Class: MockServer::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/mockserver/client.rb

Overview

Synchronous MockServer client.

Provides the full MockServer REST API plus a fluent builder DSL and WebSocket-based object callback support.

Examples:

Basic usage

client = MockServer::Client.new('localhost', 1080)
client.when(
  HttpRequest.request(path: '/hello')
).respond(
  HttpResponse.response(body: 'world')
)
client.close

Block form (auto-close)

MockServer::Client.new('localhost', 1080) do |c|
  c.when(HttpRequest.request(path: '/hello'))
   .respond(HttpResponse.response(body: 'world'))
end

Constant Summary collapse

HTTP_TIMEOUT =

seconds, matching Python client

60

Instance Method Summary collapse

Constructor Details

#initialize(host, port, context_path: '', secure: false, ca_cert_path: nil, tls_verify: true) ⇒ Client

Returns a new instance of Client.

Parameters:

  • host (String)
  • port (Integer)
  • context_path (String) (defaults to: '')
  • secure (Boolean) (defaults to: false)
  • ca_cert_path (String, nil) (defaults to: nil)
  • tls_verify (Boolean) (defaults to: true)


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/mockserver/client.rb', line 37

def initialize(host, port, context_path: '', secure: false,
               ca_cert_path: nil, tls_verify: true)
  @host = host
  @port = port
  @context_path = context_path
  @secure = secure
  @ca_cert_path = ca_cert_path
  @tls_verify = tls_verify
  @websocket_clients = []
  @websocket_mutex = Mutex.new

  scheme = secure ? 'https' : 'http'
  ctx_path = ''
  if context_path && !context_path.empty?
    ctx_path = context_path.start_with?('/') ? context_path : "/#{context_path}"
  end
  @base_url = "#{scheme}://#{host}:#{port}#{ctx_path}"

  if block_given?
    begin
      yield self
    ensure
      close
    end
  end
end

Instance Method Details

#bind(*ports) ⇒ Array<Integer>

Bind additional ports.

Parameters:

  • ports (Array<Integer>)

Returns:

  • (Array<Integer>)


303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/mockserver/client.rb', line 303

def bind(*ports)
  body = JSON.generate(Ports.new(ports: ports.flatten).to_h)
  status, response_body = request('PUT', '/mockserver/bind', body)
  if status >= 400
    raise Error, "Failed to bind ports (status=#{status}): #{response_body}"
  end

  if response_body && !response_body.empty?
    parsed = JSON.parse(response_body)
    return Ports.from_hash(parsed).ports
  end
  []
end

#clear(request = nil, type: nil) ⇒ nil

Clear expectations and/or logs.

Parameters:

  • request (HttpRequest, nil) (defaults to: nil)
  • type (String, nil) (defaults to: nil)

    “EXPECTATIONS”, “LOG”, or “ALL”

Returns:

  • (nil)


106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/mockserver/client.rb', line 106

def clear(request = nil, type: nil)
  query_params = {}
  query_params['type'] = type if type
  body = request ? JSON.generate(request.to_h) : ''
  status, response_body = do_request(
    'PUT', '/mockserver/clear', body, query_params.empty? ? nil : query_params
  )
  if status >= 400
    raise Error, "Failed to clear (status=#{status}): #{response_body}"
  end

  nil
end

#clear_by_id(expectation_id, type: nil) ⇒ nil

Clear by expectation ID.

Parameters:

  • expectation_id (String)
  • type (String, nil) (defaults to: nil)

Returns:

  • (nil)


124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/mockserver/client.rb', line 124

def clear_by_id(expectation_id, type: nil)
  query_params = {}
  query_params['type'] = type if type
  body = JSON.generate({ 'id' => expectation_id })
  status, response_body = do_request(
    'PUT', '/mockserver/clear', body, query_params.empty? ? nil : query_params
  )
  if status >= 400
    raise Error, "Failed to clear by id (status=#{status}): #{response_body}"
  end

  nil
end

#closenil

Close all WebSocket connections.

Returns:

  • (nil)


411
412
413
414
415
416
417
# File 'lib/mockserver/client.rb', line 411

def close
  @websocket_mutex.synchronize do
    @websocket_clients.each(&:close)
    @websocket_clients.clear
  end
  nil
end

#has_started?(attempts: 10, timeout: 0.5) ⇒ Boolean Also known as: has_started

Check if MockServer has started.

Parameters:

  • attempts (Integer) (defaults to: 10)
  • timeout (Float) (defaults to: 0.5)

    seconds between attempts

Returns:

  • (Boolean)


332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/mockserver/client.rb', line 332

def has_started?(attempts: 10, timeout: 0.5)
  attempts.times do |i|
    begin
      status, = request('PUT', '/mockserver/status')
      return true if status == 200
    rescue ConnectionError
      # not yet started
    end
    sleep(timeout) if i < attempts - 1
  end
  false
end

#mock_with_callback(request, callback, times: nil, time_to_live: nil) ⇒ Array<Expectation>

Register a response callback via WebSocket.

Parameters:

Returns:



377
378
379
380
381
382
383
384
385
386
# File 'lib/mockserver/client.rb', line 377

def mock_with_callback(request, callback, times: nil, time_to_live: nil)
  client_id = register_websocket_callback('response', callback)
  expectation = Expectation.new(
    http_request: request,
    http_response_object_callback: HttpObjectCallback.new(client_id: client_id),
    times: times,
    time_to_live: time_to_live
  )
  upsert(expectation)
end

#mock_with_forward_callback(request, forward_callback, response_callback = nil, times: nil, time_to_live: nil) ⇒ Array<Expectation>

Register a forward callback via WebSocket.

Parameters:

  • request (HttpRequest)
  • forward_callback (Proc)
  • response_callback (Proc, nil) (defaults to: nil)
  • times (Times, nil) (defaults to: nil)
  • time_to_live (TimeToLive, nil) (defaults to: nil)

Returns:



395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/mockserver/client.rb', line 395

def mock_with_forward_callback(request, forward_callback, response_callback = nil,
                                times: nil, time_to_live: nil)
  client_id = register_websocket_callback('forward', forward_callback, response_callback)
  obj_callback = HttpObjectCallback.new(client_id: client_id)
  obj_callback.response_callback = true if response_callback
  expectation = Expectation.new(
    http_request: request,
    http_forward_object_callback: obj_callback,
    times: times,
    time_to_live: time_to_live
  )
  upsert(expectation)
end

#open_api_expectation(expectation) ⇒ nil

Create an OpenAPI expectation.

Parameters:

Returns:

  • (nil)


92
93
94
95
96
97
98
99
100
# File 'lib/mockserver/client.rb', line 92

def open_api_expectation(expectation)
  body = JSON.generate(expectation.to_h)
  status, response_body = request('PUT', '/mockserver/openapi', body)
  if status >= 400
    raise Error, "Failed to create OpenAPI expectation (status=#{status}): #{response_body}"
  end

  nil
end

#resetnil

Reset all expectations and logs.

Returns:

  • (nil)


140
141
142
143
144
145
146
147
148
149
# File 'lib/mockserver/client.rb', line 140

def reset
  status, response_body = request('PUT', '/mockserver/reset')
  if status >= 400
    raise Error, "Failed to reset (status=#{status}): #{response_body}"
  end

  nil
ensure
  close
end

#retrieve_active_expectations(request: nil) ⇒ Array<Expectation>

Retrieve active expectations.

Parameters:

Returns:



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/mockserver/client.rb', line 219

def retrieve_active_expectations(request: nil)
  body = request ? JSON.generate(request.to_h) : ''
  status, response_body = do_request(
    'PUT', '/mockserver/retrieve', body,
    { 'type' => 'ACTIVE_EXPECTATIONS', 'format' => 'JSON' }
  )
  if status >= 400
    raise Error, "Failed to retrieve active expectations (status=#{status}): #{response_body}"
  end

  if response_body && !response_body.empty?
    parsed = JSON.parse(response_body)
    return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
  end
  []
end

#retrieve_log_messages(request: nil) ⇒ Array<String>

Retrieve log messages.

Parameters:

Returns:

  • (Array<String>)


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/mockserver/client.rb', line 279

def retrieve_log_messages(request: nil)
  body = request ? JSON.generate(request.to_h) : ''
  status, response_body = do_request(
    'PUT', '/mockserver/retrieve', body,
    { 'type' => 'LOGS' }
  )
  if status >= 400
    raise Error, "Failed to retrieve log messages (status=#{status}): #{response_body}"
  end

  if response_body && !response_body.empty?
    begin
      parsed = JSON.parse(response_body)
      return parsed if parsed.is_a?(Array)
    rescue JSON::ParserError
      return response_body.split("------------------------------------\n")
    end
  end
  []
end

#retrieve_recorded_expectations(request: nil) ⇒ Array<Expectation>

Retrieve recorded expectations.

Parameters:

Returns:



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/mockserver/client.rb', line 239

def retrieve_recorded_expectations(request: nil)
  body = request ? JSON.generate(request.to_h) : ''
  status, response_body = do_request(
    'PUT', '/mockserver/retrieve', body,
    { 'type' => 'RECORDED_EXPECTATIONS', 'format' => 'JSON' }
  )
  if status >= 400
    raise Error, "Failed to retrieve recorded expectations (status=#{status}): #{response_body}"
  end

  if response_body && !response_body.empty?
    parsed = JSON.parse(response_body)
    return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
  end
  []
end

#retrieve_recorded_requests(request: nil) ⇒ Array<HttpRequest>

Retrieve recorded requests.

Parameters:

Returns:



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/mockserver/client.rb', line 199

def retrieve_recorded_requests(request: nil)
  body = request ? JSON.generate(request.to_h) : ''
  status, response_body = do_request(
    'PUT', '/mockserver/retrieve', body,
    { 'type' => 'REQUESTS', 'format' => 'JSON' }
  )
  if status >= 400
    raise Error, "Failed to retrieve recorded requests (status=#{status}): #{response_body}"
  end

  if response_body && !response_body.empty?
    parsed = JSON.parse(response_body)
    return parsed.map { |r| HttpRequest.from_hash(r) } if parsed.is_a?(Array)
  end
  []
end

#retrieve_recorded_requests_and_responses(request: nil) ⇒ Array<HttpRequestAndHttpResponse>

Retrieve recorded requests and responses.

Parameters:

Returns:



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/mockserver/client.rb', line 259

def retrieve_recorded_requests_and_responses(request: nil)
  body = request ? JSON.generate(request.to_h) : ''
  status, response_body = do_request(
    'PUT', '/mockserver/retrieve', body,
    { 'type' => 'REQUEST_RESPONSES', 'format' => 'JSON' }
  )
  if status >= 400
    raise Error, "Failed to retrieve request/responses (status=#{status}): #{response_body}"
  end

  if response_body && !response_body.empty?
    parsed = JSON.parse(response_body)
    return parsed.map { |rr| HttpRequestAndHttpResponse.from_hash(rr) } if parsed.is_a?(Array)
  end
  []
end

#stopnil

Stop the MockServer instance.

Returns:

  • (nil)


319
320
321
322
323
324
325
326
# File 'lib/mockserver/client.rb', line 319

def stop
  request('PUT', '/mockserver/stop')
  nil
rescue ConnectionError
  nil
ensure
  close
end

#upsert(*expectations) ⇒ Array<Expectation>

Create or update expectations.

Parameters:

Returns:



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/mockserver/client.rb', line 71

def upsert(*expectations)
  body = JSON.generate(expectations.map(&:to_h))
  status, response_body = request('PUT', '/mockserver/expectation', body)
  if status == 400
    raise Error, "Invalid expectation: #{response_body}"
  end

  if status >= 400
    raise Error, "Failed to upsert expectations (status=#{status}): #{response_body}"
  end

  if response_body && !response_body.empty?
    parsed = JSON.parse(response_body)
    return parsed.map { |e| Expectation.from_hash(e) } if parsed.is_a?(Array)
  end
  expectations.to_a
end

#verify(request, times: nil) ⇒ nil

Verify that a request was received.

Parameters:

Returns:

  • (nil)

Raises:



156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/mockserver/client.rb', line 156

def verify(request, times: nil)
  verification = Verification.new(http_request: request, times: times)
  body = JSON.generate(verification.to_h)
  status, response_body = do_request('PUT', '/mockserver/verify', body)
  if status == 406
    raise VerificationError, response_body
  end

  if status >= 400
    raise Error, "Failed to verify (status=#{status}): #{response_body}"
  end

  nil
end

#verify_sequence(*requests) ⇒ nil

Verify that requests were received in sequence.

Parameters:

Returns:

  • (nil)

Raises:



175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/mockserver/client.rb', line 175

def verify_sequence(*requests)
  verification = VerificationSequence.new(http_requests: requests.to_a)
  body = JSON.generate(verification.to_h)
  status, response_body = request('PUT', '/mockserver/verifySequence', body)
  if status == 406
    raise VerificationError, response_body
  end

  if status >= 400
    raise Error, "Failed to verify sequence (status=#{status}): #{response_body}"
  end

  nil
end

#verify_zero_interactionsnil

Verify zero interactions.

Returns:

  • (nil)


192
193
194
# File 'lib/mockserver/client.rb', line 192

def verify_zero_interactions
  verify(HttpRequest.new, times: VerificationTimes.new(at_most: 0))
end

#when(request, times: nil, time_to_live: nil, priority: nil) ⇒ ForwardChainExpectation

Begin building an expectation via the fluent API.

Parameters:

  • request (HttpRequest)
  • times (Times, nil) (defaults to: nil)
  • time_to_live (TimeToLive, nil) (defaults to: nil)
  • priority (Integer, nil) (defaults to: nil)

Returns:



357
358
359
360
361
362
363
364
365
# File 'lib/mockserver/client.rb', line 357

def when(request, times: nil, time_to_live: nil, priority: nil)
  expectation = Expectation.new(
    http_request: request,
    times: times,
    time_to_live: time_to_live,
    priority: priority
  )
  ForwardChainExpectation.new(self, expectation)
end