mockserver-client 7.0.0 → 7.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 +216 -0
- data/lib/mockserver/binary_launcher.rb +664 -0
- data/lib/mockserver/client.rb +462 -13
- data/lib/mockserver/forward_chain_expectation.rb +16 -6
- data/lib/mockserver/llm.rb +855 -0
- data/lib/mockserver/mcp.rb +453 -0
- data/lib/mockserver/models.rb +488 -37
- data/lib/mockserver/rspec.rb +56 -0
- data/lib/mockserver/version.rb +1 -1
- data/lib/mockserver/websocket_client.rb +129 -2
- data/lib/mockserver-client.rb +3 -0
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1a47589dbc527bf772961f968c2235d9b64ebc13fd8a1f963e143e50319db9f
|
|
4
|
+
data.tar.gz: 85aa291fefefdce077d8f4eaca22a5fd56d473628ec819c2029fae9f8e91aa4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b5adcd45c05bc417a62ad3a6fab6e00b42ef1180b7a89cc0084f972bb83aac5d68832d2e02da83d899a7fb511a71fb2ff87576eba4487f51a0bfafc546d5c06
|
|
7
|
+
data.tar.gz: dd83794b0fd9ad0ffc7439b505bc7ff3c8461e076d6acf209ce8b273b6ca4d233c19638ece936faaacf43e196d39a1a9220206c1a559dbe86dd2154a15b078e8
|
data/README.md
CHANGED
|
@@ -99,6 +99,222 @@ All 25 domain model classes are available under the `MockServer` module:
|
|
|
99
99
|
- `Ports`
|
|
100
100
|
- `RequestDefinition` (alias for `HttpRequest`)
|
|
101
101
|
|
|
102
|
+
## LLM Mocking
|
|
103
|
+
|
|
104
|
+
Fluent builders under `MockServer::LLM` mock LLM provider endpoints. They produce
|
|
105
|
+
expectations whose action is carried in the `httpLlmResponse` field; MockServer
|
|
106
|
+
re-encodes the provider-agnostic completion into the configured provider's wire
|
|
107
|
+
shape (OpenAI / Anthropic / Gemini / Bedrock / ...) when serving the request.
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
require 'mockserver-client'
|
|
111
|
+
|
|
112
|
+
client = MockServer::Client.new('localhost', 1080)
|
|
113
|
+
|
|
114
|
+
# A single completion mock
|
|
115
|
+
MockServer::LLM.llm_mock('/v1/chat/completions')
|
|
116
|
+
.with_provider(MockServer::LLM::Provider::OPENAI)
|
|
117
|
+
.with_model('gpt-4o')
|
|
118
|
+
.responding_with(
|
|
119
|
+
MockServer::LLM.completion
|
|
120
|
+
.with_text('Hello!')
|
|
121
|
+
.with_usage(MockServer::LLM.usage.with_input_tokens(10).with_output_tokens(5))
|
|
122
|
+
)
|
|
123
|
+
.apply_to(client)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Multi-turn **conversations** use MockServer scenario-state advancement so each
|
|
127
|
+
turn is served once, in order. Add per-turn predicates with `when_*` methods and
|
|
128
|
+
optionally isolate per session with `isolate_by`:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
MockServer::LLM.conversation
|
|
132
|
+
.with_path('/v1/chat/completions')
|
|
133
|
+
.with_provider(MockServer::LLM::Provider::OPENAI)
|
|
134
|
+
.isolate_by(MockServer::LLM.header('x-session-id'))
|
|
135
|
+
.turn.when_latest_message_contains('weather')
|
|
136
|
+
.responding_with(MockServer::LLM.completion.with_text('It is sunny.'))
|
|
137
|
+
.turn.responding_with(MockServer::LLM.completion.with_text('Anything else?'))
|
|
138
|
+
.apply_to(client)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Failover** scenarios script N upstream failures (consecutive identical failures
|
|
142
|
+
are coalesced) followed by a success; default JSON error bodies are generated per
|
|
143
|
+
status code unless a custom body is supplied:
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
MockServer::LLM.llm_failover
|
|
147
|
+
.with_path('/v1/chat/completions')
|
|
148
|
+
.with_provider(MockServer::LLM::Provider::OPENAI)
|
|
149
|
+
.fail_with(429, 2) # two rate-limit failures
|
|
150
|
+
.fail_with(503) # then one service-unavailable
|
|
151
|
+
.then_respond_with(MockServer::LLM.completion.with_text('Recovered'))
|
|
152
|
+
.apply_to(client)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Each builder also exposes `build` to obtain the raw expectation Hash(es) without
|
|
156
|
+
registering them.
|
|
157
|
+
|
|
158
|
+
## MCP Mocking
|
|
159
|
+
|
|
160
|
+
`MockServer::MCP.mcp_mock` builds the set of expectations needed to emulate a
|
|
161
|
+
Streamable-HTTP MCP (Model Context Protocol) server speaking JSON-RPC 2.0. It
|
|
162
|
+
generates `initialize`, `ping`, `notifications/initialized`, and per-capability
|
|
163
|
+
`tools`, `resources`, and `prompts` handlers.
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
MockServer::MCP.mcp_mock('/mcp')
|
|
167
|
+
.with_server_name('MyServer')
|
|
168
|
+
.with_tool('get_weather')
|
|
169
|
+
.with_description('Get the weather for a city')
|
|
170
|
+
.with_input_schema('{"type":"object","properties":{"city":{"type":"string"}}}')
|
|
171
|
+
.responding_with('72F and sunny')
|
|
172
|
+
.and_then
|
|
173
|
+
.with_resource('file:///config.json')
|
|
174
|
+
.with_name('config')
|
|
175
|
+
.with_content('{"debug":true}')
|
|
176
|
+
.and_then
|
|
177
|
+
.with_prompt('greet')
|
|
178
|
+
.with_argument('name', 'who to greet', true)
|
|
179
|
+
.responding_with('user', 'Hello there')
|
|
180
|
+
.and_then
|
|
181
|
+
.apply_to(client)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`build` returns the raw expectation Hashes if you prefer to register them yourself.
|
|
185
|
+
|
|
186
|
+
## Interactive Breakpoints
|
|
187
|
+
|
|
188
|
+
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.
|
|
189
|
+
|
|
190
|
+
### Register a breakpoint
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
client = MockServer::Client.new('localhost', 1080)
|
|
194
|
+
|
|
195
|
+
# REQUEST phase only
|
|
196
|
+
bp_id = client.add_request_breakpoint(
|
|
197
|
+
MockServer::HttpRequest.new(path: '/api/.*'),
|
|
198
|
+
->(request) { request } # continue unchanged (or return HttpResponse to abort)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# REQUEST + RESPONSE
|
|
202
|
+
bp_id = client.add_request_and_response_breakpoint(
|
|
203
|
+
MockServer::HttpRequest.new(path: '/api/.*'),
|
|
204
|
+
->(request) { request }, # REQUEST handler
|
|
205
|
+
->(request, response) { response } # RESPONSE handler
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# All phases with stream frame handler
|
|
209
|
+
bp_id = client.add_breakpoint(
|
|
210
|
+
MockServer::HttpRequest.new(path: '/stream/.*'),
|
|
211
|
+
%w[REQUEST RESPONSE RESPONSE_STREAM INBOUND_STREAM],
|
|
212
|
+
request_handler: ->(request) { request },
|
|
213
|
+
response_handler: ->(request, response) { response },
|
|
214
|
+
stream_frame_handler: ->(frame) { { 'action' => 'CONTINUE' } }
|
|
215
|
+
# Other actions: MODIFY (with body), DROP, INJECT (with body), CLOSE
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Manage breakpoints
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
# List all matchers
|
|
223
|
+
matchers = client.list_breakpoint_matchers # {"matchers" => [...]}
|
|
224
|
+
|
|
225
|
+
# Remove a specific matcher
|
|
226
|
+
client.remove_breakpoint_matcher(bp_id)
|
|
227
|
+
|
|
228
|
+
# Clear all matchers
|
|
229
|
+
client.clear_breakpoint_matchers
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Start / Launch MockServer
|
|
233
|
+
|
|
234
|
+
The Ruby client can download and launch a local MockServer instance directly -- no Java installation and no Docker required. The launcher downloads a self-contained platform bundle (`mockserver-<version>-<os>-<arch>`) from the GitHub Release, verifies its SHA-256, caches it per-user, and starts it.
|
|
235
|
+
|
|
236
|
+
### Quick start
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
require 'mockserver-client'
|
|
240
|
+
|
|
241
|
+
# Download (first run) and start MockServer on port 1080
|
|
242
|
+
handle = MockServer::BinaryLauncher.start(port: 1080)
|
|
243
|
+
puts "MockServer running on port #{handle.port}, PID #{handle.pid}"
|
|
244
|
+
|
|
245
|
+
# ... use MockServer ...
|
|
246
|
+
|
|
247
|
+
handle.stop
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Just ensure the binary is present
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
launcher_path = MockServer::BinaryLauncher.ensure_launcher
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Specify a version
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
handle = MockServer::BinaryLauncher.start(port: 1080, version: '7.2.0')
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### API reference
|
|
263
|
+
|
|
264
|
+
| Method / Class | Description |
|
|
265
|
+
|---|---|
|
|
266
|
+
| `MockServer::BinaryLauncher.ensure_launcher(version:, log:)` | Download, verify, cache, and return the launcher path. Defaults to `MockServer::VERSION`. |
|
|
267
|
+
| `MockServer::BinaryLauncher.start(port:, version:, extra_args:, log:)` | Ensure the binary and start MockServer. Returns a `ServerHandle`. |
|
|
268
|
+
| `MockServer::BinaryLauncher::ServerHandle` | Handle to the running process. Methods: `stop(timeout:)`, `running?`. Attributes: `pid`, `port`, `launcher`. |
|
|
269
|
+
|
|
270
|
+
### Supported platforms
|
|
271
|
+
|
|
272
|
+
| OS | Architecture |
|
|
273
|
+
|---|---|
|
|
274
|
+
| Linux | x86_64, aarch64 |
|
|
275
|
+
| macOS (darwin) | x86_64, aarch64 |
|
|
276
|
+
| Windows | x86_64, aarch64 |
|
|
277
|
+
|
|
278
|
+
### Environment variables
|
|
279
|
+
|
|
280
|
+
| Variable | Purpose |
|
|
281
|
+
|---|---|
|
|
282
|
+
| `MOCKSERVER_BINARY_BASE_URL` | Mirror host for the release assets (corporate / air-gapped networks) |
|
|
283
|
+
| `MOCKSERVER_BINARY_CACHE` | Override the cache directory (default: `~/.cache/mockserver/binaries` on Unix) |
|
|
284
|
+
| `MOCKSERVER_SKIP_BINARY_DOWNLOAD` | Fail instead of downloading (use with a pre-seeded cache in CI) |
|
|
285
|
+
|
|
286
|
+
### Version
|
|
287
|
+
|
|
288
|
+
By default the launcher downloads the MockServer version matching this client gem (currently `MockServer::VERSION` from `lib/mockserver/version.rb`). Pass an explicit `version:` keyword to override.
|
|
289
|
+
|
|
290
|
+
## Using in tests (RSpec)
|
|
291
|
+
|
|
292
|
+
Require `mockserver/rspec` to get a shared context that provides a fresh
|
|
293
|
+
`MockServer::Client` per example and resets the server before and after each
|
|
294
|
+
example, so recorded requests, expectations and logs never leak between
|
|
295
|
+
examples:
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
# spec_helper.rb
|
|
299
|
+
require 'mockserver/rspec'
|
|
300
|
+
|
|
301
|
+
# any spec tagged :mockserver gets a reset `mockserver` client
|
|
302
|
+
RSpec.describe 'my integration', :mockserver do
|
|
303
|
+
it 'records the request' do
|
|
304
|
+
# `mockserver` is the shared, reset client
|
|
305
|
+
mockserver.when(
|
|
306
|
+
MockServer::HttpRequest.request(path: '/hello')
|
|
307
|
+
).respond(
|
|
308
|
+
MockServer::HttpResponse.response(body: 'world', status_code: 200)
|
|
309
|
+
)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Host and port default to `127.0.0.1:1080` and can be overridden with the
|
|
315
|
+
`MOCKSERVER_HOST` / `MOCKSERVER_PORT` environment variables, or by defining a
|
|
316
|
+
`mockserver_host` / `mockserver_port` `let` in your example group.
|
|
317
|
+
|
|
102
318
|
## License
|
|
103
319
|
|
|
104
320
|
Apache-2.0
|