Spaces:
Runtime error
Runtime error
| """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 | |
| 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 | |