Spaces:
Running
Running
File size: 4,556 Bytes
9591ffa | 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 | """
Shared fixtures for Bio Nexus backend smoke tests.
Mocks heavy/unavailable dependencies (supabase, litellm, Bio, sentry, etc.)
at sys.modules level so routers can import cleanly in a local test env.
"""
import base64
import json
import os
import sys
from unittest.mock import MagicMock
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
# Ensure the backend app package is importable
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
# ---------------------------------------------------------------------------
# Mock out heavy dependencies that aren't installed in the test env
# ---------------------------------------------------------------------------
_MOCK_MODULES = [
"supabase",
"supabase._sync.client",
"litellm",
"reportlab",
"reportlab.lib",
"reportlab.lib.pagesizes",
"reportlab.lib.styles",
"reportlab.lib.units",
"reportlab.lib.colors",
"reportlab.pdfgen",
"reportlab.pdfgen.canvas",
"reportlab.platypus",
"sentry_sdk",
"redis",
]
for mod_name in _MOCK_MODULES:
if mod_name not in sys.modules:
sys.modules[mod_name] = MagicMock()
# ---------------------------------------------------------------------------
# Minimal JWT helper
# ---------------------------------------------------------------------------
def _make_test_jwt(user_id: str = "test-user-000") -> str:
header = base64.urlsafe_b64encode(json.dumps({"alg": "none", "typ": "JWT"}).encode()).rstrip(b"=").decode()
payload = base64.urlsafe_b64encode(json.dumps({"sub": user_id}).encode()).rstrip(b"=").decode()
return f"{header}.{payload}.sig"
TEST_JWT = _make_test_jwt()
# ---------------------------------------------------------------------------
# Lightweight test app — only imports the routers we actually test
# ---------------------------------------------------------------------------
@pytest.fixture(scope="session")
def client():
"""Build a minimal FastAPI app with only the 4 routers under test."""
app = FastAPI(title="Bio Nexus Smoke Tests")
from app.routers import admet, md, function_predict, docking
app.include_router(admet.router)
app.include_router(md.router)
app.include_router(function_predict.router)
app.include_router(docking.router)
with TestClient(app, raise_server_exceptions=False) as c:
yield c
@pytest.fixture()
def auth_headers():
"""Headers dict with a valid Bearer token."""
return {"Authorization": f"Bearer {TEST_JWT}"}
# ---------------------------------------------------------------------------
# Pre-load OpenMM native libs BEFORE rdkit. OpenMM and RDKit wheels bundle
# conflicting copies of MSVC runtime DLLs (msvcp140/concrt140); importing
# rdkit first makes OpenMM's Context creation crash with an access violation.
# Loading openmm.app first resolves the conflict.
# ---------------------------------------------------------------------------
try:
import openmm.app # noqa: F401
except Exception:
pass
# ---------------------------------------------------------------------------
# Detect rdkit availability (only on HF Spaces Docker, not local dev)
# find_spec avoids actually importing rdkit, which would trigger the
# OpenMM/RDKit runtime conflict above for real-OpenMM tests.
# ---------------------------------------------------------------------------
try:
import importlib.util
HAS_RDKIT = importlib.util.find_spec("rdkit.Chem") is not None
except (ImportError, ValueError):
HAS_RDKIT = False
requires_rdkit = pytest.mark.skipif(not HAS_RDKIT, reason="rdkit not installed locally — run on HF Spaces")
# ---------------------------------------------------------------------------
# Sample molecules
# ---------------------------------------------------------------------------
SAMPLE_MOLECULES = {
"aspirin": "CC(=O)OC1=CC=CC=C1C(=O)O",
"caffeine": "CN1C=NC2=C1C(=O)N(C(=O)N2C)C",
"ibuprofen": "CC(C)CC1=CC=C(C=C1)C(C)C(=O)O",
"paracetamol": "CC(=O)NC1=CC=C(C=C1)O",
"metformin": "CN(C)C(=N)NC(=O)N",
"paclitaxel": "CC(=O)OC1C(O)CC2OC3C(O)C(=CC(=O)O3)CC(O)C12C4=CC=CC=C4C(=O)OC5C(O)C(COC(=O)C)OC(O)C5NC(=O)C6=CC=CC=C6",
"short_pseudo": "C",
}
@pytest.fixture(params=list(SAMPLE_MOLECULES.keys()), ids=list(SAMPLE_MOLECULES.keys()))
def sample_smiles(request):
name = request.param
return name, SAMPLE_MOLECULES[name]
@pytest.fixture
def valid_smiles():
return "CC(=O)OC1=CC=CC=C1C(=O)O"
@pytest.fixture
def invalid_smiles():
return "NOT_A_SMILES_12345"
|