"""Teste P1-5 (hardening 2026-07-03): plafon global corp cerere (BodyCapMiddleware).""" from __future__ import annotations import asyncio import io import json import os import tempfile import pytest from fastapi.testclient import TestClient from app.web.body_cap import BodyCapMiddleware @pytest.fixture() def client(monkeypatch): tmp = tempfile.mkdtemp() monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "bc.db")) monkeypatch.setenv("AUTOPASS_LOG_DIR", os.path.join(tmp, "logs")) # Prag mic ca sa nu generam MB-uri reale in test. monkeypatch.setenv("AUTOPASS_MAX_REQUEST_BYTES", "1000") from app.config import get_settings get_settings.cache_clear() from app.main import app with TestClient(app) as c: yield c get_settings.cache_clear() def test_multipart_peste_limita_413(client): data = b"x" * 5000 r = client.post( "/v1/import", files={"file": ("mare.csv", io.BytesIO(data), "text/csv")}, ) assert r.status_code == 413 assert r.json()["cod"] == "CERERE_PREA_MARE" def test_json_peste_limita_413(client): payload = {"blob": "x" * 5000} r = client.post( "/v1/prezentari", content=json.dumps(payload), headers={"content-type": "application/json"}, ) assert r.status_code == 413 assert r.json()["cod"] == "CERERE_PREA_MARE" def test_cerere_normala_sub_limita_trece(client): r = client.get("/healthz") assert r.status_code == 200 def test_content_length_mare_body_mic_413_devreme(client): """Content-Length declarat peste limita respinge inainte sa citeasca body-ul, chiar daca body-ul trimis efectiv e mic (client "mincinos").""" r = client.post( "/v1/prezentari", content=b"{}", headers={"content-type": "application/json", "content-length": "999999"}, ) assert r.status_code == 413 assert r.json()["cod"] == "CERERE_PREA_MARE" async def _dummy_app(scope, receive, send): """App ASGI minimal: consuma tot body-ul (ca un endpoint real), apoi 200.""" more_body = True while more_body: message = await receive() more_body = message.get("more_body", False) await send({"type": "http.response.start", "status": 200, "headers": []}) await send({"type": "http.response.body", "body": b"ok"}) def _run_body_cap(headers, body_chunks): """Ruleaza BodyCapMiddleware direct pe un scope/receive/send ASGI construit manual (TestClient/httpx nu lasa Content-Length malformat/negativ sa treaca la request real).""" scope = {"type": "http", "method": "POST", "path": "/x", "headers": headers} chunks = list(body_chunks) async def receive(): if not chunks: return {"type": "http.request", "body": b"", "more_body": False} body, more = chunks.pop(0) return {"type": "http.request", "body": body, "more_body": more} messages = [] async def send(message): messages.append(message) async def run(): await BodyCapMiddleware(_dummy_app)(scope, receive, send) asyncio.run(run()) return next(m["status"] for m in messages if m["type"] == "http.response.start") def test_content_length_malformat_nu_crapa(monkeypatch): monkeypatch.setenv("AUTOPASS_MAX_REQUEST_BYTES", "1000") from app.config import get_settings get_settings.cache_clear() try: status = _run_body_cap( headers=[(b"content-length", b"abc")], body_chunks=[(b"body mic", False)], ) assert status == 200 finally: get_settings.cache_clear() def test_content_length_negativ_nu_crapa_dar_plafonul_ramane_aplicat(monkeypatch): monkeypatch.setenv("AUTOPASS_MAX_REQUEST_BYTES", "1000") from app.config import get_settings get_settings.cache_clear() try: status = _run_body_cap( headers=[(b"content-length", b"-5")], body_chunks=[(b"x" * 2000, False)], ) assert status == 413 finally: get_settings.cache_clear()