test_content_size_middleware.py
python
sha256:a34090cc4a394a78bd72cbbe34b08cc59525141e19135b6c0ab154f10611b9ef
debug(push/stream): instrument O-frame decode path with INF…
Sonnet 4.6
patch
122 days ago
| 1 | """Tests for ContentSizeLimitMiddleware (checklist 2.3 — body size cap).""" |
| 2 | from __future__ import annotations |
| 3 | |
| 4 | import pytest |
| 5 | from httpx import AsyncClient |
| 6 | |
| 7 | from musehub.middleware.content_size import API_MAX_BYTES, PUSH_MAX_BYTES |
| 8 | |
| 9 | |
| 10 | async def test_api_request_within_limit_passes( |
| 11 | client: AsyncClient, |
| 12 | ) -> None: |
| 13 | """A body under 10 MB on an API route must not be rejected by the middleware.""" |
| 14 | small_body = b"x" * 1024 # 1 KB |
| 15 | resp = await client.post( |
| 16 | "/api/repos", |
| 17 | content=small_body, |
| 18 | headers={"Content-Type": "application/json", "Content-Length": str(len(small_body))}, |
| 19 | ) |
| 20 | # May be 422 (invalid JSON) or 401 (auth required) — not 413 |
| 21 | assert resp.status_code != 413 |
| 22 | |
| 23 | |
| 24 | async def test_api_request_over_limit_returns_413( |
| 25 | client: AsyncClient, |
| 26 | ) -> None: |
| 27 | """Content-Length over 10 MB on a non-push route must return 413.""" |
| 28 | over_limit = API_MAX_BYTES + 1 |
| 29 | resp = await client.post( |
| 30 | "/api/repos", |
| 31 | headers={"Content-Length": str(over_limit)}, |
| 32 | content=b"", # actual body irrelevant — Content-Length header drives the check |
| 33 | ) |
| 34 | assert resp.status_code == 413 |
| 35 | assert "too large" in resp.json()["detail"].lower() |
| 36 | |
| 37 | |
| 38 | async def test_push_request_under_push_limit_not_rejected( |
| 39 | client: AsyncClient, |
| 40 | db_session: AsyncSession, |
| 41 | ) -> None: |
| 42 | """Content-Length under 500 MB on a push route must not be rejected by the middleware.""" |
| 43 | from tests.factories import create_repo |
| 44 | repo = await create_repo(db_session, slug="size-check-repo", owner="size-user") |
| 45 | import msgpack |
| 46 | body = msgpack.packb( |
| 47 | {"bundle": {"commits": [], "snapshots": [], "objects": []}, "branch": "main"}, |
| 48 | use_bin_type=True, |
| 49 | ) |
| 50 | # No auth → will get 401, but that's AFTER the size check — must not be 413 |
| 51 | resp = await client.post( |
| 52 | f"/{repo.owner}/{repo.slug}/push", |
| 53 | content=body, |
| 54 | headers={"Content-Type": "application/x-msgpack"}, |
| 55 | ) |
| 56 | assert resp.status_code != 413 |
| 57 | |
| 58 | |
| 59 | async def test_push_request_over_api_limit_but_under_push_limit_allowed( |
| 60 | client: AsyncClient, |
| 61 | ) -> None: |
| 62 | """A 20 MB Content-Length on the push path must NOT return 413 (push limit is 500 MB).""" |
| 63 | over_api_limit = API_MAX_BYTES * 2 # 20 MB — over API limit but under push limit |
| 64 | resp = await client.post( |
| 65 | "/gabriel/my-repo/push", |
| 66 | headers={"Content-Length": str(over_api_limit)}, |
| 67 | content=b"", |
| 68 | ) |
| 69 | # Should get 401 (no auth) or 422 (bad body) — not 413 |
| 70 | assert resp.status_code != 413 |
| 71 | |
| 72 | |
| 73 | async def test_push_objects_path_uses_push_limit( |
| 74 | client: AsyncClient, |
| 75 | ) -> None: |
| 76 | """/{owner}/{slug}/push/objects must also use the 500 MB limit, not 10 MB.""" |
| 77 | over_api_limit = API_MAX_BYTES + 1 |
| 78 | resp = await client.post( |
| 79 | "/gabriel/my-repo/push/objects", |
| 80 | headers={"Content-Length": str(over_api_limit)}, |
| 81 | content=b"", |
| 82 | ) |
| 83 | assert resp.status_code != 413 |
| 84 | |
| 85 | |
| 86 | def test_limits_are_correct_values() -> None: |
| 87 | assert API_MAX_BYTES == 10 * 1024 * 1024 |
| 88 | assert PUSH_MAX_BYTES == 500 * 1024 * 1024 |
File History
1 commit
sha256:a34090cc4a394a78bd72cbbe34b08cc59525141e19135b6c0ab154f10611b9ef
debug(push/stream): instrument O-frame decode path with INF…
Sonnet 4.6
patch
122 days ago