Spaces:
Runtime error
Runtime error
| """Tests for upload-time redaction strategy wiring.""" | |
| from __future__ import annotations | |
| import io | |
| import json | |
| import pytest | |
| from httpx import AsyncClient | |
| from sqlalchemy.ext.asyncio import AsyncSession | |
| from app.db.models import Document, IngestStatus | |
| from app.services.document_sanitiser import RedactionStrategy | |
| from app.services.redaction_upload import ( | |
| InvalidRedactionStrategyError, | |
| build_upload_db_context, | |
| deserialize_db_context, | |
| parse_redaction_strategy, | |
| serialize_db_context, | |
| ) | |
| def test_parse_redaction_strategy_defaults_and_aliases() -> None: | |
| assert parse_redaction_strategy(None) is RedactionStrategy.AI_HYBRID | |
| assert parse_redaction_strategy("") is RedactionStrategy.AI_HYBRID | |
| assert parse_redaction_strategy("AI_HYBRID") is RedactionStrategy.AI_HYBRID | |
| assert parse_redaction_strategy("deterministic") is RedactionStrategy.DETERMINISTIC_CODE | |
| assert parse_redaction_strategy("DETERMINISTIC_CODE") is RedactionStrategy.DETERMINISTIC_CODE | |
| def test_parse_redaction_strategy_rejects_unknown() -> None: | |
| with pytest.raises(InvalidRedactionStrategyError): | |
| parse_redaction_strategy("magic_redact") | |
| def test_serialize_deserialize_db_context_roundtrip() -> None: | |
| ctx = {"client_name": "Jane Doe", "property_address": "10 High Street"} | |
| raw = serialize_db_context(ctx) | |
| assert raw is not None | |
| assert deserialize_db_context(raw) == ctx | |
| async def test_build_upload_db_context_prefers_form_over_tenant_backfill( | |
| test_db: AsyncSession, | |
| ) -> None: | |
| ctx = await build_upload_db_context( | |
| test_db, | |
| "tenant_test", | |
| client_name="Explicit Client", | |
| property_address="99 Form Street", | |
| surveyor_name="Form Surveyor", | |
| ) | |
| assert ctx["client_name"] == "Explicit Client" | |
| assert ctx["property_address"] == "99 Form Street" | |
| assert ctx["surveyor_name"] == "Form Surveyor" | |
| def _minimal_docx_bytes() -> bytes: | |
| from docx import Document | |
| buf = io.BytesIO() | |
| doc = Document() | |
| doc.add_paragraph("Upload redaction strategy fixture.") | |
| doc.save(buf) | |
| return buf.getvalue() | |
| def noop_ingest(monkeypatch: pytest.MonkeyPatch) -> None: | |
| async def _noop(doc_id: str, file_path) -> None: # noqa: ANN001, ARG001 | |
| return None | |
| monkeypatch.setattr("app.ingest.pipeline.ingest_document", _noop) | |
| async def test_upload_accepts_deterministic_strategy( | |
| async_client: AsyncClient, | |
| test_db: AsyncSession, | |
| noop_ingest: None, | |
| ) -> None: | |
| files = [ | |
| ( | |
| "file", | |
| ( | |
| "report.docx", | |
| _minimal_docx_bytes(), | |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.document", | |
| ), | |
| ) | |
| ] | |
| data = { | |
| "redaction_strategy": "deterministic", | |
| "client_name": "Jane Client", | |
| "property_address": "1 Test Lane", | |
| } | |
| res = await async_client.post("/upload", files=files, data=data) | |
| assert res.status_code == 202 | |
| body = res.json() | |
| assert body["redaction_strategy"] == "deterministic" | |
| doc = await test_db.get(Document, body["document_id"]) | |
| assert doc is not None | |
| assert doc.redaction_strategy == "deterministic" | |
| assert json.loads(doc.redaction_context_json or "{}")["client_name"] == "Jane Client" | |
| async def test_upload_rejects_invalid_redaction_strategy( | |
| async_client: AsyncClient, | |
| test_db: AsyncSession, | |
| noop_ingest: None, | |
| ) -> None: | |
| files = [ | |
| ( | |
| "file", | |
| ( | |
| "report.docx", | |
| _minimal_docx_bytes(), | |
| "application/vnd.openxmlformats-officedocument.wordprocessingml.document", | |
| ), | |
| ) | |
| ] | |
| res = await async_client.post( | |
| "/upload", | |
| files=files, | |
| data={"redaction_strategy": "not_a_real_strategy"}, | |
| ) | |
| assert res.status_code == 400 | |
| assert "Invalid redaction_strategy" in res.json()["detail"] | |
| def test_ingest_sanitisation_gate_for_deterministic_strategy() -> None: | |
| """Explicit deterministic uploads must always enter the sanitiser path.""" | |
| from app.db.models import DocumentPurpose | |
| from app.services.document_sanitiser import RedactionStrategy | |
| strategy = RedactionStrategy.DETERMINISTIC_CODE | |
| doc_purpose = DocumentPurpose.report_source | |
| needs_sanitise = ( | |
| doc_purpose == DocumentPurpose.style_corpus | |
| or strategy is RedactionStrategy.DETERMINISTIC_CODE | |
| ) | |
| assert needs_sanitise is True | |