mockserver-client 7.3.0__tar.gz → 7.5.0__tar.gz

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 (39) hide show
  1. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/PKG-INFO +59 -2
  2. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/README.md +58 -1
  3. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/__init__.py +58 -0
  4. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/async_client.py +32 -3
  5. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/client.py +11 -0
  6. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/launcher.py +1 -1
  7. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/llm.py +174 -0
  8. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/models.py +882 -7
  9. mockserver_client-7.5.0/mockserver/pytest_plugin.py +131 -0
  10. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/websocket_client.py +20 -8
  11. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver_client.egg-info/PKG-INFO +59 -2
  12. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver_client.egg-info/SOURCES.txt +4 -0
  13. mockserver_client-7.5.0/mockserver_client.egg-info/entry_points.txt +2 -0
  14. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/pyproject.toml +4 -1
  15. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_control_plane.py +31 -0
  16. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_integration.py +38 -0
  17. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_llm.py +85 -0
  18. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_models.py +711 -2
  19. mockserver_client-7.5.0/tests/test_pytest_plugin.py +64 -0
  20. mockserver_client-7.5.0/tests/test_roundtrip_fidelity.py +233 -0
  21. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/a2a.py +0 -0
  22. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/exceptions.py +0 -0
  23. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/fluent.py +0 -0
  24. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver/mcp.py +0 -0
  25. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver_client.egg-info/dependency_links.txt +0 -0
  26. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver_client.egg-info/requires.txt +0 -0
  27. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/mockserver_client.egg-info/top_level.txt +0 -0
  28. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/setup.cfg +0 -0
  29. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_a2a.py +0 -0
  30. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_async_client.py +0 -0
  31. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_auth_tls.py +0 -0
  32. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_breakpoint.py +0 -0
  33. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_client.py +0 -0
  34. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_exceptions.py +0 -0
  35. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_fluent.py +0 -0
  36. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_init.py +0 -0
  37. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_launcher.py +0 -0
  38. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_mcp.py +0 -0
  39. {mockserver_client-7.3.0 → mockserver_client-7.5.0}/tests/test_websocket_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mockserver-client
3
- Version: 7.3.0
3
+ Version: 7.5.0
4
4
  Summary: Official Python client for MockServer — create expectations, verify requests, and register dynamic callbacks via WebSocket
