225 lines
7.8 KiB
Python
225 lines
7.8 KiB
Python
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
|