bio-nexus-api / tests /test_md_config.py
Samad14's picture
feat(md): verified force field/solvent menu with alanine-dipeptide startup probe
571487c
Raw
History Blame Contribute Delete
3.86 kB
"""
Unit tests for the MD force field / solvent configuration menu.
Covers the parts that do not require OpenMM (fast, always run):
- FF_XML / SOLVENT_XML menus contain the documented entries
- get_forcefields_menu() shape and that it only exposes verified combos
- resolve_combo() normalization and explicit ValueError for unknown force
fields, unknown solvents, and (when OpenMM is present) unverified pairs
The heavy per-combo createSystem() probe is exercised once (skip-if-no-OpenMM)
to confirm the startup verification itself runs end to end.
"""
import pytest
from app.tools.md_config import (
FF_XML,
SOLVENT_XML,
DEFAULT_FORCEFIELD,
DEFAULT_SOLVENT,
get_forcefields_menu,
resolve_combo,
verify_ff_solvent_combos,
)
class TestMenus:
def test_forcefield_menu_entries(self):
assert FF_XML == {
"amber14": "amber14-all.xml",
"ff14sb": "amber14/protein.ff14SB.xml",
"ff15ipq": "amber14/protein.ff15ipq.xml",
"ff19sb": "amber19/protein.ff19SB.xml",
"amberfb15": "amberfb15.xml",
"charmm36": "charmm36.xml",
}
def test_solvent_menu_entries(self):
assert SOLVENT_XML == {
"obc1": "implicit/obc1.xml",
"obc2": "implicit/obc2.xml",
"gbn2": "implicit/gbn2.xml",
}
def test_defaults_are_amber14_obc2(self):
assert DEFAULT_FORCEFIELD == "amber14"
assert DEFAULT_SOLVENT == "obc2"
class TestGetForcefieldsMenu:
def test_menu_shape(self):
menu = get_forcefields_menu()
assert set(menu) >= {"forcefields", "solvents", "combos", "defaults"}
assert isinstance(menu["forcefields"], list)
assert isinstance(menu["solvents"], list)
assert menu["defaults"] == {"forcefield": "amber14", "solvent": "obc2"}
# every listed force field has at least one verified solvent
combos = menu["combos"]
for ff in menu["forcefields"]:
assert ff["value"] in combos
assert combos[ff["value"]]
def test_combos_only_contain_known_solvents(self):
menu = get_forcefields_menu()
for ff, solvents in menu["combos"].items():
assert ff in FF_XML
assert set(solvents) <= set(SOLVENT_XML)
class TestResolveCombo:
def test_defaults(self):
assert resolve_combo(None, None) == ("amber14", "obc2")
def test_normalization(self):
assert resolve_combo(" ff14SB ", "GBN2") == ("ff14sb", "gbn2")
def test_unknown_forcefield_raises(self):
with pytest.raises(ValueError, match="Unsupported force field"):
resolve_combo("opls", "obc2")
def test_unknown_solvent_raises(self):
with pytest.raises(ValueError, match="Unsupported solvent"):
resolve_combo("ff14sb", "explicit")
def test_unverified_combo_raises(self):
# Simulate a deployment where only amber14 passed verification.
verified = verify_ff_solvent_combos()
if not verified:
pytest.skip("OpenMM unavailable — verification empty")
unverified = [ff for ff in FF_XML if ff not in verified]
if not unverified:
pytest.skip("all force fields verified — nothing unverified to test")
with pytest.raises(ValueError, match="not available"):
resolve_combo(unverified[0], "obc2")
class TestStartupVerification:
def test_verification_runs_end_to_end(self):
verified = verify_ff_solvent_combos()
if not verified:
pytest.importorskip("openmm")
# Every entry maps to a valid force field with known solvents.
assert verified == {ff: tuple(s) for ff, s in verified.items()}
for ff, solvents in verified.items():
assert ff in FF_XML
assert set(solvents) <= set(SOLVENT_XML)