151 lines
5 KiB
Python
151 lines
5 KiB
Python
"""Tests for API schemas — camelCase serialization and validation."""
|
|
|
|
import pytest
|
|
|
|
from api.schemas import (
|
|
AnalysisResponse,
|
|
CreateAnalysisRequest,
|
|
DocumentResponse,
|
|
PipelineOptionsRequest,
|
|
_to_camel,
|
|
)
|
|
|
|
|
|
class TestToCamel:
|
|
def test_single_word(self):
|
|
assert _to_camel("status") == "status"
|
|
|
|
def test_two_words(self):
|
|
assert _to_camel("document_id") == "documentId"
|
|
|
|
def test_three_words(self):
|
|
assert _to_camel("content_html_raw") == "contentHtmlRaw"
|
|
|
|
def test_already_camel(self):
|
|
assert _to_camel("documentId") == "documentId"
|
|
|
|
|
|
class TestDocumentResponse:
|
|
def test_serializes_to_camel_case(self):
|
|
doc = DocumentResponse(
|
|
id="1",
|
|
filename="test.pdf",
|
|
content_type="application/pdf",
|
|
file_size=1024,
|
|
page_count=5,
|
|
created_at="2024-01-01",
|
|
)
|
|
data = doc.model_dump(by_alias=True)
|
|
assert "contentType" in data
|
|
assert "fileSize" in data
|
|
assert "pageCount" in data
|
|
assert "createdAt" in data
|
|
# Original snake_case should not appear
|
|
assert "content_type" not in data
|
|
assert "file_size" not in data
|
|
|
|
def test_optional_fields_default_to_none(self):
|
|
doc = DocumentResponse(id="1", filename="test.pdf", created_at="2024-01-01")
|
|
assert doc.content_type is None
|
|
assert doc.file_size is None
|
|
assert doc.page_count is None
|
|
|
|
|
|
class TestAnalysisResponse:
|
|
def test_serializes_to_camel_case(self):
|
|
resp = AnalysisResponse(
|
|
id="1",
|
|
document_id="d1",
|
|
status="COMPLETED",
|
|
content_markdown="# Hello",
|
|
pages_json="[]",
|
|
created_at="2024-01-01",
|
|
)
|
|
data = resp.model_dump(by_alias=True)
|
|
assert "documentId" in data
|
|
assert "contentMarkdown" in data
|
|
assert "pagesJson" in data
|
|
assert "errorMessage" in data
|
|
assert "startedAt" in data
|
|
assert "completedAt" in data
|
|
|
|
def test_populate_by_name(self):
|
|
"""Can create using snake_case field names."""
|
|
resp = AnalysisResponse(
|
|
id="1",
|
|
document_id="d1",
|
|
status="PENDING",
|
|
created_at="2024-01-01",
|
|
)
|
|
assert resp.document_id == "d1"
|
|
|
|
|
|
class TestPipelineOptionsRequest:
|
|
def test_defaults(self):
|
|
opts = PipelineOptionsRequest()
|
|
assert opts.do_ocr is True
|
|
assert opts.do_table_structure is True
|
|
assert opts.table_mode == "accurate"
|
|
assert opts.do_code_enrichment is False
|
|
assert opts.do_formula_enrichment is False
|
|
assert opts.do_picture_classification is False
|
|
assert opts.do_picture_description is False
|
|
assert opts.generate_picture_images is False
|
|
assert opts.generate_page_images is False
|
|
assert opts.images_scale == 1.0
|
|
|
|
def test_custom_values(self):
|
|
opts = PipelineOptionsRequest(
|
|
do_ocr=False,
|
|
table_mode="fast",
|
|
do_code_enrichment=True,
|
|
images_scale=2.0,
|
|
)
|
|
assert opts.do_ocr is False
|
|
assert opts.table_mode == "fast"
|
|
assert opts.do_code_enrichment is True
|
|
assert opts.images_scale == 2.0
|
|
|
|
def test_model_dump(self):
|
|
opts = PipelineOptionsRequest(do_ocr=False)
|
|
data = opts.model_dump()
|
|
assert data["do_ocr"] is False
|
|
assert data["do_table_structure"] is True # default preserved
|
|
|
|
def test_invalid_table_mode_rejected(self):
|
|
with pytest.raises(ValueError, match='table_mode must be "accurate" or "fast"'):
|
|
PipelineOptionsRequest(table_mode="invalid")
|
|
|
|
def test_negative_images_scale_rejected(self):
|
|
with pytest.raises(ValueError, match="images_scale must be between"):
|
|
PipelineOptionsRequest(images_scale=-1.0)
|
|
|
|
def test_zero_images_scale_rejected(self):
|
|
with pytest.raises(ValueError, match="images_scale must be between"):
|
|
PipelineOptionsRequest(images_scale=0)
|
|
|
|
def test_excessive_images_scale_rejected(self):
|
|
with pytest.raises(ValueError, match="images_scale must be between"):
|
|
PipelineOptionsRequest(images_scale=11.0)
|
|
|
|
def test_boundary_images_scale_accepted(self):
|
|
opts = PipelineOptionsRequest(images_scale=0.1)
|
|
assert opts.images_scale == 0.1
|
|
opts2 = PipelineOptionsRequest(images_scale=10.0)
|
|
assert opts2.images_scale == 10.0
|
|
|
|
|
|
class TestCreateAnalysisRequest:
|
|
def test_parses_document_id(self):
|
|
req = CreateAnalysisRequest(documentId="doc-42")
|
|
assert req.documentId == "doc-42"
|
|
assert req.pipelineOptions is None
|
|
|
|
def test_parses_with_pipeline_options(self):
|
|
req = CreateAnalysisRequest(
|
|
documentId="doc-42",
|
|
pipelineOptions=PipelineOptionsRequest(do_ocr=False, table_mode="fast"),
|
|
)
|
|
assert req.documentId == "doc-42"
|
|
assert req.pipelineOptions.do_ocr is False
|
|
assert req.pipelineOptions.table_mode == "fast"
|