proxy-mock 2.13.0__tar.gz → 3.0.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.
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/CHANGELOG.md +114 -0
- proxy_mock-3.0.0/MIGRATING.md +425 -0
- proxy_mock-3.0.0/PKG-INFO +703 -0
- proxy_mock-3.0.0/README.md +671 -0
- proxy_mock-3.0.0/examples/README.md +79 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/examples/compose/README.md +18 -15
- proxy_mock-3.0.0/proxy_mock/_server.py +15 -0
- proxy_mock-3.0.0/proxy_mock/api/errors.py +51 -0
- proxy_mock-3.0.0/proxy_mock/api/openapi.py +112 -0
- proxy_mock-3.0.0/proxy_mock/api/routes/admin.py +324 -0
- proxy_mock-3.0.0/proxy_mock/api/schemas.py +87 -0
- proxy_mock-3.0.0/proxy_mock/app/__init__.py +7 -0
- proxy_mock-3.0.0/proxy_mock/app/factory.py +157 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/cli.py +11 -2
- proxy_mock-3.0.0/proxy_mock/client/__init__.py +12 -0
- proxy_mock-3.0.0/proxy_mock/client/async_client.py +203 -0
- proxy_mock-3.0.0/proxy_mock/client/calls.py +154 -0
- proxy_mock-3.0.0/proxy_mock/client/client.py +124 -0
- proxy_mock-3.0.0/proxy_mock/client/migration.py +40 -0
- proxy_mock-3.0.0/proxy_mock/client/route.py +67 -0
- proxy_mock-3.0.0/proxy_mock/client/service_endpoints.py +14 -0
- proxy_mock-3.0.0/proxy_mock/client/transport.py +49 -0
- proxy_mock-3.0.0/proxy_mock/domain/models.py +187 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/pytest_plugin.py +7 -3
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/repositories/mock_storage.py +8 -17
- proxy_mock-3.0.0/proxy_mock/services/mock_service.py +112 -0
- proxy_mock-3.0.0/proxy_mock/services/recordings.py +141 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/request_parser.py +2 -2
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/rule_engine.py +22 -6
- proxy_mock-3.0.0/proxy_mock/services/sequences.py +59 -0
- proxy_mock-3.0.0/proxy_mock/services/snapshot.py +240 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/utils.py +49 -32
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/pyproject.toml +6 -3
- proxy_mock-2.13.0/MIGRATING.md +0 -128
- proxy_mock-2.13.0/PKG-INFO +0 -425
- proxy_mock-2.13.0/README.md +0 -393
- proxy_mock-2.13.0/examples/README.md +0 -40
- proxy_mock-2.13.0/proxy_mock/api/routes/admin.py +0 -25
- proxy_mock-2.13.0/proxy_mock/api/routes/configure.py +0 -51
- proxy_mock-2.13.0/proxy_mock/api/routes/service.py +0 -25
- proxy_mock-2.13.0/proxy_mock/api/routes/storage.py +0 -88
- proxy_mock-2.13.0/proxy_mock/api/routes/traffic.py +0 -98
- proxy_mock-2.13.0/proxy_mock/api/schemas.py +0 -10
- proxy_mock-2.13.0/proxy_mock/app/__init__.py +0 -3
- proxy_mock-2.13.0/proxy_mock/app/factory.py +0 -122
- proxy_mock-2.13.0/proxy_mock/client/__init__.py +0 -4
- proxy_mock-2.13.0/proxy_mock/client/async_client.py +0 -287
- proxy_mock-2.13.0/proxy_mock/client/client.py +0 -220
- proxy_mock-2.13.0/proxy_mock/client/migration.py +0 -42
- proxy_mock-2.13.0/proxy_mock/client/route.py +0 -106
- proxy_mock-2.13.0/proxy_mock/client/service_endpoints.py +0 -15
- proxy_mock-2.13.0/proxy_mock/core/deprecation.py +0 -51
- proxy_mock-2.13.0/proxy_mock/domain/models.py +0 -95
- proxy_mock-2.13.0/proxy_mock/repositories/ttl_cache.py +0 -35
- proxy_mock-2.13.0/proxy_mock/services/cache_service.py +0 -46
- proxy_mock-2.13.0/proxy_mock/services/mock_service.py +0 -106
- proxy_mock-2.13.0/proxy_mock/services/snapshot.py +0 -153
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/.gitignore +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/LICENSE +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/__init__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/__main__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/any_catcher.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/api/__init__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/api/deps.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/api/routes/__init__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/app/state.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/__init__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/logging.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/serializers.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/settings.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/time.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/urls.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/domain/__init__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/domain/constants.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/repositories/__init__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/repositories/traffic_store.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/__init__.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/proxy_service.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/response_factory.py +0 -0
- {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/traffic_service.py +0 -0
|
@@ -1,3 +1,117 @@
|
|
|
1
|
+
Version 3.0.0
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
Released on 2026-10-02.
|
|
5
|
+
|
|
6
|
+
3.0 is the single breaking release announced in 2.13. Breaking changes are listed first; the
|
|
7
|
+
upgrade path for each of them is in [MIGRATING.md](MIGRATING.md). Projects that need the 2.x
|
|
8
|
+
API can pin `proxy_mock>=2.13,<3`.
|
|
9
|
+
|
|
10
|
+
Breaking changes
|
|
11
|
+
----------------
|
|
12
|
+
|
|
13
|
+
* **Breaking: administrative REST API.** The configurable administrative namespace defaults
|
|
14
|
+
to `/__admin` and includes docs and OpenAPI. Legacy service routes and `POST` aliases are
|
|
15
|
+
removed; their former paths are available to user mocks. The whole namespace is protected
|
|
16
|
+
from mock shadowing, including unknown administrative paths.
|
|
17
|
+
* Individual mocks use `/__admin/mocks?path=<encoded-path>`. `PUT` creates (`201`) or replaces
|
|
18
|
+
(`200`); `PATCH` updates an existing mock (`200`) and preserves omitted fields. Explicit null
|
|
19
|
+
clears nullable fields, nested response properties merge, and arrays replace in full.
|
|
20
|
+
Missing item reads, patches and deletes return `404`. Administrative errors share
|
|
21
|
+
`{"success": false, "error": {"code": "...", "message": "...", "details": ...}}`, with
|
|
22
|
+
optional details. Invalid msgpack fields, including arbitrary binary values and mixed key
|
|
23
|
+
types, return these structured validation errors without changing existing configuration.
|
|
24
|
+
* Snapshots export format 2 and import formats 1 and 2. `GET /__admin/snapshot` exports;
|
|
25
|
+
`PUT` replaces and `PATCH` merges complete mocks by path. Traffic uses `GET` and
|
|
26
|
+
`DELETE /__admin/traffic`; recording settings use `GET` and `PATCH /__admin/settings`.
|
|
27
|
+
Invalid traffic filters return `422`.
|
|
28
|
+
* The Python clients and pytest fixtures use the administrative API.
|
|
29
|
+
|
|
30
|
+
* **Breaking: removed response caching.** The cache resource, `cache_time`, `clean_cache()`,
|
|
31
|
+
response-cache implementation and TTL storage are removed. Client helpers reject the old
|
|
32
|
+
argument locally; HTTP configuration rejects it with `422`. Static delays apply on every
|
|
33
|
+
request, and ordinary mock/rule proxying fetches the upstream every time. Saved upstream
|
|
34
|
+
replies use explicit record/replay.
|
|
35
|
+
* Legacy snapshot imports discard disabled null/zero cache settings and reject enabled or
|
|
36
|
+
invalid settings atomically with a migration hint. New storage and exports omit the field.
|
|
37
|
+
|
|
38
|
+
* **Breaking: unified Python transports.** `ProxyMock.execute_request()` now returns
|
|
39
|
+
`httpx2.Response`, matching `AsyncProxyMock`. Replace `.ok` / response truth testing with
|
|
40
|
+
explicit status checks, `allow_redirects` with `follow_redirects`, raw `data=` with
|
|
41
|
+
`content=`, and requests session/adapters with native httpx2 clients. Wrapper-created
|
|
42
|
+
clients do not follow redirects by default; both use a 10-second per-operation timeout.
|
|
43
|
+
* Both clients wrap every httpx2 `RequestError`, preserving its cause. HTTP response errors
|
|
44
|
+
expose `.response`; the wrapper's `raise_for_status=True` still checks >=400. All four
|
|
45
|
+
wrapper error classes are exported from `proxy_mock.client`, with shared base classes.
|
|
46
|
+
* Added `http_client=` injection and `.http_client` access for native transport settings.
|
|
47
|
+
Context exit closes owned clients, while injected clients remain caller-owned. Request
|
|
48
|
+
timeouts override the wrapper constructor timeout, including `None` to disable them.
|
|
49
|
+
* Removed requests and its exclusive dependencies from the package and lockfile.
|
|
50
|
+
|
|
51
|
+
* **Breaking: optional server dependencies.** The base `proxy_mock` install depends only on
|
|
52
|
+
httpx2 and msgpack. Use `proxy_mock[server]` for local server startup, the app factory and
|
|
53
|
+
local pytest fixtures; FastAPI, Pydantic and uvicorn retain their existing bounds.
|
|
54
|
+
* Base-only environments support both clients and pytest fixtures using `PROXY_MOCK_URL`.
|
|
55
|
+
CLI help/version do not import server packages; local startup reports how to install the
|
|
56
|
+
extra, with exit code 2 and no traceback. Server code still ships in the same wheel.
|
|
57
|
+
|
|
58
|
+
* **Breaking: removed the deprecated `clean_storage(path=...)` selector** from both clients.
|
|
59
|
+
Use `delete_mock(path)` for one mock or `clean_storage()` for the entire collection.
|
|
60
|
+
Old keyword/positional selectors raise `TypeError` before any data is deleted.
|
|
61
|
+
|
|
62
|
+
New features
|
|
63
|
+
------------
|
|
64
|
+
|
|
65
|
+
* Added ordered response sequences to mocks and rules. Each entry has its own response body,
|
|
66
|
+
status and headers. Exhaustion repeats the final response by default; the `error` policy
|
|
67
|
+
returns `409`. Reservations are atomic and happen before delays.
|
|
68
|
+
* Added `GET` and `PATCH /__admin/sequence-state` and matching sync/async client helpers to
|
|
69
|
+
inspect and restart a cursor. Unrelated PATCH updates preserve positions; explicit sequence
|
|
70
|
+
or rule replacement restarts the corresponding cursors. Full mock replacement resets all.
|
|
71
|
+
* Sequence configuration rejects incompatible proxy settings on the mock and on the rule.
|
|
72
|
+
Format 1 imports containing sequences return `422`; format 2 preserves their definitions
|
|
73
|
+
and binary responses.
|
|
74
|
+
|
|
75
|
+
* Added explicit mock-level record/replay. Record mode proxies every request and stores the
|
|
76
|
+
last completed HTTP reply per exact method/path/query/body key, with optional selected
|
|
77
|
+
headers. Replay returns the saved reply without upstream access; missing entries return
|
|
78
|
+
`404`. Transport failures are not recorded, while upstream HTTP errors are.
|
|
79
|
+
* Added `GET` and `DELETE /__admin/recordings` with required mock path and optional entry id,
|
|
80
|
+
plus both client helpers. Binary bodies, repeated response headers, redirects, decompressed
|
|
81
|
+
payloads and HEAD representation lengths are supported. An absent HEAD length stays absent
|
|
82
|
+
on replay, and a compressed HEAD length is omitted together with its content encoding.
|
|
83
|
+
* Recording collections have configurable entry and byte limits. Late responses cannot
|
|
84
|
+
restore deleted recordings or mutate a replaced mock generation. Snapshot format 1 rejects
|
|
85
|
+
record/replay explicitly; format 2 preserves completed recordings.
|
|
86
|
+
|
|
87
|
+
* Added snapshot format 2 with mock/rule sequence definitions and completed recording entries,
|
|
88
|
+
preserving binary bodies, repeated header pairs, matching keys and eviction order. Export
|
|
89
|
+
is observational; imported sequence cursors restart at zero. Traffic and pending
|
|
90
|
+
requests are excluded. Format 1 remains accepted for legacy mock configuration.
|
|
91
|
+
* Snapshot validation checks the whole document, including recording integrity, duplicate ids,
|
|
92
|
+
header/body consistency and configured storage limits, before mutating state. Invalid data
|
|
93
|
+
is rejected rather than trimmed. Merge replaces supplied mocks and recordings in full.
|
|
94
|
+
* CLI preload and both clients restore format 2; OpenAPI documents both accepted formats and
|
|
95
|
+
the format 2 export. Ambiguous config bodies containing both body and body_b64 are rejected.
|
|
96
|
+
|
|
97
|
+
Fixes and documentation
|
|
98
|
+
-----------------------
|
|
99
|
+
|
|
100
|
+
* **Fixed:** each application created by `create_app()` owns its mock storage alongside
|
|
101
|
+
routes, recordings and sequence cursors. In 2.x the mock storage was shared by the process,
|
|
102
|
+
so an embedded second application saw and could clear the first one's mocks. Recreating an
|
|
103
|
+
application now starts empty.
|
|
104
|
+
* Rewrote the HTTP reference for the administrative REST API and added a migration guide
|
|
105
|
+
section for every breaking change. The 2.13 alias behaviour is kept as a separate reference.
|
|
106
|
+
* Added runnable REST lifecycle, binary sequence, offline recording snapshot and async-client
|
|
107
|
+
examples. The storefront scripts accept local URLs and a custom administrative prefix, so
|
|
108
|
+
their HTTP behavior is tested without Docker as well as through the Compose CI check.
|
|
109
|
+
* The release workflow no longer publishes a container image to GHCR: the registry package was
|
|
110
|
+
never publicly readable. Build the image from the repository as described in the README.
|
|
111
|
+
* Development instructions, Docker and server CI checks select the `server` extra explicitly.
|
|
112
|
+
Added isolated base/server wheel checks, including clients and fixtures against an external
|
|
113
|
+
server.
|
|
114
|
+
|
|
1
115
|
Version 2.13.0
|
|
2
116
|
==============
|
|
3
117
|
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# Migrating from 2.x to 3.0
|
|
2
|
+
|
|
3
|
+
3.0 is a breaking release. It replaces the administrative endpoints with a resource-oriented
|
|
4
|
+
REST API, adds response sequences and record/replay, removes response caching and moves both
|
|
5
|
+
Python clients to `httpx2`. 2.13 keeps the old routes, status codes, response bodies and client
|
|
6
|
+
transports; see [the 2.13 reference](#released-213-compatibility-reference) to stay on 2.x for now.
|
|
7
|
+
|
|
8
|
+
Both clients now use `httpx2`. The base install contains clients; the optional `server` extra
|
|
9
|
+
provides dependencies for a local server as described below.
|
|
10
|
+
|
|
11
|
+
## Upgrade checklist
|
|
12
|
+
|
|
13
|
+
1. Upgrade clients and server together; a 2.x client is not HTTP-compatible with a 3.0 server,
|
|
14
|
+
even when both use `/__admin`.
|
|
15
|
+
2. Install the base package for an external server, or the `server` extra for local startup.
|
|
16
|
+
Match the administrative prefix in both environments.
|
|
17
|
+
3. Move handwritten HTTP calls to the resource table below. Review replacement versus partial
|
|
18
|
+
update, missing-item errors, and administrative error envelopes.
|
|
19
|
+
4. Update Python response checks, request body arguments, redirects, timeouts and native client
|
|
20
|
+
customization using the transport section below.
|
|
21
|
+
5. Remove caching calls and fields. Choose explicit record/replay for saved upstream replies;
|
|
22
|
+
ordinary proxying fetches every time.
|
|
23
|
+
6. Keep a copy of existing format 1 snapshots. Import is supported, but new format 2 exports
|
|
24
|
+
cannot be loaded by a 2.x server. Sequence cursors restart on import.
|
|
25
|
+
7. Run the [3.0 examples](examples/README.md) against a dedicated instance; pytest fixture
|
|
26
|
+
cleanup clears its mocks and traffic.
|
|
27
|
+
|
|
28
|
+
## Administrative REST API
|
|
29
|
+
|
|
30
|
+
The server and both Python clients now default to `/__admin`. Set `PROXY_MOCK_ADMIN_PREFIX` on
|
|
31
|
+
the server and pass the same `admin_prefix` to clients to change it. The pytest fixtures read
|
|
32
|
+
the environment setting for both local and externally configured servers. An unset value uses
|
|
33
|
+
`/__admin`; an empty value is invalid. Prefixes use absolute paths of letters, digits,
|
|
34
|
+
underscores, hyphens and optional path segments, without a trailing slash.
|
|
35
|
+
|
|
36
|
+
The whole administrative namespace is reserved, including unknown children. User mocks cannot
|
|
37
|
+
shadow it. Legacy service paths are removed and become ordinary mock paths: `/storage`,
|
|
38
|
+
`/traffic`, `/configure_mock`, `/docs`, `/redoc` and `/openapi.json` no longer provide service
|
|
39
|
+
operations. A prefix provides routing isolation, not authentication.
|
|
40
|
+
|
|
41
|
+
| Operation in 2.x | Resource and method in 3.0 |
|
|
42
|
+
|-----------------|----------------------------|
|
|
43
|
+
| `GET /proxy_mock` | `GET /__admin` |
|
|
44
|
+
| `POST /configure_mock` | `PUT /__admin/mocks?path=<encoded-path>` |
|
|
45
|
+
| `PATCH /configure_mock` | `PATCH /__admin/mocks?path=<encoded-path>` |
|
|
46
|
+
| `GET /storage` | `GET /__admin/mocks` |
|
|
47
|
+
| `GET /storage?path=...` | `GET /__admin/mocks?path=<encoded-path>` |
|
|
48
|
+
| `DELETE /storage`, `POST /storage/clean` without a path | `DELETE /__admin/mocks` |
|
|
49
|
+
| Delete a specific mock | `DELETE /__admin/mocks?path=<encoded-path>` |
|
|
50
|
+
| `GET /traffic` | `GET /__admin/traffic` |
|
|
51
|
+
| `DELETE /traffic`, `POST /traffic/clean` | `DELETE /__admin/traffic` |
|
|
52
|
+
| `GET /traffic/settings` | `GET /__admin/settings` |
|
|
53
|
+
| `PATCH /traffic/settings`, `POST /traffic/settings` | `PATCH /__admin/settings` |
|
|
54
|
+
| `GET /storage/snapshot` | `GET /__admin/snapshot` |
|
|
55
|
+
| `POST /storage/snapshot?mode=replace` | `PUT /__admin/snapshot` |
|
|
56
|
+
| `POST /storage/snapshot?mode=merge` | `PATCH /__admin/snapshot` |
|
|
57
|
+
| `POST /cache/clean` | Removed; no administrative cache resource |
|
|
58
|
+
| `GET /docs`, `/redoc`, `/openapi.json` | `GET /__admin/docs`, `/__admin/redoc`, `/__admin/openapi.json` |
|
|
59
|
+
|
|
60
|
+
There are no action-style `POST` aliases. A query is part of the resource URI, so
|
|
61
|
+
`/__admin/mocks?path=%2Finventory` identifies the configuration for `/inventory` without
|
|
62
|
+
mixing its potentially nested or encoded request path into the administrative path hierarchy.
|
|
63
|
+
Use a client's query encoder rather than concatenating arbitrary paths into a URL.
|
|
64
|
+
|
|
65
|
+
### Mock creation, replacement and partial updates
|
|
66
|
+
|
|
67
|
+
The mock path moves to the required query parameter on `PUT` and `PATCH`. A body `path` is
|
|
68
|
+
optional and, when supplied, must match. `GET` and `DELETE` without a query address the whole
|
|
69
|
+
collection; `?path=` is invalid and never means all mocks.
|
|
70
|
+
|
|
71
|
+
Before, in 2.x:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
curl -X POST http://localhost:5000/configure_mock \
|
|
75
|
+
-H 'Content-Type: application/json' \
|
|
76
|
+
-d '{"path":"/inventory","mock_data":{"body":{"available":3}}}'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
After, in 3.0:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
curl -X PUT 'http://localhost:5000/__admin/mocks?path=%2Finventory' \
|
|
83
|
+
-H 'Content-Type: application/json' \
|
|
84
|
+
-d '{"mock_data":{"body":{"available":3}}}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`PUT` creates the mock with `201` or replaces it with `200`. Repeating it produces the same
|
|
88
|
+
configuration, and omitted properties return to defaults. `PATCH` returns `200` only for an
|
|
89
|
+
existing mock and preserves omitted properties. It merges supplied `mock_data` properties,
|
|
90
|
+
while replacing a supplied response body or array as a whole. It is not JSON Merge Patch.
|
|
91
|
+
Explicit `null` clears nullable fields; non-nullable fields reject null. For example:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
curl -X PATCH 'http://localhost:5000/__admin/mocks?path=%2Finventory' \
|
|
95
|
+
-H 'Content-Type: application/json' \
|
|
96
|
+
-d '{"proxy_host":null,"mock_data":{"body":null,"status_code":204},"rules":[]}'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This clears the proxy target and response body, changes the status and removes every rule,
|
|
100
|
+
while retaining response headers and other omitted fields. JSON and msgpack mock bodies remain
|
|
101
|
+
supported. Missing item reads, patches and deletes return `404`; a repeated deletion therefore
|
|
102
|
+
returns `404` after its first successful `200`, while remaining idempotent in its effect.
|
|
103
|
+
|
|
104
|
+
### Responses and validation
|
|
105
|
+
|
|
106
|
+
Successful responses retain their operation-specific `success`/data envelopes; snapshot
|
|
107
|
+
export still returns the snapshot document directly. Administrative errors now use:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{"success": false, "error": {"code": "mock_not_found", "message": "No mock found for /inventory"}}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`error.details` is optional. Update callers that expected a string or list in `error`, a
|
|
114
|
+
successful empty result for a missing mock, or `201` for replacement. Invalid representations,
|
|
115
|
+
empty paths, body/query path mismatches and invalid traffic filters return `422`. Malformed
|
|
116
|
+
JSON/msgpack returns `400`; unsupported mock body content types return `415`. Unsupported
|
|
117
|
+
administrative methods on known resources return `405`. These errors do not change user-defined mock responses.
|
|
118
|
+
|
|
119
|
+
### Traffic, settings and snapshots
|
|
120
|
+
|
|
121
|
+
Traffic reads accept `path`, `method` and `limit`. Deletion clears all traffic and accepts no
|
|
122
|
+
query parameters; supplying a filter returns `422` without deleting records. Settings remain a partial update
|
|
123
|
+
of `record_unknown_traffic` and/or positive `max_items` through `PATCH /__admin/settings`.
|
|
124
|
+
|
|
125
|
+
Snapshots export format 2; imports and CLI preload accept formats 1 and 2, including binary
|
|
126
|
+
`body_b64`. Format 2 adds sequence definitions and completed recordings. Use `PUT` to replace
|
|
127
|
+
storage or `PATCH` to merge complete mocks keyed by path. Snapshot PATCH is a custom merge:
|
|
128
|
+
it replaces every included mock in full, keeps other paths, and is not RFC 7396 JSON Merge
|
|
129
|
+
Patch. Both methods accept the exported snapshot document. Invalid imports leave storage
|
|
130
|
+
unchanged. The old `POST` and `mode` query switch are removed.
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
curl http://localhost:5000/__admin/snapshot > mocks.json
|
|
134
|
+
curl -X PUT http://localhost:5000/__admin/snapshot \
|
|
135
|
+
-H 'Content-Type: application/json' --data-binary @mocks.json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Python clients
|
|
139
|
+
|
|
140
|
+
Use 3.0 clients with a 3.0 server. The public convenience methods select
|
|
141
|
+
the new resource methods; `configure_mock()` uses PUT and `patch_mock()` uses PATCH.
|
|
142
|
+
`import_mocks(..., mode="replace")` uses PUT and `mode="merge"` uses PATCH. Generic
|
|
143
|
+
`execute_request()` calls still use the exact route you provide, so migrate any handwritten
|
|
144
|
+
administrative requests. 2.13 clients use a different HTTP contract even when their
|
|
145
|
+
opt-in prefix is `/__admin`.
|
|
146
|
+
|
|
147
|
+
Both clients return `httpx2.Response`; migrate request arguments and response handling
|
|
148
|
+
as described below. Response caching is removed; local startup requires the `server` extra.
|
|
149
|
+
`clean_storage()` deletes the whole collection. The deprecated `path` argument is removed
|
|
150
|
+
and raises `TypeError`, including explicit null; use `delete_mock(path)` for one mock.
|
|
151
|
+
|
|
152
|
+
## Record/replay
|
|
153
|
+
|
|
154
|
+
Record/replay is explicit: configure `recording: {"mode": "record"}` with a mock-level
|
|
155
|
+
`proxy_host`, then switch with `PATCH` to `recording: {"mode": "replay"}`. This preserves
|
|
156
|
+
completed replies while upstream and `match_headers` remain unchanged. Repeat non-default
|
|
157
|
+
recording settings when switching; the object is replaced in full. Both clients accept
|
|
158
|
+
`recording=...`, `get_recordings()` and `delete_recordings()`.
|
|
159
|
+
|
|
160
|
+
`GET` and `DELETE /__admin/recordings?path=...` inspect and clear a mock's recorded replies;
|
|
161
|
+
an optional `id` selects one entry. Modes belong to the mock resource rather than action
|
|
162
|
+
endpoints. Matching uses method, raw path/query and exact body bytes, optionally selected
|
|
163
|
+
headers. Replay does not contact the upstream and returns `404` on a missing key. Completed
|
|
164
|
+
HTTP errors are recorded; transport errors are not. Collections have configurable count and
|
|
165
|
+
byte limits, and record-mode responses report whether storage succeeded through
|
|
166
|
+
`X-Proxy-Mock-Recording`. See the [full contract](README.md#recordreplay), including lifecycle,
|
|
167
|
+
concurrent requests, binary bodies and repeated headers.
|
|
168
|
+
|
|
169
|
+
Snapshot format 2 preserves recording configuration and completed replies, including binary
|
|
170
|
+
bodies, repeated header pairs and eviction order. Invalid keys or collections exceeding their
|
|
171
|
+
configured limits return `422` before mutation. Format 1 has no recording representation and
|
|
172
|
+
rejects this data. 2.13 has no record/replay API.
|
|
173
|
+
|
|
174
|
+
## Response sequences
|
|
175
|
+
|
|
176
|
+
Mocks and rules accept `sequence: {"responses": [...], "on_exhaustion": "repeat_last"}`.
|
|
177
|
+
The default repeats the final response; `on_exhaustion: "error"` returns `409` when all entries
|
|
178
|
+
have been consumed. Existing static mocks and rule matching keep their behavior when no
|
|
179
|
+
sequence is configured. See [the sequence contract](README.md#response-sequences) for examples.
|
|
180
|
+
|
|
181
|
+
Inspect cursors with `GET /__admin/sequence-state?path=...`; reset one with `PATCH` and
|
|
182
|
+
`{"position": 0}`. Add `rule=<zero-based-index>` to select a rule cursor. Both clients provide
|
|
183
|
+
`get_sequence_state()` and `reset_sequence()`. Full mock replacement starts fresh; unrelated
|
|
184
|
+
partial updates keep cursor positions. Requests already assigned a response keep it across
|
|
185
|
+
reset, reconfiguration or deletion.
|
|
186
|
+
|
|
187
|
+
Format 2 exports sequence definitions and binary responses; imported cursors start at zero.
|
|
188
|
+
Export does not change positions. Format 1 imports with sequences return `422` atomically.
|
|
189
|
+
Ordinary format 1 snapshots remain supported. A 2.x server cannot read format 2; do not
|
|
190
|
+
relabel its envelope as format 1. Traffic and in-flight requests are excluded.
|
|
191
|
+
Snapshot merge replaces supplied mocks and recordings completely while preserving omitted
|
|
192
|
+
mocks. Missing recording arrays mean empty collections. The CLI restores format 2 at startup.
|
|
193
|
+
See the [snapshot contract](README.md#snapshots-of-the-storage).
|
|
194
|
+
|
|
195
|
+
## Python client transport
|
|
196
|
+
|
|
197
|
+
Both `ProxyMock` and `AsyncProxyMock` now use `httpx2` and return its buffered `Response`
|
|
198
|
+
from `execute_request()`. `requests`, `charset-normalizer` and `urllib3` are no longer
|
|
199
|
+
installed by proxy-mock. Early supported httpx2 versions can still depend on `certifi`;
|
|
200
|
+
it is absent from the current lockfile. 2.13 retains `requests.Session` and
|
|
201
|
+
`requests.Response` for the synchronous client.
|
|
202
|
+
|
|
203
|
+
### Response handling and request arguments
|
|
204
|
+
|
|
205
|
+
Replace `.ok` and response truth testing with an explicit status check. `httpx2.Response`
|
|
206
|
+
is always truthy, including HTTP errors; `.is_success` is true only for 200–299. If your
|
|
207
|
+
old `.ok` check accepted redirects, use `response.status_code < 400` instead.
|
|
208
|
+
|
|
209
|
+
Before (2.x sync client):
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
with ProxyMock(url) as client:
|
|
213
|
+
response = client.execute_request("POST", "/upload", data=b"raw", allow_redirects=False)
|
|
214
|
+
assert response.ok
|
|
215
|
+
cookies = response.raw.headers.getlist("Set-Cookie")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
After (3.0 sync client):
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
with ProxyMock(url) as client:
|
|
222
|
+
response = client.execute_request("POST", "/upload", content=b"raw", follow_redirects=False)
|
|
223
|
+
assert response.is_success
|
|
224
|
+
cookies = response.headers.get_list("Set-Cookie")
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The async client accepts the same arguments using `await`. Request options follow the
|
|
228
|
+
native `httpx2` request API: `content=` sends raw bytes or text, `json=` serializes JSON,
|
|
229
|
+
`data=` accepts a form mapping, and `files=` uploads multipart data. Raw text or bytes in
|
|
230
|
+
`data=` raise `TypeError`. Replace `allow_redirects` with `follow_redirects`. `stream=` is
|
|
231
|
+
unsupported: wrapper calls read the complete response. Use the native client's streaming
|
|
232
|
+
API when needed. Use `response.headers.raw` for original header bytes; convenience header
|
|
233
|
+
accessors may decode UTF-8 rather than Latin-1.
|
|
234
|
+
|
|
235
|
+
JSON, forms and multipart encoding can differ from requests. Recording keys include exact
|
|
236
|
+
request bytes: reuse explicit `content=` when byte identity matters, or capture a new
|
|
237
|
+
recording after migrating the request encoding. Generic relative routes append to the
|
|
238
|
+
wrapper host's base path, preserving encoded paths, raw query strings and trailing slashes;
|
|
239
|
+
absolute request URLs override the host.
|
|
240
|
+
|
|
241
|
+
### Defaults and exceptions
|
|
242
|
+
|
|
243
|
+
Clients created by the wrapper do not follow redirects by default. Set `follow_redirects=True`
|
|
244
|
+
per request to follow them. The constructor timeout defaults to 10 seconds for each connect,
|
|
245
|
+
read, write and pool operation, not a total deadline. Pass a number, `httpx2.Timeout` or
|
|
246
|
+
`None` (disable timeouts); a per-request `timeout=` overrides the constructor value.
|
|
247
|
+
Native TLS verification and environment proxy settings remain enabled by default.
|
|
248
|
+
|
|
249
|
+
HTTP errors return a response by default. `execute_request(..., raise_for_status=True)`
|
|
250
|
+
raises `ProxyMockResponseError` for status codes >=400, or `AsyncProxyMockResponseError`
|
|
251
|
+
for async calls; the exception exposes `.response`. Redirects do not raise through this
|
|
252
|
+
flag. Calling the native `response.raise_for_status()` instead raises
|
|
253
|
+
`httpx2.HTTPStatusError` for any non-2xx status, including redirects.
|
|
254
|
+
|
|
255
|
+
All native `httpx2.RequestError` failures are wrapped in `ProxyMockRequestError` or
|
|
256
|
+
`AsyncProxyMockRequestError`, with the original exception in `__cause__`. This includes
|
|
257
|
+
connection, read/write, timeout, protocol, decoding and redirect-limit errors. Native
|
|
258
|
+
`httpx2.InvalidURL` and argument errors such as `TypeError` propagate unchanged. All four
|
|
259
|
+
wrapper exception classes are exported from `proxy_mock.client`; the async error classes
|
|
260
|
+
also subclass the corresponding sync error classes, so shared callers can catch the common
|
|
261
|
+
base classes. Their existing module import locations remain available.
|
|
262
|
+
|
|
263
|
+
### Custom native clients and ownership
|
|
264
|
+
|
|
265
|
+
Replace `.session` access and requests adapters with an `httpx2.Client` or `AsyncClient`.
|
|
266
|
+
Pass it as `http_client=` to preserve configured headers, cookies, authentication, TLS,
|
|
267
|
+
proxy, transport and event hooks. The wrapper exposes it through `.http_client` and always
|
|
268
|
+
uses its own host for relative routes, regardless of the native client's `base_url`.
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
import httpx2
|
|
272
|
+
from proxy_mock.client import ProxyMock
|
|
273
|
+
|
|
274
|
+
with httpx2.Client(headers={"X-Test": "yes"}, trust_env=False) as http:
|
|
275
|
+
with ProxyMock(url, http_client=http, timeout=5.0) as client:
|
|
276
|
+
response = client.execute_request("GET", "/item")
|
|
277
|
+
assert response.is_success
|
|
278
|
+
# The borrowed native client is still open here.
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
For async use, nest `async with httpx2.AsyncClient(...) as http` and
|
|
282
|
+
`async with AsyncProxyMock(url, http_client=http) as client`. The injected client's
|
|
283
|
+
`follow_redirects` default is honored, while the wrapper's constructor timeout applies
|
|
284
|
+
unless overridden per request. A wrapper-created native client is closed by `close()` /
|
|
285
|
+
`aclose()` or context exit, including exceptional exit. An injected client is borrowed:
|
|
286
|
+
the caller closes it. Passing the wrong native client type raises `TypeError`.
|
|
287
|
+
|
|
288
|
+
## Installation and startup
|
|
289
|
+
|
|
290
|
+
The base install, `proxy_mock`, depends only on `httpx2` and `msgpack`. Server source files
|
|
291
|
+
still ship in the same wheel, but FastAPI, Pydantic and uvicorn move to `proxy_mock[server]`.
|
|
292
|
+
Their version bounds are unchanged. Use the extra to run the server, embed `create_app()`,
|
|
293
|
+
validate snapshots with the CLI or start a local instance through pytest fixtures.
|
|
294
|
+
|
|
295
|
+
```sh
|
|
296
|
+
python -m pip install proxy_mock # clients talking to an existing server
|
|
297
|
+
python -m pip install 'proxy_mock[server]' # local server
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
2.13 has no server extra and installs server dependencies by default. For the CLI in an
|
|
301
|
+
isolated environment, use `uvx --from 'proxy_mock[server]' proxy-mock` or
|
|
302
|
+
`pipx run --spec 'proxy_mock[server]' proxy-mock`. Development and tests of proxy-mock itself
|
|
303
|
+
use `uv sync --locked --extra server`.
|
|
304
|
+
|
|
305
|
+
A base-only environment supports both clients, snapshot export/import against a running
|
|
306
|
+
server, pytest auto-loading and fixtures using `PROXY_MOCK_URL`. It does not need server
|
|
307
|
+
packages even if unrelated pytest tests never use the plugin. `proxy-mock --help` and
|
|
308
|
+
`--version` (also through `python -m proxy_mock`) work without the extra. Attempting local
|
|
309
|
+
startup, importing the app factory or using local pytest fixtures gives an installation hint
|
|
310
|
+
for `proxy_mock[server]`. The CLI exits with code 2 without a traceback. Installing an extra
|
|
311
|
+
resolves dependencies; it does not change HTTP routes or choose a different wheel.
|
|
312
|
+
|
|
313
|
+
`make install` includes the extra. When using uv directly, include `--extra server` in both
|
|
314
|
+
`uv sync` and `uv run` commands for server work; plain `uv run` can remove optional packages
|
|
315
|
+
from the project environment. Docker and the server checks in CI select the extra explicitly.
|
|
316
|
+
|
|
317
|
+
Switch existing uvicorn entry points from `proxy_mock.any_catcher:app` to the supported factory
|
|
318
|
+
`uvicorn proxy_mock.app:create_app --factory`, or use the `proxy-mock` console command already
|
|
319
|
+
available in 2.x. Only one worker is supported because storage is in process memory.
|
|
320
|
+
|
|
321
|
+
## Removed response caching
|
|
322
|
+
|
|
323
|
+
`cache_time`, `clean_cache()` and both the legacy and administrative cache resources are
|
|
324
|
+
removed. Passing the field to either client's configure/patch helper raises `TypeError`
|
|
325
|
+
locally, and direct HTTP mock configuration returns `422` even for null or zero. Calling
|
|
326
|
+
`clean_cache()` raises `AttributeError`; `/__admin/cache` returns `404` and remains protected
|
|
327
|
+
from user-mock shadowing. Remove cleanup calls and the field from your tests.
|
|
328
|
+
|
|
329
|
+
Static mock replies stay available until reconfiguration or deletion, and delays apply to
|
|
330
|
+
every request. Ordinary proxying reaches the upstream on each request. Use explicit
|
|
331
|
+
[record/replay](README.md#recordreplay) to capture and serve upstream replies.
|
|
332
|
+
|
|
333
|
+
For migration, snapshot imports accept and discard disabled legacy cache values (`null` or
|
|
334
|
+
integer zero). Enabled or invalid cache values return `422` without changing configuration,
|
|
335
|
+
recordings or cursors. Remove the field from those files or migrate the scenario explicitly;
|
|
336
|
+
import never contacts an upstream to populate recordings. New exports omit the field.
|
|
337
|
+
2.13 retains its caching API and transport behavior.
|
|
338
|
+
|
|
339
|
+
gRPC and an authentication token are outside the 3.0 scope.
|
|
340
|
+
|
|
341
|
+
## Released 2.13 compatibility reference
|
|
342
|
+
|
|
343
|
+
The following sections describe 2.13 only, for projects that stay on 2.x for now.
|
|
344
|
+
|
|
345
|
+
### Stay on 2.x until you are ready
|
|
346
|
+
|
|
347
|
+
Pin `proxy_mock>=2.13,<3` in a test project that needs the existing contract. Upgrade the server
|
|
348
|
+
first within the 2.x line, then the clients. A 2.13 client without `admin_prefix` still uses the
|
|
349
|
+
legacy paths and can talk to an older 2.x server. Keep `requests` behaviour until you have checked your callers.
|
|
350
|
+
Already installed old versions do not acquire warnings remotely: read release notes and update
|
|
351
|
+
to 2.13 to see these notices. There is no network update check or telemetry.
|
|
352
|
+
|
|
353
|
+
### Try the administrative aliases in 2.13
|
|
354
|
+
|
|
355
|
+
Aliases are **opt-in** so that an existing mock at `/__admin` is not unexpectedly shadowed.
|
|
356
|
+
Start the server with:
|
|
357
|
+
|
|
358
|
+
```sh
|
|
359
|
+
PROXY_MOCK_ADMIN_PREFIX=/__admin proxy-mock --host 127.0.0.1 --port 5000
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Use the same prefix explicitly in either client:
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
from proxy_mock.client import ProxyMock, AsyncProxyMock
|
|
366
|
+
|
|
367
|
+
with ProxyMock("http://127.0.0.1:5000", admin_prefix="/__admin") as client:
|
|
368
|
+
client.configure_mock(path="/inventory", body={"available": True})
|
|
369
|
+
assert client.execute_request("GET", "/inventory").json()["available"]
|
|
370
|
+
|
|
371
|
+
# In async code:
|
|
372
|
+
# async with AsyncProxyMock("http://127.0.0.1:5000", admin_prefix="/__admin") as client:
|
|
373
|
+
# await client.get_storage()
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The prefix must be an absolute path of letters, digits, underscores, hyphens and optional
|
|
377
|
+
path segments, without a trailing slash. It must not overlap an existing service path such as
|
|
378
|
+
`/storage`, `/traffic`, `/docs` or their children. A prefix is not authentication. Reserve its
|
|
379
|
+
administrative paths for the server and choose a prefix that does not collide with your mocks.
|
|
380
|
+
Leaving the environment variable unset disables aliases; an explicitly empty value is invalid.
|
|
381
|
+
Generic `execute_request()` calls always use the route you provide, without rewriting mock URLs.
|
|
382
|
+
The pytest fixture continues using the legacy routes in 2.13; construct a client explicitly to
|
|
383
|
+
exercise the aliases.
|
|
384
|
+
|
|
385
|
+
| Legacy operation | Released 2.13 opt-in alias |
|
|
386
|
+
| --- | --- |
|
|
387
|
+
| `GET /proxy_mock` | `GET /__admin` |
|
|
388
|
+
| `POST /configure_mock` | `POST /__admin/mocks` |
|
|
389
|
+
| `PATCH /configure_mock` | `PATCH /__admin/mocks` |
|
|
390
|
+
| `GET /storage` | `GET /__admin/mocks` |
|
|
391
|
+
| `DELETE /storage` | `DELETE /__admin/mocks` |
|
|
392
|
+
| `POST /storage/clean` | `DELETE /__admin/mocks` |
|
|
393
|
+
| `GET /traffic` | `GET /__admin/traffic` |
|
|
394
|
+
| `DELETE /traffic`, `POST /traffic/clean` | `DELETE /__admin/traffic` |
|
|
395
|
+
| `GET /traffic/settings` | `GET /__admin/settings` |
|
|
396
|
+
| `PATCH /traffic/settings`, `POST /traffic/settings` | `PATCH /__admin/settings` |
|
|
397
|
+
| `GET /storage/snapshot` | `GET /__admin/snapshot` |
|
|
398
|
+
| `POST /storage/snapshot` | `POST /__admin/snapshot` |
|
|
399
|
+
| `POST /cache/clean` | No alias; the legacy cache endpoint remains |
|
|
400
|
+
|
|
401
|
+
In 2.13 aliases keep the existing JSON/msgpack bodies, query parameters and status codes.
|
|
402
|
+
`?path=...`, traffic filters and snapshot `?mode=merge|replace` work unchanged. These aliases do not implement the new
|
|
403
|
+
PUT/PATCH contract above. Old paths remain available even when aliases are enabled in 2.13.
|
|
404
|
+
|
|
405
|
+
`clean_storage(path=...)` still calls its legacy endpoint, even with `admin_prefix`, because
|
|
406
|
+
it returns `200` with `success: false` for a missing mock. Switch to `delete_mock(path)`, which
|
|
407
|
+
returns `404` when the mock is missing. `clean_cache()` also stays on its legacy path in 2.13.
|
|
408
|
+
|
|
409
|
+
### Understand the 2.13 warnings
|
|
410
|
+
|
|
411
|
+
Every matched legacy service operation returns `Deprecation: @1789603200` (the announcement
|
|
412
|
+
date, 2026-09-17 UTC) and a `Link` with `rel="deprecation"` pointing to this guide. The date
|
|
413
|
+
syntax follows [RFC 9745](https://www.rfc-editor.org/rfc/rfc9745.html); it replaces the old
|
|
414
|
+
non-standard `Deprecation: true` value. When aliases are enabled, another link with
|
|
415
|
+
`rel="successor-version"` points to the replacement resource. Consult the table for the HTTP
|
|
416
|
+
method: a URI alone does not tell you to change `POST` to `DELETE` or `PATCH`.
|
|
417
|
+
The same notices accompany handled error responses. User mock responses are not marked.
|
|
418
|
+
Aliases themselves have no route-deprecation header. Server warnings are logged once per
|
|
419
|
+
operation per process, rather than for every request. No `Sunset` date is promised yet.
|
|
420
|
+
|
|
421
|
+
Python emits `DeprecationWarning` for the synchronous client's upcoming transport change,
|
|
422
|
+
`cache_time`, `clean_cache()` and `clean_storage(path=...)`. Python's standard warning filters
|
|
423
|
+
control repetition and visibility; use `python -W default::DeprecationWarning -m pytest` to
|
|
424
|
+
review them. Warnings do not change successful responses or retries. Projects treating warnings
|
|
425
|
+
as errors should review these notices before enabling that policy for a dependency upgrade.
|