"""Legacy UI compatibility routes backed by v2 generation.""" from __future__ import annotations import io import zipfile from pathlib import Path import pytest from docx import Document from fastapi.testclient import TestClient from backend.config import settings from backend.core import ingest, template_discoverer from backend.main import app @pytest.fixture def client(): return TestClient(app) def _seed_tenant(tmp_path: Path, monkeypatch, tenant: str = "legacy-ui") -> None: monkeypatch.setattr(settings, "data_dir", str(tmp_path / "data")) monkeypatch.setattr(settings, "openai_api_key", "") tpl = tmp_path / "template.docx" doc = Document() doc.add_paragraph("D2 Roof coverings") doc.add_paragraph("D1 Chimney stacks") doc.save(str(tpl)) past = tmp_path / "past.docx" doc2 = Document() doc2.add_paragraph("D2 Roof coverings") doc2.add_paragraph( "The roof is covered with concrete interlocking tiles laid to pitched timber rafters." ) doc2.add_paragraph("D1 Chimney stacks") doc2.add_paragraph("Chimney pots appear serviceable from ground level.") doc2.save(str(past)) ingest.ingest_report_template(tenant, tpl) ingest.ingest_reference(tenant, past) def test_legacy_auth_passphrase_and_catalog(tmp_path, monkeypatch, client): tenant = "legacy-auth" _seed_tenant(tmp_path, monkeypatch, tenant) r = client.post("/auth/register", json={"tenant_id": tenant, "passphrase": "secret123"}) assert r.status_code == 201 body = r.json() assert body["access_token"] assert body["tenant_id"] == tenant token = body["access_token"] r = client.get("/auth/me", headers={"Authorization": f"Bearer {token}"}) assert r.status_code == 200 r = client.get("/templates/catalog?survey_level=3", headers={"Authorization": f"Bearer {token}"}) assert r.status_code == 200 cat = r.json() assert any(s["code"] == "D2" for s in cat["sections"]) def test_legacy_upload_to_export_flow(tmp_path, monkeypatch, client): tenant = "legacy-flow" _seed_tenant(tmp_path, monkeypatch, tenant) r = client.post("/auth/register", json={"tenant_id": tenant, "passphrase": "secret123"}) token = r.json()["access_token"] headers = {"Authorization": f"Bearer {token}"} past = tmp_path / "ref.docx" doc = Document() doc.add_paragraph("D2 Roof coverings") doc.add_paragraph("The roof comprises slate tiles to pitched rafters.") doc.save(str(past)) with past.open("rb") as fh: r = client.post( "/upload/batch", headers=headers, files=[("files", ("ref.docx", fh, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"))], ) assert r.status_code == 201 up = r.json() assert up["accepted"] == 1 doc_id = up["items"][0]["document_id"] r = client.post("/documents/batch-status", headers=headers, json={"document_ids": [doc_id]}) assert r.json()["complete"] == 1 r = client.get("/documents/tenant-chunk-summary", headers=headers) assert r.json()["indexed_chunk_count"] > 0 r = client.post(f"/reports?document_id={doc_id}&survey_level=3", headers=headers) assert r.status_code == 200 report_id = r.json()["report_id"] r = client.get(f"/reports/{report_id}/photo-policy", headers=headers) assert r.status_code == 200 r = client.post( f"/reports/{report_id}/generate", headers=headers, json={ "template_id": "D2", "bullets": ["slate tile slipped south slope"], "mode": "generate", "interference_level": "minimum", }, ) assert r.status_code == 200 for _ in range(60): st = client.get(f"/reports/{report_id}/status", headers=headers).json() if st["status"] in ("complete", "partial", "failed"): break import time time.sleep(0.1) assert st["status"] == "complete" r = client.get(f"/reports/{report_id}/sections", headers=headers) sections = r.json()["sections"] assert "D2" in sections assert sections["D2"]["text"] assert sections["D2"]["provenance"] r = client.get(f"/reports/{report_id}/export?format=docx", headers=headers) assert r.status_code == 200 with zipfile.ZipFile(io.BytesIO(r.content)) as zf: xml = zf.read("word/document.xml").decode("utf-8") assert "slate" in xml.lower() or "roof" in xml.lower() prov = sections["D2"]["provenance"] source_name = prov[0].get("filename") or prov[0].get("doc_id") or "" if source_name.startswith("reference:"): source_name = source_name.split(":", 1)[-1] assert source_name in xml assert "Source:" in xml def test_serves_legacy_index_html(client): r = client.get("/") assert r.status_code == 200 assert "Report Genius" in r.text def test_extract_notes_txt(client): r = client.post( "/extract-notes", files=[("file", ("notes.txt", b"E2: slate tile slipped\nD1: chimney pots ok", "text/plain"))], ) assert r.status_code == 200 body = r.json() assert body["line_count"] >= 2 assert any("slate" in ln.lower() for ln in body["lines"]) def test_route_notes_screenshot_cases(client): r = client.post( "/route-notes", json={ "lines": [ "Main roof: Pitched valley-style roof with imitation slate covering. " "Some tiles have slipped. Valley gutters appear to be in good condition.", "Rainwater fittings: UPVC gutters and gullies present. Rainwater downpipe discharges below ground.", ] }, ) assert r.status_code == 200 body = r.json() assert body["routed_line_count"] == 2 assert any("Main roof" in ln for ln in body["routed"].get("D2", [])) assert any("Rainwater fittings" in ln for ln in body["routed"].get("D3", [])) assert "D4" not in body["routed"] def test_document_delete_and_similarity(tmp_path, monkeypatch, client): tenant = "legacy-docs" _seed_tenant(tmp_path, monkeypatch, tenant) r = client.post("/auth/register", json={"tenant_id": tenant, "passphrase": "secret123"}) token = r.json()["access_token"] headers = {"Authorization": f"Bearer {token}"} past = tmp_path / "ref2.docx" doc = Document() doc.add_paragraph("D2 Roof coverings") doc.add_paragraph("Concrete tile roof in fair condition with minor moss.") doc.save(str(past)) with past.open("rb") as fh: r = client.post( "/upload/batch", headers=headers, files=[("files", ("ref2.docx", fh, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"))], ) doc_id = r.json()["items"][0]["document_id"] r = client.post( "/content/similar", headers=headers, json={ "text": "concrete tile roof moss growth south elevation", "section_code": "D2", "peer_sections": {}, "limit": 5, "min_relevance_percent": 10, }, ) assert r.status_code == 200 sim = r.json() assert "library_matches" in sim r = client.delete(f"/documents/{doc_id}", headers=headers) assert r.status_code == 200 assert r.json()["deleted"] is True r = client.get("/documents", headers=headers) ids = [d["document_id"] for d in r.json()["documents"]] assert doc_id not in ids def test_proofread_mode(tmp_path, monkeypatch, client): tenant = "legacy-proof" _seed_tenant(tmp_path, monkeypatch, tenant) r = client.post("/auth/register", json={"tenant_id": tenant, "passphrase": "secret123"}) token = r.json()["access_token"] headers = {"Authorization": f"Bearer {token}"} past = tmp_path / "past.docx" doc = Document() doc.add_paragraph("D2 Roof coverings") doc.add_paragraph("The roof comprises slate tiles to pitched rafters.") doc.save(str(past)) with past.open("rb") as fh: up = client.post( "/upload/batch", headers=headers, files=[("files", ("past.docx", fh, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"))], ) doc_id = up.json()["items"][0]["document_id"] r = client.post(f"/reports?document_id={doc_id}&survey_level=3", headers=headers) report_id = r.json()["report_id"] draft = "The roof comprise slate tiles to pitched rafters." r = client.post( f"/reports/{report_id}/generate", headers=headers, json={ "template_id": "D2", "bullets": ["slate tiles"], "mode": "proofread", "draft_paragraph": draft, }, ) assert r.status_code == 200 import time for _ in range(60): st = client.get(f"/reports/{report_id}/status", headers=headers).json() if st["status"] in ("complete", "failed"): break time.sleep(0.1) assert st["status"] == "complete" sections = client.get(f"/reports/{report_id}/sections", headers=headers).json()["sections"] assert sections["D2"]["mode"] == "proofread" assert sections["D2"]["text"] assert sections["D2"].get("style_profile") is not None