File size: 4,556 Bytes
cd0c7a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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"