VirtForemanBackend / tests /test_uploads_pipeline.py
Dirbol's picture
feat(uploads): D-light blueprint pipeline (Phase-2, no OCR)
e10629e
Raw
History Blame Contribute Delete
3.49 kB
"""End-to-end smoke test for the D-light upload pipeline.
Stays local: drives the FastAPI app via Starlette TestClient and exercises
the unit-level pipeline helpers. Skips the LLM `extract-text` body so we
don't need a live NVIDIA key β€” the offline fallback is what we want here.
"""
from __future__ import annotations
import io
from pathlib import Path
from fastapi.testclient import TestClient
from app.services.proj_doc_pipeline import (
IngestArtifact,
collect_text_window,
extract_pages_text,
stamp_index,
)
TEST_PDF = Path(__file__).parent / "test.pdf"
def test_pipeline_helpers_text_extraction():
"""Pure pipeline layer: native text + stamp heuristic on tests/test.pdf."""
if not TEST_PDF.exists():
# No fixture PDF β€” skip; the assertion below is meant to be smoke.
return
raw = TEST_PDF.read_bytes()
artifact = extract_pages_text(raw)
assert artifact.sha256, "sha256 must be present"
assert artifact.page_count >= 1, "at least one page expected"
assert isinstance(artifact.pages, list)
# Stamp index is keyed on label incl. "(стр. N)" suffix.
stamps = stamp_index(artifact.pages)
assert isinstance(stamps, dict)
# Window helper must return a non-empty string for any page.
if artifact.pages:
window = collect_text_window(
artifact.pages, around=0, span=2
)
assert window and isinstance(window, str)
def test_pipeline_artifact_default_empty_text():
"""Direct call against the dataclass β€” no IO."""
artifact = IngestArtifact(
file_name="x.pdf",
mime_type="application/pdf",
sha256="0" * 64,
page_count=0,
)
assert artifact.full_text(1) == ""
assert artifact.has_native_text is False
def test_uploads_blueprint_endpoint_accepts_pdf(minimal_app_client: TestClient):
"""POST /uploads/blueprint β†’ 200, page list non-empty (no Supabase)."""
if not TEST_PDF.exists():
return
client = minimal_app_client
files = {"file": ("test.pdf", TEST_PDF.read_bytes(), "application/pdf")}
resp = client.post("/uploads/blueprint", files=files)
assert resp.status_code == 200, resp.text
body = resp.json()
assert body["file_name"] == "test.pdf"
assert body["page_count"] >= 1
assert isinstance(body["pages"], list)
assert body["stored_in_supabase"] is False # no SB creds in tests
status = client.get(
f"/uploads/blueprint/{body['upload_id']}"
)
assert status.status_code in (200, 404), status.text
# If Supabase isn't reachable we fall back to the on-disk cache β†’ 200.
if status.status_code == 200:
s = status.json()
assert s["upload_id"] == body["upload_id"]
assert s["page_count"] == body["page_count"]
def test_uploads_blueprint_rejects_oversize(minimal_app_client: TestClient):
client = minimal_app_client
payload = b"x" * (51 * 1024 * 1024) # 51 MB > MAX_BYTES=50MB
files = {
"file": ("huge.pdf", io.BytesIO(payload), "application/pdf")
}
resp = client.post("/uploads/blueprint", files=files)
assert resp.status_code == 413
assert "too large" in resp.json()["detail"]
def test_uploads_blueprint_rejects_unsupported_mime(minimal_app_client: TestClient):
client = minimal_app_client
files = {
"file": ("odd.avi", b"abcd", "video/x-msvideo")
}
resp = client.post("/uploads/blueprint", files=files)
assert resp.status_code == 415