Spaces:
Running
Running
| """Tests for the admin maintenance routes that produce the REAL glass-box | |
| receipts server-side (/api/admin/run-benchmarks, /api/admin/seed-commons) — | |
| run in-Space, where the live ESM-2 model and Supabase credentials already | |
| exist. Both are admin-token gated; both are exercised against the ACTUAL | |
| bundled fixture (dee/data/dms_fixtures/) so a malformed manifest/CSV would | |
| fail here too, not just in production. | |
| """ | |
| import json | |
| import re | |
| import types | |
| from pathlib import Path | |
| import pandas as pd | |
| import pytest | |
| from dee import server | |
| from dee.core.dms_seed import parse_proteingym_csv | |
| ADMIN_TOKEN = "test-admin-token-xyz" | |
| def client(): | |
| app = server.create_app() | |
| app.config.update(TESTING=True) | |
| return app.test_client() | |
| def _with_admin(monkeypatch): | |
| monkeypatch.setenv("TURINGDNA_ADMIN_TOKEN", ADMIN_TOKEN) | |
| def _fixtures_dir(): | |
| return Path(server.__file__).resolve().parent / "data" / "dms_fixtures" | |
| # --------------------------------------------------------------------------- # | |
| # auth gate — shared shape for both routes | |
| # --------------------------------------------------------------------------- # | |
| def test_run_benchmarks_requires_admin_token(client, monkeypatch): | |
| _with_admin(monkeypatch) | |
| r = client.post("/api/admin/run-benchmarks") | |
| assert r.status_code == 403 | |
| r2 = client.post("/api/admin/run-benchmarks", headers={"X-Admin-Token": "wrong"}) | |
| assert r2.status_code == 403 | |
| def test_seed_commons_requires_admin_token(client, monkeypatch): | |
| _with_admin(monkeypatch) | |
| r = client.post("/api/admin/seed-commons") | |
| assert r.status_code == 403 | |
| # --------------------------------------------------------------------------- # | |
| # /api/admin/run-benchmarks — real bundled fixture, fake (fast) scorer | |
| # --------------------------------------------------------------------------- # | |
| def test_run_benchmarks_against_real_fixture(client, monkeypatch, tmp_path): | |
| _with_admin(monkeypatch) | |
| real_fixtures = _fixtures_dir() # resolve BEFORE monkeypatching server.__file__ below | |
| manifest = json.loads((real_fixtures / "manifest.json").read_text(encoding="utf-8")) | |
| assert len(manifest) >= 3, "expected the 3 bundled + validated DMS assays" | |
| # Build a per-sequence fake scorer covering EVERY bundled assay's own real | |
| # labels (synthetic-but-distinct values, index-based) — a genuine smoke | |
| # test of all 3 production fixtures end to end (real manifest -> real CSV | |
| # -> predict_additive -> evaluate_dataset -> summarize -> written file), | |
| # without needing ESM. | |
| rx = re.compile(r"^([A-Za-z])(\d+)([A-Za-z*])$") | |
| df_by_seq = {} | |
| for a in manifest: | |
| recs = parse_proteingym_csv((real_fixtures / a["csv"]).read_text(encoding="utf-8")) | |
| assert len(recs) > 50, f"{a['name']} fixture looks too small to be real" | |
| rows, seen = [], set() | |
| for i, (lab, _v) in enumerate(recs): | |
| m = rx.match(lab) | |
| key = (int(m.group(2)) - 1, m.group(3).upper()) | |
| if key in seen: | |
| continue | |
| seen.add(key) | |
| rows.append({"position": key[0], "wt_aa": m.group(1).upper(), | |
| "mut_aa": key[1], "delta_ll": float(i % 37) * 0.1}) | |
| df_by_seq[a["sequence"]] = pd.DataFrame(rows) | |
| monkeypatch.setattr(server._scoring, "get_scorer", lambda *a, **kw: "the-scorer") | |
| monkeypatch.setattr(server._scoring, "score_guarded", | |
| lambda scorer, seq: df_by_seq[seq]) | |
| # Write the output somewhere disposable so this test never touches the | |
| # committed dee/data/benchmarks.json. | |
| monkeypatch.setattr(server, "__file__", | |
| str(tmp_path / "server.py")) # relocates Path(__file__).parent | |
| (tmp_path / "data").mkdir() | |
| import shutil | |
| shutil.copytree(real_fixtures, tmp_path / "data" / "dms_fixtures") | |
| r = client.post("/api/admin/run-benchmarks", | |
| headers={"X-Admin-Token": ADMIN_TOKEN}, json={"model": "small"}) | |
| assert r.status_code == 200 | |
| body = r.get_json() | |
| assert body["ok"] is True | |
| assert body["model"] == "small" | |
| assert body["generated_at"] is not None | |
| assert body["summary"]["n_datasets"] == len(manifest) | |
| names = {d["name"] for d in body["datasets"]} | |
| assert names == {a["name"] for a in manifest} | |
| for d in body["datasets"]: | |
| assert d["n"] > 0 | |
| written = json.loads((tmp_path / "data" / "benchmarks.json").read_text(encoding="utf-8")) | |
| assert written["summary"]["n_datasets"] == len(manifest) | |
| def test_run_benchmarks_missing_fixtures_404(client, monkeypatch, tmp_path): | |
| _with_admin(monkeypatch) | |
| monkeypatch.setattr(server, "__file__", str(tmp_path / "server.py")) | |
| (tmp_path / "data").mkdir() # no dms_fixtures subdir | |
| r = client.post("/api/admin/run-benchmarks", headers={"X-Admin-Token": ADMIN_TOKEN}) | |
| assert r.status_code == 404 | |
| def test_run_benchmarks_surfaces_per_assay_failure(client, monkeypatch, tmp_path): | |
| """One assay's scoring blows up (e.g. a real ESM error on the Space) — the | |
| other two must still complete, and the failure must be visible in the | |
| response itself (name + error), not just swallowed into a shorter | |
| 'datasets' list with no explanation. This is what a real diagnosis of | |
| 'why did only 1 of 3 datasets come back' should read directly off the | |
| curl output instead of requiring server-log access.""" | |
| _with_admin(monkeypatch) | |
| real_fixtures = _fixtures_dir() | |
| manifest = json.loads((real_fixtures / "manifest.json").read_text(encoding="utf-8")) | |
| assert len(manifest) >= 3 | |
| def flaky_score_guarded(scorer, seq): | |
| if len(seq) > 500: # the two longer real fixtures (PABP 577aa, DLG4 724aa) | |
| raise RuntimeError("boom: simulated real scoring failure") | |
| return pd.DataFrame([{"position": 0, "wt_aa": seq[0], "mut_aa": "Z", "delta_ll": 0.1}]) | |
| monkeypatch.setattr(server._scoring, "get_scorer", lambda *a, **kw: "the-scorer") | |
| monkeypatch.setattr(server._scoring, "score_guarded", flaky_score_guarded) | |
| monkeypatch.setattr(server, "__file__", str(tmp_path / "server.py")) | |
| (tmp_path / "data").mkdir() | |
| import shutil | |
| shutil.copytree(real_fixtures, tmp_path / "data" / "dms_fixtures") | |
| r = client.post("/api/admin/run-benchmarks", | |
| headers={"X-Admin-Token": ADMIN_TOKEN}, json={"model": "small"}) | |
| assert r.status_code == 200 | |
| body = r.get_json() | |
| long_assays = [a["name"] for a in manifest if len(a["sequence"]) > 500] | |
| assert len(long_assays) == 2, "expected exactly PABP + DLG4 to be >500aa" | |
| assert body["summary"]["n_datasets"] == len(manifest) - len(long_assays) | |
| assert "failed" in body and len(body["failed"]) == len(long_assays) | |
| failed_names = {f["name"] for f in body["failed"]} | |
| assert failed_names == set(long_assays) | |
| for f in body["failed"]: | |
| assert "boom: simulated real scoring failure" in f["error"] | |
| # --------------------------------------------------------------------------- # | |
| # /api/admin/seed-commons — real bundled fixture, mocked Supabase write | |
| # --------------------------------------------------------------------------- # | |
| def test_seed_commons_against_real_fixture(client, monkeypatch): | |
| _with_admin(monkeypatch) | |
| captured = {} | |
| def fake_replace(rows, source="user"): | |
| captured["rows"] = rows | |
| return {"ok": True} | |
| monkeypatch.setattr(server._auth, "replace_mutation_priors", fake_replace) | |
| r = client.post("/api/admin/seed-commons", headers={"X-Admin-Token": ADMIN_TOKEN}) | |
| assert r.status_code == 200 | |
| body = r.get_json() | |
| assert body["ok"] is True | |
| assert body["contributing_assays"] == 3 | |
| # 3 independent assays clears the k-anonymity floor (MIN_USERS=3): every | |
| # single-mutant scan covers close to all 19 substitutions at every | |
| # position, so most of the 380 possible substitution TYPES show up in | |
| # all three proteins' data and survive. Proves the privacy floor is | |
| # exactly a floor, not a permanent block, once there's real independent | |
| # coverage — and that it produces real, usable rows on real data. | |
| assert body["substitutions"] > 0 | |
| assert len(captured["rows"]) == body["substitutions"] | |
| for row in captured["rows"]: | |
| assert row["n_users"] >= 3 # every kept row backed by all 3 independent assays | |
| assert ">" in row["substitution"] | |
| def test_seed_commons_survives_one_bad_csv(client, monkeypatch, tmp_path): | |
| """A single unreadable/corrupt fixture CSV must not 500 the whole request | |
| (there was previously no try/except around this loop at all) — it should | |
| degrade to the other assays and report the failure by name.""" | |
| _with_admin(monkeypatch) | |
| real_fixtures = _fixtures_dir() | |
| monkeypatch.setattr(server._auth, "replace_mutation_priors", lambda rows, source="user": {"ok": True}) | |
| monkeypatch.setattr(server, "__file__", str(tmp_path / "server.py")) | |
| (tmp_path / "data").mkdir() | |
| import shutil | |
| dest = tmp_path / "data" / "dms_fixtures" | |
| shutil.copytree(real_fixtures, dest) | |
| manifest = json.loads((dest / "manifest.json").read_text(encoding="utf-8")) | |
| broken_name = manifest[0]["name"] | |
| (dest / manifest[0]["csv"]).unlink() # simulate a missing/corrupt fixture file | |
| r = client.post("/api/admin/seed-commons", headers={"X-Admin-Token": ADMIN_TOKEN}) | |
| assert r.status_code == 200 | |
| body = r.get_json() | |
| assert body["contributing_assays"] == len(manifest) - 1 | |
| assert "failed" in body and len(body["failed"]) == 1 | |
| assert body["failed"][0]["name"] == broken_name | |
| def test_seed_commons_gated_before_effective_date(client, monkeypatch): | |
| _with_admin(monkeypatch) | |
| import datetime as _dt | |
| from dee.core import aggregate as _agg | |
| monkeypatch.setattr(_agg, "EFFECTIVE_DATE", _dt.date(2099, 1, 1)) | |
| r = client.post("/api/admin/seed-commons", headers={"X-Admin-Token": ADMIN_TOKEN}) | |
| assert r.status_code == 423 | |
| assert r.get_json()["error"] == "gated" | |