5
5
  Author-email: James Bloom <jamesdbloom@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -190,6 +190,63 @@ client.verify_sequence(
190
190
  client.verify_zero_interactions()
191
191
  ```
192
192
 
193
+ ## Request Matching
194
+
195
+ ### JWT matcher
196
+
197
+ Match requests by the claims (and metadata) of a JWT carried in a header. Each
198
+ claim value is exact-or-regex; prefix a value with `!` to negate it.
199
+
200
+ ```python
201
+ from mockserver import HttpRequest, HttpResponse, Jwt
202
+
203
+ client.when(
204
+ HttpRequest.request("/secure")
205
+ .with_method("GET")
206
+ .with_jwt(
207
+ claims={
208
+ "sub": "user-123", # exact match
209
+ "email": "^.+@example.com$", # regex match
210
+ "role": "!admin", # negated — matches any role except admin
211
+ },
212
+ issuer="https://issuer.example.com",
213
+ audience="my-api",
214
+ algorithm="RS256",
215
+ header="authorization", # optional (defaults to Authorization)
216
+ scheme="Bearer", # optional token scheme
217
+ )
218
+ ).respond(
219
+ HttpResponse.response("granted", status_code=200)
220
+ )
221
+
222
+ # ...or pass a Jwt object directly:
223
+ HttpRequest.request("/secure").with_jwt(Jwt(claims={"sub": "user-123"}))
224
+ ```
225
+
226
+ ### allOf body matcher
227
+
228
+ Require a request body to satisfy **all** of several body matchers at once.
229
+
230
+ ```python
231
+ from mockserver import AllOfBody, Body, JsonPathBody, RegexBody, HttpRequest
232
+
233
+ client.when(
234
+ HttpRequest.request("/api/orders")
235
+ .with_method("POST")
236
+ .with_body(AllOfBody(body_all_of=[
237
+ JsonPathBody(json_path="$.name"), # body has a name field
238
+ RegexBody(regex=".*active.*"), # and mentions "active"
239
+ ]))
240
+ ).respond(
241
+ HttpResponse.response("ok")
242
+ )
243
+
244
+ # Shorthand factory:
245
+ HttpRequest.request("/api/orders").with_body(
246
+ Body.all_of(JsonPathBody(json_path="$.name"), RegexBody(regex=".*active.*"))
247
+ )
248
+ ```
249
+
193
250
  ## Retrieval
194
251
 
195
252
  ```python
@@ -429,7 +486,7 @@ launcher_path = ensure_binary() # returns Path to the launcher executable
429
486
  ```python
430
487
  from mockserver.launcher import start
431
488
 
432
- server = start(port=1080, version="7.3.0")
489
+ server = start(port=1080, version="7.5.0")
433
490
  # ...
434
491
  server.stop()
435
492
  ```
@@ -160,6 +160,63 @@ client.verify_sequence(
160
160
  client.verify_zero_interactions()
161
161
  ```
162
162
 
163
+ ## Request Matching
164
+
165
+ ### JWT matcher
166
+
167
+ Match requests by the claims (and metadata) of a JWT carried in a header. Each
168
+ claim value is exact-or-regex; prefix a value with `!` to negate it.
169
+
170
+ ```python
171
+ from mockserver import HttpRequest, HttpResponse, Jwt
172
+
173
+ client.when(
174
+ HttpRequest.request("/secure")
175
+ .with_method("GET")
176
+ .with_jwt(
177
+ claims={
178
+ "sub": "user-123", # exact match
179
+ "email": "^.+@example.com$", # regex match
180
+ "role": "!admin", # negated — matches any role except admin
181
+ },
182
+ issuer="https://issuer.example.com",
183
+ audience="my-api",
184
+ algorithm="RS256",
185
+ header="authorization", # optional (defaults to Authorization)
186
+ scheme="Bearer", # optional token scheme
187
+ )
188
+ ).respond(
189
+ HttpResponse.response("granted", status_code=200)
190
+ )
191
+
192
+ # ...or pass a Jwt object directly:
193
+ HttpRequest.request("/secure").with_jwt(Jwt(claims={"sub": "user-123"}))
194
+ ```
195
+
196
+ ### allOf body matcher
197
+
198
+ Require a request body to satisfy **all** of several body matchers at once.
199
+
200
+ ```python
201
+ from mockserver import AllOfBody, Body, JsonPathBody, RegexBody, HttpRequest
202
+
203
+ client.when(
204
+ HttpRequest.request("/api/orders")
205
+ .with_method("POST")
206
+ .with_body(AllOfBody(body_all_of=[
207
+ JsonPathBody(json_path="$.name"), # body has a name field
208
+ RegexBody(regex=".*active.*"), # and mentions "active"
209
+ ]))
210
+ ).respond(
211
+ HttpResponse.response("ok")
212
+ )
213
+
214
+ # Shorthand factory:
215
+ HttpRequest.request("/api/orders").with_body(
216
+ Body.all_of(JsonPathBody(json_path="$.name"), RegexBody(regex=".*active.*"))
217
+ )
218
+ ```
219
+
163
220
  ## Retrieval
164
221
 
165
222
  ```python
@@ -399,7 +456,7 @@ launcher_path = ensure_binary() # returns Path to the launcher executable
399
456
  ```python
400
457
  from mockserver.launcher import start
401
458
 
402
- server = start(port=1080, version="7.3.0")
459
+ server = start(port=1080, version="7.5.0")
403
460
  # ...
404
461
  server.stop()
405
462
  ```
@@ -1,17 +1,24 @@
1
1
  from mockserver.models import (
2
2
  AfterAction,
3
+ AllOfBody,
3
4
  BinaryResponse,
4
5
  Body,
6
+ CaptureRule,
7
+ CaptureSource,
5
8
  ConnectionOptions,
6
9
  CrossProtocolScenario,
7
10
  CrossProtocolTrigger,
8
11
  Delay,
9
12
  DelayDistribution,
10
13
  DnsRecord,
14
+ DnsRecordClass,
15
+ DnsRecordType,
16
+ DnsRequestDefinition,
11
17
  DnsResponse,
12
18
  Expectation,
13
19
  ExpectationId,
14
20
  ExpectationStep,
21
+ GraphQLBody,
15
22
  GrpcBidiResponse,
16
23
  GrpcBidiRule,
17
24
  GrpcStreamMessage,
@@ -20,6 +27,8 @@ from mockserver.models import (
20
27
  HttpClassCallback,
21
28
  HttpError,
22
29
  HttpForward,
30
+ HttpForwardValidateAction,
31
+ HttpForwardWithFallback,
23
32
  HttpObjectCallback,
24
33
  HttpOverrideForwardedRequest,
25
34
  HttpRequest,
@@ -30,7 +39,19 @@ from mockserver.models import (
30
39
  HttpWebSocketResponse,
31
40
  JsonPathBody,
32
41
  JsonRpcBody,
42
+ JsonSchemaBody,
43
+ Jwt,
33
44
  KeyToMultiValue,
45
+ MultipartBody,
46
+ ParameterBody,
47
+ Protocol,
48
+ RateLimit,
49
+ RateLimitAlgorithm,
50
+ ValidationMode,
51
+ WasmBody,
52
+ XmlBody,
53
+ XmlSchemaBody,
54
+ XPathBody,
34
55
  LoadCapture,
35
56
  LoadFeeder,
36
57
  LoadPacing,
@@ -44,6 +65,7 @@ from mockserver.models import (
44
65
  OpenAPIDefinition,
45
66
  OpenAPIExpectation,
46
67
  Ports,
68
+ RegexBody,
47
69
  RequestDefinition,
48
70
  ResponseMode,
49
71
  SocketAddress,
@@ -53,10 +75,12 @@ from mockserver.models import (
53
75
  Verification,
54
76
  VerificationSequence,
55
77
  VerificationTimes,
78
+ WebSocketFrameMatcher,
56
79
  WebSocketMessage,
57
80
  )
58
81
  from mockserver.llm import (
59
82
  Completion,
83
+ ContentFilter,
60
84
  ConversationPredicates,
61
85
  EmbeddingResponse,
62
86
  HttpLlmResponse,
@@ -65,14 +89,17 @@ from mockserver.llm import (
65
89
  LlmConversationBuilder,
66
90
  LlmFailoverBuilder,
67
91
  LlmMockBuilder,
92
+ ModerationResponse,
68
93
  NormalizationOptions,
69
94
  Provider,
95
+ RerankResponse,
70
96
  Role,
71
97
  StreamingPhysics,
72
98
  ToolUse,
73
99
  TurnBuilder,
74
100
  Usage,
75
101
  completion,
102
+ content_filter,
76
103
  conversation,
77
104
  cookie,
78
105
  embedding,
@@ -80,7 +107,9 @@ from mockserver.llm import (
80
107
  llm_failover,
81
108
  llm_mock,
82
109
  llm_response,
110
+ moderation,
83
111
  query_parameter,
112
+ rerank,
84
113
  streaming_physics,
85
114
  time_to_first_token,
86
115
  tool_use,
@@ -119,19 +148,26 @@ from mockserver.launcher import (
119
148
 
120
149
  __all__ = [
121
150
  "AfterAction",
151
+ "AllOfBody",
122
152
  "AsyncMockServerClient",
123
153
  "BinaryResponse",
124
154
  "Body",
155
+ "CaptureRule",
156
+ "CaptureSource",
125
157
  "ConnectionOptions",
126
158
  "CrossProtocolScenario",
127
159
  "CrossProtocolTrigger",
128
160
  "Delay",
129
161
  "DelayDistribution",
130
162
  "DnsRecord",
163
+ "DnsRecordClass",
164
+ "DnsRecordType",
165
+ "DnsRequestDefinition",
131
166
  "DnsResponse",
132
167
  "Expectation",
133
168
  "ExpectationId",
134
169
  "ExpectationStep",
170
+ "GraphQLBody",
135
171
  "GrpcBidiResponse",
136
172
  "GrpcBidiRule",
137
173
  "GrpcStreamMessage",
@@ -140,6 +176,8 @@ __all__ = [
140
176
  "HttpClassCallback",
141
177
  "HttpError",
142
178
  "HttpForward",
179
+ "HttpForwardValidateAction",
180
+ "HttpForwardWithFallback",
143
181
  "HttpObjectCallback",
144
182
  "HttpOverrideForwardedRequest",
145
183
  "HttpRequest",
@@ -150,7 +188,19 @@ __all__ = [
150
188
  "HttpWebSocketResponse",
151
189
  "JsonPathBody",
152
190
  "JsonRpcBody",
191
+ "JsonSchemaBody",
192
+ "Jwt",
153
193
  "KeyToMultiValue",
194
+ "MultipartBody",
195
+ "ParameterBody",
196
+ "Protocol",
197
+ "RateLimit",
198
+ "RateLimitAlgorithm",
199
+ "ValidationMode",
200
+ "WasmBody",
201
+ "XmlBody",
202
+ "XmlSchemaBody",
203
+ "XPathBody",
154
204
  "LoadCapture",
155
205
  "LoadFeeder",
156
206
  "LoadPacing",
@@ -162,6 +212,7 @@ __all__ = [
162
212
  "LoadThreshold",
163
213
  "MockMode",
164
214
  "Completion",
215
+ "ContentFilter",
165
216
  "ConversationPredicates",
166
217
  "EmbeddingResponse",
167
218
  "HttpLlmResponse",
@@ -170,14 +221,17 @@ __all__ = [
170
221
  "LlmConversationBuilder",
171
222
  "LlmFailoverBuilder",
172
223
  "LlmMockBuilder",
224
+ "ModerationResponse",
173
225
  "NormalizationOptions",
174
226
  "Provider",
227
+ "RerankResponse",
175
228
  "Role",
176
229
  "StreamingPhysics",
177
230
  "ToolUse",
178
231
  "TurnBuilder",
179
232
  "Usage",
180
233
  "completion",
234
+ "content_filter",
181
235
  "conversation",
182
236
  "cookie",
183
237
  "embedding",
@@ -185,7 +239,9 @@ __all__ = [
185
239
  "llm_failover",
186
240
  "llm_mock",
187
241
  "llm_response",
242
+ "moderation",
188
243
  "query_parameter",
244
+ "rerank",
189
245
  "streaming_physics",
190
246
  "time_to_first_token",
191
247
  "tool_use",
@@ -208,6 +264,7 @@ __all__ = [
208
264
  "OpenAPIDefinition",
209
265
  "OpenAPIExpectation",
210
266
  "Ports",
267
+ "RegexBody",
211
268
  "RequestDefinition",
212
269
  "ResponseMode",
213
270
  "SocketAddress",
@@ -217,6 +274,7 @@ __all__ = [
217
274
  "Verification",
218
275
  "VerificationSequence",
219
276
  "VerificationTimes",
277
+ "WebSocketFrameMatcher",
220
278
  "WebSocketMessage",
221
279
  "MockServerProcess",
222
280
  "bundle_base_name",
@@ -58,7 +58,7 @@ class AsyncScenarioHandle:
58
58
  async def state(self) -> str | None:
59
59
  """GET the current state of this scenario (``None`` if not yet set)."""
60
60
  result = await self._client._scenario_request(
61
- "GET", f"/mockserver/scenario/{urllib.parse.quote(self._name, safe="")}"
61
+ "GET", f"/mockserver/scenario/{urllib.parse.quote(self._name, safe='')}"
62
62
  )
63
63
  return result.get("currentState")
64
64
 
@@ -78,7 +78,7 @@ class AsyncScenarioHandle:
78
78
  payload["nextState"] = next_state
79
79
  return await self._client._scenario_request(
80
80
  "PUT",
81
- f"/mockserver/scenario/{urllib.parse.quote(self._name, safe="")}",
81
+ f"/mockserver/scenario/{urllib.parse.quote(self._name, safe='')}",
82
82
  json.dumps(payload),
83
83
  )
84
84
 
@@ -86,7 +86,7 @@ class AsyncScenarioHandle:
86
86
  """PUT an external trigger advancing this scenario to ``new_state``."""
87
87
  return await self._client._scenario_request(
88
88
  "PUT",
89
- f"/mockserver/scenario/{urllib.parse.quote(self._name, safe="")}/trigger",
89
+ f"/mockserver/scenario/{urllib.parse.quote(self._name, safe='')}/trigger",
90
90
  json.dumps({"newState": new_state}),
91
91
  )
92
92
 
@@ -383,6 +383,35 @@ class AsyncMockServerClient:
383
383
  )
384
384
  return response_body or ""
385
385
 
386
+ async def retrieve_drift(self) -> dict:
387
+ """Retrieve the recorded mock drift report.
388
+
389
+ Issues ``GET /mockserver/drift`` and returns the parsed report, a dict of
390
+ the form ``{"count": <n>, "drifts": [ ... ]}`` where each entry describes a
391
+ difference detected between a mock's configured response and the live
392
+ upstream response for the same request. When no drift has been recorded the
393
+ server returns an empty report (``{}``).
394
+ """
395
+ status, response_body = await self._request("GET", "/mockserver/drift")
396
+ if status >= 400:
397
+ raise MockServerError(
398
+ f"Failed to retrieve drift (status={status}): {response_body}"
399
+ )
400
+ return json.loads(response_body) if response_body else {}
401
+
402
+ async def clear_drift(self) -> None:
403
+ """Clear all recorded mock drift.
404
+
405
+ Issues ``PUT /mockserver/drift/clear``.
406
+ """
407
+ status, response_body = await self._request(
408
+ "PUT", "/mockserver/drift/clear"
409
+ )
410
+ if status >= 400:
411
+ raise MockServerError(
412
+ f"Failed to clear drift (status={status}): {response_body}"
413
+ )
414
+
386
415
  async def pact_import(self, pact_json: str) -> str:
387
416
  """Import a Pact v3 contract as expectations.
388
417
 
@@ -224,6 +224,17 @@ class MockServerClient:
224
224
  """
225
225
  return self._run(self._async_client.update_configuration(config_json))
226
226
 
227
+ def retrieve_drift(self) -> dict:
228
+ """Retrieve the recorded mock drift report
229
+ (``GET /mockserver/drift``); returns a dict of the form
230
+ ``{"count": <n>, "drifts": [ ... ]}``.
231
+ """
232
+ return self._run(self._async_client.retrieve_drift())
233
+
234
+ def clear_drift(self) -> None:
235
+ """Clear all recorded mock drift (``PUT /mockserver/drift/clear``)."""
236
+ return self._run(self._async_client.clear_drift())
237
+
227
238
  def pact_import(self, pact_json: str) -> str:
228
239
  """Import a Pact v3 contract as expectations; returns the upserted
229
240
  expectations JSON (``PUT /mockserver/pact/import``).
@@ -45,7 +45,7 @@ logger = logging.getLogger(__name__)
45
45
 
46
46
  # The version shipped with this client. ``ensure_binary()`` defaults to this
47
47
  # when no explicit version is passed.
48
- _CLIENT_VERSION = "7.1.0"
48
+ _CLIENT_VERSION = "7.5.0"
49
49
 
50
50
  _REPO = "mock-server/mockserver-monorepo"
51
51