apitally 0.16.2__tar.gz → 0.16.3__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.
- {apitally-0.16.2 → apitally-0.16.3}/.github/workflows/tests.yaml +1 -11
- {apitally-0.16.2 → apitally-0.16.3}/.pre-commit-config.yaml +2 -2
- {apitally-0.16.2 → apitally-0.16.3}/PKG-INFO +4 -4
- {apitally-0.16.2 → apitally-0.16.3}/apitally/starlette.py +37 -10
- {apitally-0.16.2 → apitally-0.16.3}/pyproject.toml +2 -2
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_starlette.py +103 -102
- {apitally-0.16.2 → apitally-0.16.3}/uv.lock +1574 -1574
- {apitally-0.16.2 → apitally-0.16.3}/.github/workflows/publish.yaml +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/.github/workflows/summary.yaml +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/.gitignore +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/LICENSE +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/Makefile +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/README.md +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/__init__.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/blacksheep.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/__init__.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/client_asyncio.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/client_base.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/client_threading.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/consumers.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/logging.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/request_logging.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/requests.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/sentry.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/server_errors.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/client/validation_errors.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/common.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/django.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/django_ninja.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/django_rest_framework.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/fastapi.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/flask.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/litestar.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/apitally/py.typed +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/renovate.json +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/__init__.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/conftest.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/constants.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/django_ninja_urls.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/django_rest_framework_urls.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_blacksheep.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_client_asyncio.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_client_consumers.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_client_request_logging.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_client_requests.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_client_server_errors.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_client_threading.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_client_validation_errors.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_django_ninja.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_django_rest_framework.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_fastapi.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_flask.py +0 -0
- {apitally-0.16.2 → apitally-0.16.3}/tests/test_litestar.py +0 -0
@@ -49,7 +49,7 @@ jobs:
|
|
49
49
|
python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
50
50
|
deps:
|
51
51
|
- fastapi starlette
|
52
|
-
- fastapi==0.
|
52
|
+
- fastapi==0.94.1 starlette
|
53
53
|
- flask
|
54
54
|
- flask==2.3.*
|
55
55
|
- flask==2.0.3 Werkzeug==2.*
|
@@ -66,24 +66,14 @@ jobs:
|
|
66
66
|
exclude:
|
67
67
|
- python: "3.8"
|
68
68
|
deps: blacksheep
|
69
|
-
- python: "3.8"
|
70
|
-
deps: blacksheep==2.2.0
|
71
69
|
- python: "3.8"
|
72
70
|
deps: blacksheep==2.1.0
|
73
|
-
- python: "3.12"
|
74
|
-
deps: fastapi==0.100.1 starlette
|
75
|
-
- python: "3.12"
|
76
|
-
deps: fastapi==0.87.0 starlette
|
77
71
|
- python: "3.12"
|
78
72
|
deps: djangorestframework==3.12.* django==3.2.* uritemplate
|
79
73
|
- python: "3.12"
|
80
74
|
deps: djangorestframework==3.10.* django==2.2.* uritemplate
|
81
75
|
- python: "3.12"
|
82
76
|
deps: litestar==2.0.1
|
83
|
-
- python: "3.13"
|
84
|
-
deps: fastapi==0.100.1 starlette
|
85
|
-
- python: "3.13"
|
86
|
-
deps: fastapi==0.87.0 starlette
|
87
77
|
- python: "3.13"
|
88
78
|
deps: djangorestframework==3.12.* django==3.2.* uritemplate
|
89
79
|
- python: "3.13"
|
@@ -8,12 +8,12 @@ repos:
|
|
8
8
|
- id: trailing-whitespace
|
9
9
|
- id: mixed-line-ending
|
10
10
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
11
|
-
rev: v0.11.
|
11
|
+
rev: v0.11.8
|
12
12
|
hooks:
|
13
13
|
- id: ruff
|
14
14
|
args: ["--fix", "--exit-non-zero-on-fix"]
|
15
15
|
- id: ruff-format
|
16
16
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
17
|
-
rev: 0.
|
17
|
+
rev: 0.7.2
|
18
18
|
hooks:
|
19
19
|
- id: uv-lock
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: apitally
|
3
|
-
Version: 0.16.
|
3
|
+
Version: 0.16.3
|
4
4
|
Summary: Simple API monitoring & analytics for REST APIs built with FastAPI, Flask, Django, Starlette, Litestar and BlackSheep.
|
5
5
|
Project-URL: Homepage, https://apitally.io
|
6
6
|
Project-URL: Documentation, https://docs.apitally.io
|
@@ -49,9 +49,9 @@ Requires-Dist: inflection>=0.5.1; extra == 'django-rest-framework'
|
|
49
49
|
Requires-Dist: requests>=2.26.0; extra == 'django-rest-framework'
|
50
50
|
Requires-Dist: uritemplate>=3.0.0; extra == 'django-rest-framework'
|
51
51
|
Provides-Extra: fastapi
|
52
|
-
Requires-Dist: fastapi>=0.
|
52
|
+
Requires-Dist: fastapi>=0.94.1; extra == 'fastapi'
|
53
53
|
Requires-Dist: httpx>=0.22.0; extra == 'fastapi'
|
54
|
-
Requires-Dist: starlette<1.0.0,>=0.
|
54
|
+
Requires-Dist: starlette<1.0.0,>=0.26.1; extra == 'fastapi'
|
55
55
|
Provides-Extra: flask
|
56
56
|
Requires-Dist: flask>=2.0.0; extra == 'flask'
|
57
57
|
Requires-Dist: requests>=2.26.0; extra == 'flask'
|
@@ -62,7 +62,7 @@ Provides-Extra: sentry
|
|
62
62
|
Requires-Dist: sentry-sdk>=2.2.0; extra == 'sentry'
|
63
63
|
Provides-Extra: starlette
|
64
64
|
Requires-Dist: httpx>=0.22.0; extra == 'starlette'
|
65
|
-
Requires-Dist: starlette<1.0.0,>=0.
|
65
|
+
Requires-Dist: starlette<1.0.0,>=0.26.1; extra == 'starlette'
|
66
66
|
Description-Content-Type: text/markdown
|
67
67
|
|
68
68
|
<p align="center">
|
@@ -1,16 +1,18 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import time
|
4
|
-
from
|
4
|
+
from contextlib import asynccontextmanager
|
5
|
+
from typing import Any, Awaitable, Callable, Dict, List, Optional, Union
|
5
6
|
from warnings import warn
|
6
7
|
|
7
8
|
from httpx import HTTPStatusError, Proxy
|
9
|
+
from starlette.applications import Starlette
|
8
10
|
from starlette.datastructures import Headers
|
9
11
|
from starlette.requests import Request
|
10
12
|
from starlette.routing import BaseRoute, Match, Router
|
11
13
|
from starlette.schemas import EndpointInfo, SchemaGenerator
|
12
14
|
from starlette.testclient import TestClient
|
13
|
-
from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
15
|
+
from starlette.types import ASGIApp, Lifespan, Message, Receive, Scope, Send
|
14
16
|
|
15
17
|
from apitally.client.client_asyncio import ApitallyClient
|
16
18
|
from apitally.client.consumers import Consumer as ApitallyConsumer
|
@@ -56,10 +58,13 @@ class ApitallyMiddleware:
|
|
56
58
|
self.client.request_logger.config.enabled and self.client.request_logger.config.log_response_body
|
57
59
|
)
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
+
_inject_lifespan_handlers(
|
62
|
+
app,
|
63
|
+
on_startup=self.on_startup,
|
64
|
+
on_shutdown=self.client.handle_shutdown,
|
65
|
+
)
|
61
66
|
|
62
|
-
def on_startup(self) -> None:
|
67
|
+
async def on_startup(self) -> None:
|
63
68
|
data = _get_startup_data(self.app, app_version=self.app_version, openapi_url=self.openapi_url)
|
64
69
|
self.client.set_startup_data(data)
|
65
70
|
self.client.start_sync_loop()
|
@@ -289,8 +294,30 @@ def _get_routes(app: Union[ASGIApp, Router]) -> List[BaseRoute]:
|
|
289
294
|
return [] # pragma: no cover
|
290
295
|
|
291
296
|
|
292
|
-
def
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
+
def _inject_lifespan_handlers(
|
298
|
+
app: Union[ASGIApp, Router],
|
299
|
+
on_startup: Callable[[], Awaitable[Any]],
|
300
|
+
on_shutdown: Callable[[], Awaitable[Any]],
|
301
|
+
) -> None:
|
302
|
+
"""
|
303
|
+
Ensures the given startup and shutdown functions are called as part of the app's lifespan context manager.
|
304
|
+
"""
|
305
|
+
router = app
|
306
|
+
while not isinstance(router, Router) and hasattr(router, "app"):
|
307
|
+
router = router.app
|
308
|
+
if not isinstance(router, Router):
|
309
|
+
raise TypeError("app must be a Starlette or Router instance")
|
310
|
+
|
311
|
+
lifespan: Optional[Lifespan] = getattr(router, "lifespan_context", None)
|
312
|
+
|
313
|
+
@asynccontextmanager
|
314
|
+
async def wrapped_lifespan(app: Starlette):
|
315
|
+
await on_startup()
|
316
|
+
if lifespan is not None:
|
317
|
+
async with lifespan(app):
|
318
|
+
yield
|
319
|
+
else:
|
320
|
+
yield
|
321
|
+
await on_shutdown()
|
322
|
+
|
323
|
+
router.lifespan_context = wrapped_lifespan
|
@@ -49,11 +49,11 @@ django_rest_framework = [
|
|
49
49
|
"inflection>=0.5.1", # required for schema generation
|
50
50
|
"requests>=2.26.0",
|
51
51
|
]
|
52
|
-
fastapi = ["fastapi>=0.
|
52
|
+
fastapi = ["fastapi>=0.94.1", "starlette>=0.26.1,<1.0.0", "httpx>=0.22.0"]
|
53
53
|
flask = ["flask>=2.0.0", "requests>=2.26.0"]
|
54
54
|
litestar = ["litestar>=2.0.0", "httpx>=0.22.0"]
|
55
55
|
sentry = ["sentry-sdk>=2.2.0"]
|
56
|
-
starlette = ["starlette>=0.
|
56
|
+
starlette = ["starlette>=0.26.1,<1.0.0", "httpx>=0.22.0"]
|
57
57
|
|
58
58
|
[project.urls]
|
59
59
|
Homepage = "https://apitally.io"
|
@@ -28,6 +28,7 @@ if TYPE_CHECKING:
|
|
28
28
|
async def app(request: FixtureRequest, module_mocker: MockerFixture) -> Starlette:
|
29
29
|
module_mocker.patch("apitally.client.client_asyncio.ApitallyClient._instance", None)
|
30
30
|
module_mocker.patch("apitally.client.client_asyncio.ApitallyClient.start_sync_loop")
|
31
|
+
module_mocker.patch("apitally.client.client_asyncio.ApitallyClient.handle_shutdown")
|
31
32
|
if request.param == "starlette":
|
32
33
|
return get_starlette_app()
|
33
34
|
elif request.param == "fastapi":
|
@@ -182,35 +183,35 @@ def test_middleware_requests_ok(app: Starlette, mocker: MockerFixture):
|
|
182
183
|
from starlette.testclient import TestClient
|
183
184
|
|
184
185
|
mock = mocker.patch("apitally.client.requests.RequestCounter.add_request")
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
186
|
+
|
187
|
+
with TestClient(app) as client:
|
188
|
+
response = client.get("/api/foo")
|
189
|
+
assert response.status_code == 200
|
190
|
+
mock.assert_called_once()
|
191
|
+
assert mock.call_args is not None
|
192
|
+
assert mock.call_args.kwargs["consumer"] == "test"
|
193
|
+
assert mock.call_args.kwargs["method"] == "GET"
|
194
|
+
assert mock.call_args.kwargs["path"] == "/api/foo"
|
195
|
+
assert mock.call_args.kwargs["status_code"] == 200
|
196
|
+
assert mock.call_args.kwargs["response_time"] > 0
|
197
|
+
|
198
|
+
response = client.get("/api/foo/123")
|
199
|
+
assert response.status_code == 200
|
200
|
+
assert mock.call_count == 2
|
201
|
+
assert mock.call_args is not None
|
202
|
+
assert mock.call_args.kwargs["path"] == "/api/foo/{bar}"
|
203
|
+
|
204
|
+
response = client.post("/api/bar")
|
205
|
+
assert response.status_code == 200
|
206
|
+
assert mock.call_count == 3
|
207
|
+
assert mock.call_args is not None
|
208
|
+
assert mock.call_args.kwargs["method"] == "POST"
|
209
|
+
|
210
|
+
response = client.get("/stream")
|
211
|
+
assert response.status_code == 200
|
212
|
+
assert mock.call_count == 4
|
213
|
+
assert mock.call_args is not None
|
214
|
+
assert mock.call_args.kwargs["response_size"] == 6
|
214
215
|
|
215
216
|
|
216
217
|
def test_middleware_requests_error(app: Starlette, mocker: MockerFixture):
|
@@ -218,60 +219,60 @@ def test_middleware_requests_error(app: Starlette, mocker: MockerFixture):
|
|
218
219
|
|
219
220
|
mock1 = mocker.patch("apitally.client.requests.RequestCounter.add_request")
|
220
221
|
mock2 = mocker.patch("apitally.client.server_errors.ServerErrorCounter.add_server_error")
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
222
|
+
|
223
|
+
with TestClient(app, raise_server_exceptions=False) as client:
|
224
|
+
response = client.post("/api/baz")
|
225
|
+
assert response.status_code == 500
|
226
|
+
mock1.assert_called_once()
|
227
|
+
assert mock1.call_args is not None
|
228
|
+
assert mock1.call_args.kwargs["method"] == "POST"
|
229
|
+
assert mock1.call_args.kwargs["path"] == "/api/baz"
|
230
|
+
assert mock1.call_args.kwargs["status_code"] == 500
|
231
|
+
assert mock1.call_args.kwargs["response_time"] > 0
|
232
|
+
|
233
|
+
mock2.assert_called_once()
|
234
|
+
assert mock2.call_args is not None
|
235
|
+
exception = mock2.call_args.kwargs["exception"]
|
236
|
+
assert isinstance(exception, ValueError)
|
237
|
+
|
238
|
+
# Throws a ValueError in a background task, but returns 200
|
239
|
+
response = client.post("/test/task")
|
240
|
+
assert response.status_code == 200
|
241
|
+
assert mock1.call_count == 2
|
242
|
+
assert mock1.call_args is not None
|
243
|
+
assert mock1.call_args.kwargs["status_code"] == 200
|
244
|
+
mock2.assert_called_once() # Not called again
|
244
245
|
|
245
246
|
|
246
247
|
def test_middleware_requests_unhandled(app: Starlette, mocker: MockerFixture):
|
247
248
|
from starlette.testclient import TestClient
|
248
249
|
|
249
250
|
mock = mocker.patch("apitally.client.requests.RequestCounter.add_request")
|
250
|
-
client = TestClient(app)
|
251
251
|
|
252
|
-
|
253
|
-
|
254
|
-
|
252
|
+
with TestClient(app) as client:
|
253
|
+
response = client.post("/xxx")
|
254
|
+
assert response.status_code == 404
|
255
|
+
mock.assert_not_called()
|
255
256
|
|
256
257
|
|
257
258
|
def test_middleware_validation_error(app: Starlette, mocker: MockerFixture):
|
258
259
|
from starlette.testclient import TestClient
|
259
260
|
|
260
261
|
mock = mocker.patch("apitally.client.validation_errors.ValidationErrorCounter.add_validation_errors")
|
261
|
-
client = TestClient(app)
|
262
262
|
|
263
|
-
|
264
|
-
|
265
|
-
|
263
|
+
with TestClient(app) as client:
|
264
|
+
# Validation error as foo must be an integer
|
265
|
+
response = client.get("/api/val?foo=bar")
|
266
|
+
assert response.status_code == 422
|
266
267
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
268
|
+
# FastAPI only
|
269
|
+
if response.headers["Content-Type"] == "application/json":
|
270
|
+
mock.assert_called_once()
|
271
|
+
assert mock.call_args is not None
|
272
|
+
assert mock.call_args.kwargs["method"] == "GET"
|
273
|
+
assert mock.call_args.kwargs["path"] == "/api/val"
|
274
|
+
assert len(mock.call_args.kwargs["detail"]) == 1
|
275
|
+
assert mock.call_args.kwargs["detail"][0]["loc"] == ["query", "foo"]
|
275
276
|
|
276
277
|
|
277
278
|
def test_middleware_request_logging(app: Starlette, mocker: MockerFixture):
|
@@ -280,40 +281,40 @@ def test_middleware_request_logging(app: Starlette, mocker: MockerFixture):
|
|
280
281
|
from apitally.client.request_logging import BODY_TOO_LARGE
|
281
282
|
|
282
283
|
mock = mocker.patch("apitally.client.request_logging.RequestLogger.log_request")
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
284
|
+
|
285
|
+
with TestClient(app) as client:
|
286
|
+
response = client.get("/api/foo/123?foo=bar", headers={"Test-Header": "test"})
|
287
|
+
assert response.status_code == 200
|
288
|
+
mock.assert_called_once()
|
289
|
+
assert mock.call_args is not None
|
290
|
+
assert mock.call_args.kwargs["request"]["method"] == "GET"
|
291
|
+
assert mock.call_args.kwargs["request"]["path"] == "/api/foo/{bar}"
|
292
|
+
assert mock.call_args.kwargs["request"]["url"] == "http://testserver/api/foo/123?foo=bar"
|
293
|
+
assert ("test-header", "test") in mock.call_args.kwargs["request"]["headers"]
|
294
|
+
assert mock.call_args.kwargs["request"]["consumer"] == "test"
|
295
|
+
assert mock.call_args.kwargs["response"]["status_code"] == 200
|
296
|
+
assert mock.call_args.kwargs["response"]["response_time"] > 0
|
297
|
+
assert ("content-type", "text/plain; charset=utf-8") in mock.call_args.kwargs["response"]["headers"]
|
298
|
+
assert mock.call_args.kwargs["response"]["size"] > 0
|
299
|
+
assert mock.call_args.kwargs["response"]["body"] == b"foo: 123"
|
300
|
+
|
301
|
+
response = client.post("/api/bar", content=b"foo")
|
302
|
+
assert response.status_code == 200
|
303
|
+
assert mock.call_count == 2
|
304
|
+
assert mock.call_args is not None
|
305
|
+
assert mock.call_args.kwargs["request"]["method"] == "POST"
|
306
|
+
assert mock.call_args.kwargs["request"]["path"] == "/api/bar"
|
307
|
+
assert mock.call_args.kwargs["request"]["url"] == "http://testserver/api/bar"
|
308
|
+
assert mock.call_args.kwargs["request"]["body"] == b"foo"
|
309
|
+
assert mock.call_args.kwargs["response"]["body"] == b"bar: foo"
|
310
|
+
|
311
|
+
mocker.patch("apitally.starlette.MAX_BODY_SIZE", 2)
|
312
|
+
response = client.post("/api/bar", content=b"foo")
|
313
|
+
assert response.status_code == 200
|
314
|
+
assert mock.call_count == 3
|
315
|
+
assert mock.call_args is not None
|
316
|
+
assert mock.call_args.kwargs["request"]["body"] == BODY_TOO_LARGE
|
317
|
+
assert mock.call_args.kwargs["response"]["body"] == BODY_TOO_LARGE
|
317
318
|
|
318
319
|
|
319
320
|
def test_get_startup_data(app: Starlette, mocker: MockerFixture):
|