Changes committed
This commit is contained in:
0
docengine/tests/api/__init__.py
Normal file
0
docengine/tests/api/__init__.py
Normal file
0
docengine/tests/api/test_auth.py
Normal file
0
docengine/tests/api/test_auth.py
Normal file
224
docengine/tests/api/test_documents.py
Normal file
224
docengine/tests/api/test_documents.py
Normal file
@@ -0,0 +1,224 @@
|
||||
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
|
||||
0
docengine/tests/api/test_health.py
Normal file
0
docengine/tests/api/test_health.py
Normal file
254
docengine/tests/api/test_templates.py
Normal file
254
docengine/tests/api/test_templates.py
Normal file
@@ -0,0 +1,254 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from tests.conftest import (
|
||||
create_test_document,
|
||||
create_test_fingerprint,
|
||||
create_test_page,
|
||||
create_test_template,
|
||||
create_test_text_block,
|
||||
create_test_user,
|
||||
get_auth_headers,
|
||||
)
|
||||
|
||||
|
||||
class TestListTemplates:
|
||||
"""Tests for listing templates."""
|
||||
|
||||
def test_list_templates_empty(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.get("/api/v1/templates", headers=headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "items" in data
|
||||
assert "total" in data
|
||||
assert "page" in data
|
||||
|
||||
def test_list_templates_with_data(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
create_test_template(db, name="Template_A", created_by=user)
|
||||
create_test_template(db, name="Template_B", created_by=user)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.get("/api/v1/templates", headers=headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 2
|
||||
|
||||
def test_list_templates_pagination(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
for i in range(5):
|
||||
create_test_template(db, name=f"PagTemplate_{i}", created_by=user)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.get("/api/v1/templates?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
|
||||
|
||||
|
||||
class TestGetTemplate:
|
||||
"""Tests for getting a template by ID."""
|
||||
|
||||
def test_get_template_success(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
template = create_test_template(db, name="GetMe", created_by=user)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.get(f"/api/v1/templates/{template.id}", headers=headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["id"] == str(template.id)
|
||||
assert data["name"] == "GetMe"
|
||||
assert data["page_width"] == 612.0
|
||||
assert data["page_height"] == 792.0
|
||||
assert data["is_active"] is True
|
||||
|
||||
def test_get_template_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/templates/{fake_id}", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_get_template_includes_components(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
template = create_test_template(db, name="ComponentTemplate", created_by=user)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.get(f"/api/v1/templates/{template.id}", headers=headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "cells" in data
|
||||
assert "regions" in data
|
||||
assert "table_formats" in data
|
||||
assert "watermarks" in data
|
||||
assert "image_regions" in data
|
||||
|
||||
|
||||
class TestDeleteTemplate:
|
||||
"""Tests for template soft-deletion."""
|
||||
|
||||
def test_delete_template_success(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
template = create_test_template(db, name="DeleteMe", created_by=user)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.delete(f"/api/v1/templates/{template.id}", headers=headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "message" in data
|
||||
|
||||
# Verify template is deactivated (soft-deleted), not hard-deleted
|
||||
get_response = client.get(f"/api/v1/templates/{template.id}", headers=headers)
|
||||
assert get_response.status_code == 200
|
||||
assert get_response.json()["is_active"] is False
|
||||
|
||||
def test_delete_template_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/templates/{fake_id}", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
class TestMatchTemplate:
|
||||
"""Tests for document-to-template matching endpoint."""
|
||||
|
||||
def test_match_document_not_found(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
fake_doc_id = uuid.uuid4()
|
||||
response = client.post(
|
||||
"/api/v1/templates/match",
|
||||
json={"document_id": str(fake_doc_id), "min_confidence": 0.5, "max_results": 5},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_match_document_not_completed(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
doc = create_test_document(db, user=user, status="pending")
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/templates/match",
|
||||
json={"document_id": str(doc.id), "min_confidence": 0.5, "max_results": 5},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_match_completed_document_no_templates(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
doc = create_test_document(db, user=user, status="completed")
|
||||
page = create_test_page(db, doc)
|
||||
create_test_text_block(db, page, text="Content")
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/templates/match",
|
||||
json={"document_id": str(doc.id), "min_confidence": 0.0, "max_results": 5},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
|
||||
|
||||
class TestRenderTemplate:
|
||||
"""Tests for template rendering endpoint."""
|
||||
|
||||
def test_render_template_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.post(
|
||||
"/api/v1/templates/render",
|
||||
json={
|
||||
"template_id": str(fake_id),
|
||||
"data": {},
|
||||
},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_render_inactive_template(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
template = create_test_template(db, name="InactiveRender", created_by=user)
|
||||
template.is_active = False
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/templates/render",
|
||||
json={
|
||||
"template_id": str(template.id),
|
||||
"data": {},
|
||||
},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_render_template_success(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
template = create_test_template(db, name="RenderOK", created_by=user)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/templates/render",
|
||||
json={
|
||||
"template_id": str(template.id),
|
||||
"data": {"field_1": "Hello World"},
|
||||
"output_filename": "test_render.pdf",
|
||||
},
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["filename"] == "test_render.pdf"
|
||||
assert data["file_size"] > 0
|
||||
assert data["page_count"] == template.page_count
|
||||
assert "output_path" in data
|
||||
assert "rendered_at" in data
|
||||
|
||||
|
||||
class TestDownloadRenderedPDF:
|
||||
"""Tests for downloading rendered PDFs."""
|
||||
|
||||
def test_download_not_found(self, client: TestClient, db: Session) -> None:
|
||||
user = create_test_user(db)
|
||||
template = create_test_template(db, name="DLTemplate", created_by=user)
|
||||
db.flush()
|
||||
headers = get_auth_headers(user)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1/templates/{template.id}/download?filename=nonexistent.pdf",
|
||||
headers=headers,
|
||||
)
|
||||
assert response.status_code == 404
|
||||
Reference in New Issue
Block a user