syntheogenesis / tests /test_primer_save.py
Tengo Gzirishvili
Unified "My designs" hub: save every tool, not just CRISPR
1ee54b6
Raw
History Blame Contribute Delete
5.76 kB
"""Endpoint tests for the unified saved-work hub:
- Primer Analysis persistence (/api/primers/{save,analyses,analyses/<id>})
- Directed-Evolution library list + signed download (/api/library …)
Flask test client; the auth + DB layer is stubbed (no network, no DB)."""
import types
import pytest
from dee import server
GOOD_RESULT = {
"primers": [{"name": "F1", "fitness": 0.8}, {"name": "R1", "fitness": 0.7}],
"has_template": True,
}
@pytest.fixture
def client():
app = server.create_app()
app.config.update(TESTING=True)
return app.test_client()
def _signed_in(monkeypatch):
monkeypatch.setattr(server._auth, "get_auth",
lambda: types.SimpleNamespace(anonymous=False, user_id="u1",
email="x@y.z", plan="free"))
# Lazy sweep is fire-and-forget; stub it so tests never touch the network.
monkeypatch.setattr(server._auth, "cleanup_expired_primer_analyses_async",
lambda *a, **k: None)
def _anon(monkeypatch):
monkeypatch.setattr(server._auth, "get_auth",
lambda: types.SimpleNamespace(anonymous=True, user_id=None,
email=None, plan="anon"))
# ─────────────────────────── Primer save ───────────────────────────
def test_primer_save_requires_signin(client, monkeypatch):
_anon(monkeypatch)
r = client.post("/api/primers/save", json={"result": GOOD_RESULT})
assert r.status_code == 403
assert r.get_json()["kind"] == "signin_required"
def test_primer_save_rejects_empty(client, monkeypatch):
_signed_in(monkeypatch)
r = client.post("/api/primers/save", json={"result": {"primers": []}})
assert r.status_code == 400
def test_primer_save_passes_through(client, monkeypatch):
_signed_in(monkeypatch)
captured = {}
def fake_save(user_id, **kw):
captured["user_id"] = user_id
captured.update(kw)
return {"ok": True, "id": "abc"}
monkeypatch.setattr(server._auth, "save_primer_analysis", fake_save)
r = client.post("/api/primers/save", json={
"label": "my set", "organism": "human", "template_len": 700,
"n_primers": 2, "result": GOOD_RESULT})
assert r.status_code == 200
assert r.get_json() == {"ok": True, "id": "abc"}
assert captured["user_id"] == "u1" # server-supplied, not client
assert captured["n_primers"] == 2
assert captured["result"]["primers"] # result forwarded intact
# ─────────────────────── Primer list / get / delete ───────────────────────
def test_primer_list_requires_signin(client, monkeypatch):
_anon(monkeypatch)
assert client.get("/api/primers/analyses").status_code == 403
def test_primer_list(client, monkeypatch):
_signed_in(monkeypatch)
monkeypatch.setattr(server._auth, "list_primer_analyses",
lambda uid: [{"id": "1", "label": "set A", "n_primers": 3}])
r = client.get("/api/primers/analyses")
assert r.status_code == 200
assert r.get_json()["analyses"][0]["label"] == "set A"
def test_primer_get_and_404(client, monkeypatch):
_signed_in(monkeypatch)
monkeypatch.setattr(server._auth, "get_primer_analysis",
lambda uid, aid: ({"id": aid, "result": GOOD_RESULT}
if aid == "ok" else None))
assert client.get("/api/primers/analyses/ok").status_code == 200
assert client.get("/api/primers/analyses/missing").status_code == 404
def test_primer_delete(client, monkeypatch):
_signed_in(monkeypatch)
monkeypatch.setattr(server._auth, "delete_primer_analysis",
lambda uid, aid: {"ok": True})
r = client.delete("/api/primers/analyses/xyz")
assert r.status_code == 200 and r.get_json()["ok"] is True
# ───────────────── DE library list + signed download ─────────────────
def test_library_list_requires_signin(client, monkeypatch):
_anon(monkeypatch)
assert client.get("/api/library").status_code == 403
def test_library_list(client, monkeypatch):
_signed_in(monkeypatch)
monkeypatch.setattr(server._auth, "list_libraries",
lambda uid: [{"id": "L1", "name": "lib", "n_variants": 96}])
r = client.get("/api/library")
assert r.status_code == 200
assert r.get_json()["libraries"][0]["n_variants"] == 96
def test_library_download_signed_url(client, monkeypatch):
_signed_in(monkeypatch)
monkeypatch.setattr(server._auth, "library_csv_url",
lambda uid, lid: ("https://example.test/signed.csv"
if lid == "L1" else None))
ok = client.get("/api/library/L1/download")
assert ok.status_code == 200 and ok.get_json()["url"].endswith("signed.csv")
assert client.get("/api/library/nope/download").status_code == 404
# ───────────────── Graceful degradation (no Supabase configured) ─────────────────
def test_save_primer_analysis_unconfigured_is_graceful(monkeypatch):
monkeypatch.setattr(server._auth, "SUPABASE_URL", "", raising=False)
monkeypatch.setattr(server._auth, "SUPABASE_SERVICE_KEY", "", raising=False)
out = server._auth.save_primer_analysis(
"u1", label="x", organism=None, template_len=10, n_primers=1,
result={"primers": [{"name": "F"}]})
assert out["ok"] is False and "error" in out