Spaces:
Running
Running
File size: 7,179 Bytes
4939f56 | 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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | """Tests for techspec.md Β§3 β page capture + final synthesis."""
import asyncio
import pytest
from app.services.page_capture import extract_page_content, capture_page, _last_hit
from app.services import final_synthesis as fs
# ββ HTML extraction (stdlib-only parser) βββββββββββββββββββββββββββββββββββββ
_FIXTURE = """
<html><head><title>UniProt | P04637 test</title>
<meta property="og:image" content="https://img.example.org/og.png">
</head><body>
<script>var junk = "<h2>not a heading</h2>";</script>
<h2>Function</h2><p>Tumor suppressor with <b>many</b> roles.</p>
<h3>Subcellular location</h3><p>Nucleus.</p>
<figure><img src="https://img.example.org/fig1.png"></figure>
</body></html>
"""
def test_extract_title_sections_figures():
out = extract_page_content(_FIXTURE)
assert out["title"] == "UniProt | P04637 test"
assert "Tumor suppressor" in out["text_sections"][0]["text"]
assert out["text_sections"][0]["heading"] == "Function"
assert any("Subcellular" in (s["heading"] or "") for s in out["text_sections"])
assert "https://img.example.org/og.png" in out["figure_urls"]
assert "https://img.example.org/fig1.png" in out["figure_urls"]
# script-tag contents must not leak into sections
assert all("junk" not in s["text"] for s in out["text_sections"])
def test_extract_handles_empty_html():
out = extract_page_content("<html><body></body></html>")
assert out["title"] is None
assert out["text_sections"] == []
assert out["figure_urls"] == []
# ββ Capture persistence + failure honesty ββββββββββββββββββββββββββββββββββββ
class _FakeTable:
def __init__(self, store):
self.store = store
def upsert(self, row, on_conflict=None):
self.row = row
return self
def execute(self):
self.store.append(self.row)
return self
@pytest.mark.asyncio
async def test_capture_success_persists_row(monkeypatch):
class _Resp:
status_code = 200
text = _FIXTURE
def raise_for_status(self):
pass
class _Client:
def __init__(self, *a, **k):
pass
async def __aenter__(self):
return self
async def __aexit__(self, *a):
return False
async def get(self, url, headers=None):
return _Resp()
rows = []
monkeypatch.setattr("app.services.page_capture._get_supabase", lambda: type("SB", (), {"table": staticmethod(lambda name: _FakeTable(rows))})())
monkeypatch.setattr("app.services.page_capture.httpx.AsyncClient", _Client)
monkeypatch.setattr("app.services.page_capture.MIN_INTERVAL_S", 0.0)
_last_hit.clear()
row = await capture_page("11111111-2222-3333-4444-555555555555", "uniprot", "https://www.uniprot.org/uniprotkb/P04637", user_id="u1")
assert row["fetch_status"] == "captured"
assert row["source"] == "uniprot"
assert len(rows) == 1
@pytest.mark.asyncio
async def test_capture_failure_still_records_honest_row(monkeypatch):
class _Boom:
def __init__(self, *a, **k):
pass
async def __aenter__(self):
raise ConnectionError("down")
async def __aexit__(self, *a):
return False
rows = []
monkeypatch.setattr("app.services.page_capture._get_supabase", lambda: type("SB", (), {"table": staticmethod(lambda name: _FakeTable(rows))})())
monkeypatch.setattr("app.services.page_capture.httpx.AsyncClient", _Boom)
monkeypatch.setattr("app.services.page_capture.MIN_INTERVAL_S", 0.0)
row = await capture_page("11111111-2222-3333-4444-555555555555", "rcsb", "https://www.rcsb.org/structure/1UBQ")
assert row["fetch_status"] == "failed"
assert "down" in row["error_note"]
assert len(rows) == 1 # the attempt is still recorded
@pytest.mark.asyncio
async def test_per_host_rate_limit_enforced(monkeypatch):
intervals = []
async def fake_sleep(s):
intervals.append(s)
monkeypatch.setattr("app.services.page_capture.asyncio.sleep", fake_sleep)
from app.services.page_capture import _throttle
_last_hit.clear()
await _throttle("example.org") # first hit: no wait
assert intervals == []
await _throttle("example.org") # immediate repeat: must be throttled
assert len(intervals) >= 1 and intervals[0] > 0
n_after_second = len(intervals)
await _throttle("other.example.org") # different host: not throttled
assert len(intervals) == n_after_second
# ββ Final synthesis βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def test_findings_thread_confidence_tier():
context = {
"query": {"confidence": "homolog"},
"blast": {"count": 1, "top_hit": {"accession": "P04637", "description": "Cellular tumor antigen"}},
"uniprot": {"accession": "P04637", "full_name": "Cellular tumor antigen p53"},
}
report = fs.synthesize_sync(context)
tiers = {f["confidence_tier"] for f in report["findings"]}
assert tiers == {"homolog"}
assert any("Cellular tumor antigen p53" in f["claim"] for f in report["findings"])
assert any("homolog" in c.lower() for c in report["caveats"])
def test_denovo_report_is_explicit_about_predictions():
context = {
"query": {"confidence": "de_novo"},
"blast": {"count": 0, "top_hit": None},
"uniprot": {"_de_novo": True, "composition": {"sequence_type": "protein", "length": 120}},
}
report = fs.synthesize_sync(context)
assert "de novo" in report["headline"].lower()
joined = " ".join(report["caveats"]).lower()
assert "prediction" in joined
findings_tools = [f["source_tool"] for f in report["findings"]]
assert "blast" in findings_tools and "uniprot" in findings_tools
def test_findings_reference_real_pages():
context = {
"query": {"confidence": "identified"},
"blast": {"count": 1, "top_hit": {"accession": "P04637", "description": "p53"}},
"uniprot": {"accession": "P04637", "full_name": "p53", "pdb_ids": ["1TUP"]},
"alphafold": {"structure_available": True, "pdb_url": "https://files.rcsb.org/x.pdb", "mean_plddt": 91.2},
}
report = fs.synthesize_sync(context)
by_tool = {f["source_tool"]: f for f in report["findings"]}
assert by_tool["uniprot"]["page_url"].startswith("https://www.uniprot.org/")
assert by_tool["blast"]["page_url"].startswith("https://www.ncbi.nlm.nih.gov/protein/P04637")
@pytest.mark.asyncio
async def test_synthesis_mode_is_deterministic_without_llm(monkeypatch):
async def no_llm(*a, **k):
return None
monkeypatch.setattr(fs, "_polish_with_llm", no_llm)
context = {"query": {"confidence": "identified"}, "blast": {"count": 0, "top_hit": None}}
report = await fs.synthesize(context)
assert report["_mode"] == "deterministic"
assert report["findings"][0]["claim"].startswith("No significant similarity")
|