from __future__ import annotations import io import uuid import pytest from fastapi.testclient import TestClient from sqlalchemy.orm import Session from tests.conftest import ( create_test_document, create_test_page, create_test_text_block, create_test_user, get_auth_headers, ) class TestUploadDocument: """Tests for the document upload endpoint.""" def test_upload_pdf_success(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) file_content = b"%PDF-1.4 fake pdf content for testing" response = client.post( "/api/v1/documents/upload", files={"file": ("test.pdf", io.BytesIO(file_content), "application/pdf")}, headers=headers, ) assert response.status_code == 201 data = response.json() assert data["original_filename"] == "test.pdf" assert data["content_type"] == "application/pdf" assert data["status"] == "pending" assert data["file_size"] == len(file_content) assert "id" in data assert "checksum" in data def test_upload_image_success(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) # Minimal valid PNG header png_header = ( b"\x89PNG\r\n\x1a\n" b"\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01" b"\x08\x02\x00\x00\x00\x90wS\xde" ) response = client.post( "/api/v1/documents/upload", files={"file": ("scan.png", io.BytesIO(png_header), "image/png")}, headers=headers, ) assert response.status_code == 201 data = response.json() assert data["content_type"] == "image/png" def test_upload_unsupported_type_rejected(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) response = client.post( "/api/v1/documents/upload", files={"file": ("doc.exe", io.BytesIO(b"malware"), "application/octet-stream")}, headers=headers, ) assert response.status_code in (415, 422) def test_upload_no_file_rejected(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) response = client.post("/api/v1/documents/upload", headers=headers) assert response.status_code == 422 class TestGetDocument: """Tests for getting a document by ID.""" def test_get_document_success(self, client: TestClient, db: Session) -> None: user = create_test_user(db) doc = create_test_document(db, user=user, status="completed") db.flush() headers = get_auth_headers(user) response = client.get(f"/api/v1/documents/{doc.id}", headers=headers) assert response.status_code == 200 data = response.json() assert data["id"] == str(doc.id) assert data["original_filename"] == doc.original_filename assert data["status"] == "completed" def test_get_document_not_found(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) fake_id = uuid.uuid4() response = client.get(f"/api/v1/documents/{fake_id}", headers=headers) assert response.status_code == 404 def test_get_document_invalid_uuid(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) response = client.get("/api/v1/documents/not-a-uuid", headers=headers) assert response.status_code == 422 class TestListDocuments: """Tests for listing documents.""" def test_list_documents_empty(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) response = client.get("/api/v1/documents", headers=headers) assert response.status_code == 200 data = response.json() assert "items" in data assert "total" in data assert "page" in data assert "page_size" in data def test_list_documents_with_data(self, client: TestClient, db: Session) -> None: user = create_test_user(db) create_test_document(db, user=user, filename="doc1.pdf") create_test_document(db, user=user, filename="doc2.pdf") db.flush() headers = get_auth_headers(user) response = client.get("/api/v1/documents", headers=headers) assert response.status_code == 200 data = response.json() assert data["total"] >= 2 assert len(data["items"]) >= 2 def test_list_documents_pagination(self, client: TestClient, db: Session) -> None: user = create_test_user(db) for i in range(5): create_test_document(db, user=user, filename=f"page_doc_{i}.pdf") db.flush() headers = get_auth_headers(user) response = client.get("/api/v1/documents?page=1&page_size=2", headers=headers) assert response.status_code == 200 data = response.json() assert data["page"] == 1 assert data["page_size"] == 2 assert len(data["items"]) <= 2 def test_list_documents_status_filter(self, client: TestClient, db: Session) -> None: user = create_test_user(db) create_test_document(db, user=user, filename="pending.pdf", status="pending") create_test_document(db, user=user, filename="completed.pdf", status="completed") db.flush() headers = get_auth_headers(user) response = client.get("/api/v1/documents?status=completed", headers=headers) assert response.status_code == 200 data = response.json() for item in data["items"]: assert item["status"] == "completed" class TestDeleteDocument: """Tests for deleting a document.""" def test_delete_document_success(self, client: TestClient, db: Session) -> None: user = create_test_user(db) doc = create_test_document(db, user=user) db.flush() headers = get_auth_headers(user) response = client.delete(f"/api/v1/documents/{doc.id}", headers=headers) assert response.status_code == 200 data = response.json() assert "message" in data # Verify document is gone get_response = client.get(f"/api/v1/documents/{doc.id}", headers=headers) assert get_response.status_code == 404 def test_delete_document_not_found(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) fake_id = uuid.uuid4() response = client.delete(f"/api/v1/documents/{fake_id}", headers=headers) assert response.status_code == 404 class TestGetDocumentTemplateMatches: """Tests for document template match retrieval.""" def test_get_matches_empty(self, client: TestClient, db: Session) -> None: user = create_test_user(db) doc = create_test_document(db, user=user, status="completed") db.flush() headers = get_auth_headers(user) response = client.get(f"/api/v1/documents/{doc.id}/template", headers=headers) assert response.status_code == 200 data = response.json() assert isinstance(data, list) assert len(data) == 0 def test_get_matches_document_not_found(self, client: TestClient, db: Session) -> None: user = create_test_user(db) db.flush() headers = get_auth_headers(user) fake_id = uuid.uuid4() response = client.get(f"/api/v1/documents/{fake_id}/template", headers=headers) assert response.status_code == 404