import os import io import uuid from datetime import date import pytest from fastapi.testclient import TestClient # Ensure required env vars for app import/config os.environ.setdefault("SECRET_KEY", "x" * 32) os.environ.setdefault("DATABASE_URL", "sqlite:////tmp/delphi_test.sqlite") from app.main import app # noqa: E402 from app.auth.security import get_current_user # noqa: E402 from tests.helpers import assert_http_error, assert_validation_error # noqa: E402 class _User: def __init__(self): self.id = 1 self.username = "uploader" self.is_admin = True self.is_active = True @pytest.fixture() def client(): app.dependency_overrides[get_current_user] = lambda: _User() try: yield TestClient(app) finally: app.dependency_overrides.pop(get_current_user, None) def _create_customer_and_file(client: TestClient): customer_id = f"UP-{uuid.uuid4().hex[:8]}" resp = client.post( "/api/customers/", json={"id": customer_id, "last": "Upload", "email": "u@example.com"}, ) assert resp.status_code == 200 file_no = f"U-{uuid.uuid4().hex[:6]}" file_payload = { "file_no": file_no, "id": customer_id, "regarding": "Upload doc test", "empl_num": "E01", "file_type": "CIVIL", "opened": date.today().isoformat(), "status": "ACTIVE", "rate_per_hour": 100.0, } resp = client.post("/api/files/", json=file_payload) assert resp.status_code == 200 return file_no def test_upload_invalid_file_type_returns_400_envelope_and_correlation_header(client: TestClient): file_no = _create_customer_and_file(client) files = { "file": ("bad.txt", b"hello", "text/plain"), } resp = client.post(f"/api/documents/upload/{file_no}", files=files) assert_http_error(resp, 400, "Invalid file type") def test_upload_oversize_file_returns_400_envelope_and_correlation_header(client: TestClient): file_no = _create_customer_and_file(client) # 10MB + 1 byte big_bytes = b"x" * (10 * 1024 * 1024 + 1) files = { "file": ("large.pdf", big_bytes, "application/pdf"), } resp = client.post(f"/api/documents/upload/{file_no}", files=files) assert_http_error(resp, 400, "File too large") def test_upload_uses_incoming_correlation_id_when_provided(client: TestClient): file_no = _create_customer_and_file(client) cid = f"cid-{uuid.uuid4().hex[:8]}" files = { "file": ("bad.txt", b"hello", "text/plain"), } resp = client.post( f"/api/documents/upload/{file_no}", files=files, headers={"X-Correlation-ID": cid}, ) # Envelope shape and message assert_http_error(resp, 400, "Invalid file type") # Echoes our provided correlation id body = resp.json() assert resp.headers.get("X-Correlation-ID") == cid assert body.get("correlation_id") == cid def test_upload_without_file_returns_400_no_file_uploaded(client: TestClient): file_no = _create_customer_and_file(client) # Provide an empty filename to trigger the explicit 400 in route logic files = { # Use a valid filename but zero-byte payload to hit the 400 "No file uploaded" branch "file": ("empty.pdf", io.BytesIO(b""), "application/pdf"), } resp = client.post(f"/api/documents/upload/{file_no}", files=files) assert_http_error(resp, 400, "No file uploaded") def test_upload_missing_file_field_returns_422_validation_envelope(client: TestClient): file_no = _create_customer_and_file(client) # Submit without the required `file` field resp = client.post( f"/api/documents/upload/{file_no}", data={"description": "missing file"}, headers={"X-Correlation-ID": f"cid-{uuid.uuid4().hex[:8]}"}, ) # Ensure 422 envelope and correlation header; details should mention `file` assert_validation_error(resp, "file")