Spaces:
Runtime error
Runtime error
File size: 5,356 Bytes
e561e67 aad7814 e561e67 aad7814 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | """Tests for RAG upload sanitisation (regex path; no live OpenAI)."""
from __future__ import annotations
import pytest
from app.services.document_sanitiser import (
RedactionStrategy,
_deterministic_redact,
regex_sanitise_text,
sanitise_text_for_rag_sync,
should_sanitise_for_rag,
)
def test_should_skip_kb_tenant(monkeypatch: pytest.MonkeyPatch) -> None:
from app.config import settings
monkeypatch.setattr(settings, "enable_rag_upload_sanitisation", True)
monkeypatch.setattr(settings, "rag_sanitisation_skip_kb_tenant", True)
monkeypatch.setattr(settings, "knowledge_base_tenant_id", "__rics_kb__")
assert should_sanitise_for_rag("tenant-a") is True
assert should_sanitise_for_rag("__rics_kb__") is False
def test_regex_removes_postcode_and_email() -> None:
raw = (
"The roof covering is slate. Client: jane.doe@example.com. "
"Property at 12 High Street, London SW1A 1AA. Price £450,000."
)
out = regex_sanitise_text(raw)
assert "jane.doe@example.com" not in out
assert "SW1A" not in out
assert "450,000" not in out
assert "slate" in out.lower()
def test_sanitise_sync_without_openai_key(monkeypatch: pytest.MonkeyPatch) -> None:
from app.config import settings
monkeypatch.setattr(settings, "enable_rag_upload_sanitisation", True)
monkeypatch.setattr(settings, "openai_api_key", "")
monkeypatch.setattr(settings, "rag_sanitisation_fail_closed", False)
text = "Defect noted at 10 Church Road, Bristol BS1 4ST."
out = sanitise_text_for_rag_sync(text, tenant_id="firm-1")
assert "BS1" not in out
assert "defect" in out.lower()
def test_regex_preserves_proptech_vocabulary() -> None:
raw = (
"Timber purlins to the left rear elevation; lead flashing and uPVC gutters. "
"Certified under FENSA and Building Regulations."
)
out = regex_sanitise_text(raw)
assert "timber" in out.lower()
assert "fensa" in out.lower()
assert "building regulations" in out.lower()
def test_system_prompt_includes_descriptive_intent_and_antipatterns() -> None:
from app.services.document_sanitiser import RAG_UPLOAD_SANITISATION_SYSTEM_PROMPT
assert "DESCRIPTIVE INTENT" in RAG_UPLOAD_SANITISATION_SYSTEM_PROMPT
assert "ANTI-PATTERNS" in RAG_UPLOAD_SANITISATION_SYSTEM_PROMPT
assert "timber purlins" in RAG_UPLOAD_SANITISATION_SYSTEM_PROMPT
assert "downstream deterministic pass" in RAG_UPLOAD_SANITISATION_SYSTEM_PROMPT.lower()
assert "<input_document>" not in RAG_UPLOAD_SANITISATION_SYSTEM_PROMPT
def test_deterministic_redacts_regex_pii_and_preserves_vocabulary() -> None:
raw = (
"Timber purlins to the left rear elevation; email surveyor@firm.co.uk, "
"call 07911 123456, property postcode SW1A 1AA."
)
out = _deterministic_redact(raw)
assert "surveyor@firm.co.uk" not in out
assert "07911 123456" not in out
assert "SW1A 1AA" not in out
assert "[REDACTED]" in out
# Survey vocabulary survives the deterministic pass.
for term in ("timber", "purlins", "left rear elevation"):
assert term in out.lower()
def test_deterministic_db_context_exact_wipe() -> None:
raw = "Prepared for Jonathan Pemberton at the Maple Lodge instruction."
out = _deterministic_redact(
raw, db_context={"client": "Jonathan Pemberton", "property": "Maple Lodge"}
)
assert "Jonathan Pemberton" not in out
assert "Maple Lodge" not in out
assert out.lower().count("[redacted]") >= 2
def test_deterministic_db_context_none_is_graceful() -> None:
raw = "Roof covering is natural slate in satisfactory condition."
out = _deterministic_redact(raw, db_context=None)
assert out == raw # no regex PII, no context → unchanged
def test_deterministic_empty_input_raises() -> None:
with pytest.raises(ValueError):
_deterministic_redact(" ")
def test_deterministic_strategy_runs_without_network(monkeypatch: pytest.MonkeyPatch) -> None:
"""DETERMINISTIC_CODE must not touch the LLM even with a key set + policy off."""
from app.config import settings
from app.services import document_sanitiser
monkeypatch.setattr(settings, "enable_rag_upload_sanitisation", False)
monkeypatch.setattr(settings, "openai_api_key", "sk-should-not-be-used")
def _boom(*args, **kwargs): # pragma: no cover - must never run
raise AssertionError("LLM path invoked on deterministic strategy")
monkeypatch.setattr(document_sanitiser, "_llm_sanitise_chunk_sync", _boom)
out = sanitise_text_for_rag_sync(
"Contact a@b.com about 12 Acacia Road SW1A 1AA.",
tenant_id="firm-1",
strategy=RedactionStrategy.DETERMINISTIC_CODE,
db_context={"agent": "a@b.com"},
)
assert "a@b.com" not in out
assert "SW1A 1AA" not in out
def test_default_strategy_is_ai_hybrid_and_policy_gate_preserved(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Existing callers (no strategy arg) keep AI_HYBRID behaviour: policy off → passthrough."""
from app.config import settings
monkeypatch.setattr(settings, "enable_rag_upload_sanitisation", False)
text = "Email keep@me.com — policy is off so AI path returns text unchanged."
out = sanitise_text_for_rag_sync(text, tenant_id="firm-1")
assert out == text.strip()
|