Upload 5 files
Browse files- tests/conftest.py +7 -0
- tests/test_cdc.py +17 -0
- tests/test_ctx_resolve.py +55 -0
- tests/test_iarc_render.py +19 -0
- tests/test_rate_limit.py +18 -0
tests/conftest.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
# Ensure "core" can be imported when tests run from repo root.
|
| 5 |
+
ROOT = Path(__file__).resolve().parents[1]
|
| 6 |
+
if str(ROOT) not in sys.path:
|
| 7 |
+
sys.path.insert(0, str(ROOT))
|
tests/test_cdc.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from core.sources import cdc
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_cdc_cas_exact_match():
|
| 5 |
+
matches = cdc.search("67-64-1")
|
| 6 |
+
assert any(m.get("name") == "Acetone" for m in matches)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def test_cdc_name_substring_match():
|
| 10 |
+
matches = cdc.search("xylene")
|
| 11 |
+
assert len(matches) >= 3
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_cdc_toxprofile_for_unique():
|
| 15 |
+
match = cdc.toxprofile_for("Acrylamide")
|
| 16 |
+
assert isinstance(match, dict)
|
| 17 |
+
assert match.get("cas") == "79-06-1"
|
tests/test_ctx_resolve.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
|
| 3 |
+
import httpx
|
| 4 |
+
|
| 5 |
+
from core.sources import ctx
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_resolve_dtxsid_prefers_cas(monkeypatch):
|
| 9 |
+
async def fake_cas(q, http):
|
| 10 |
+
return "DTXSID1234567"
|
| 11 |
+
|
| 12 |
+
async def fake_name(q, http):
|
| 13 |
+
return None
|
| 14 |
+
|
| 15 |
+
async def fake_pc(q, http):
|
| 16 |
+
return "DTXSID0000000"
|
| 17 |
+
|
| 18 |
+
async def fake_dash(q, http):
|
| 19 |
+
return None
|
| 20 |
+
|
| 21 |
+
monkeypatch.setattr(ctx, "_resolve_from_cas", fake_cas)
|
| 22 |
+
monkeypatch.setattr(ctx, "_resolve_from_name", fake_name)
|
| 23 |
+
monkeypatch.setattr(ctx, "_resolve_dtxsid_via_pubchem", fake_pc)
|
| 24 |
+
monkeypatch.setattr(ctx, "_resolve_dtxsid_via_dashboard", fake_dash)
|
| 25 |
+
|
| 26 |
+
async def run():
|
| 27 |
+
async with httpx.AsyncClient() as client:
|
| 28 |
+
return await ctx.resolve_dtxsid("50-00-0", client)
|
| 29 |
+
|
| 30 |
+
assert asyncio.run(run()) == "DTXSID1234567"
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def test_resolve_dtxsid_pubchem_fallback(monkeypatch):
|
| 34 |
+
async def fake_cas(q, http):
|
| 35 |
+
return None
|
| 36 |
+
|
| 37 |
+
async def fake_name(q, http):
|
| 38 |
+
return None
|
| 39 |
+
|
| 40 |
+
async def fake_pc(q, http):
|
| 41 |
+
return "DTXSID7654321"
|
| 42 |
+
|
| 43 |
+
async def fake_dash(q, http):
|
| 44 |
+
return None
|
| 45 |
+
|
| 46 |
+
monkeypatch.setattr(ctx, "_resolve_from_cas", fake_cas)
|
| 47 |
+
monkeypatch.setattr(ctx, "_resolve_from_name", fake_name)
|
| 48 |
+
monkeypatch.setattr(ctx, "_resolve_dtxsid_via_pubchem", fake_pc)
|
| 49 |
+
monkeypatch.setattr(ctx, "_resolve_dtxsid_via_dashboard", fake_dash)
|
| 50 |
+
|
| 51 |
+
async def run():
|
| 52 |
+
async with httpx.AsyncClient() as client:
|
| 53 |
+
return await ctx.resolve_dtxsid("formaldehyde", client)
|
| 54 |
+
|
| 55 |
+
assert asyncio.run(run()) == "DTXSID7654321"
|
tests/test_iarc_render.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from app import render_iarc_block
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_iarc_render_url():
|
| 5 |
+
md = render_iarc_block({"ok": True, "url": "https://example.com"})
|
| 6 |
+
assert "Search IARC Monographs" in md
|
| 7 |
+
assert "https://example.com" in md
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def test_iarc_render_results_list():
|
| 11 |
+
md = render_iarc_block(
|
| 12 |
+
{
|
| 13 |
+
"ok": True,
|
| 14 |
+
"results": [
|
| 15 |
+
{"title": "IARC Monographs - Foo", "url": "https://example.com/foo", "year": "2020"}
|
| 16 |
+
],
|
| 17 |
+
}
|
| 18 |
+
)
|
| 19 |
+
assert "- [IARC Monographs - Foo](https://example.com/foo) (2020)" in md
|
tests/test_rate_limit.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from core import rate_limit
|
| 2 |
+
from core.config import settings
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def test_rate_limit_in_memory(monkeypatch):
|
| 6 |
+
monkeypatch.setattr(settings, "max_ai_summaries_per_day", 2)
|
| 7 |
+
monkeypatch.setattr(settings, "redis_url", "")
|
| 8 |
+
rate_limit._state["date"] = None
|
| 9 |
+
rate_limit._state["count"] = 0
|
| 10 |
+
|
| 11 |
+
allowed1, info1 = rate_limit.check_and_increment_global_ai_cap()
|
| 12 |
+
allowed2, info2 = rate_limit.check_and_increment_global_ai_cap()
|
| 13 |
+
allowed3, info3 = rate_limit.check_and_increment_global_ai_cap()
|
| 14 |
+
|
| 15 |
+
assert allowed1 is True
|
| 16 |
+
assert allowed2 is True
|
| 17 |
+
assert allowed3 is False
|
| 18 |
+
assert info3["limit"] == 2
|