hackathon / tests /research /test_drug_dose_adjuster.py
mekosotto's picture
feat(researcher): DCE-MRI BBB permeability bridge + drug-dose adjuster
327b23d
"""Tests for src.research.drug_dose_adjuster — pure-function dose revision."""
from __future__ import annotations
import pytest
from src.research.drug_dose_adjuster import adjust
class TestAdjust:
# --- BBB intact ---------------------------------------------------------
def test_intact_bbb_no_change_permeable_drug(self) -> None:
out = adjust(baseline_dose_mg=100.0, bbb_permeability_score=0.05, drug_bbb_permeable=True)
assert out.recommended_dose_mg == pytest.approx(100.0)
assert out.adjustment_factor == 1.0
assert out.risk_level == "low"
def test_intact_bbb_no_change_non_permeable_drug(self) -> None:
out = adjust(baseline_dose_mg=200.0, bbb_permeability_score=0.0, drug_bbb_permeable=False)
assert out.adjustment_factor == 1.0
assert out.risk_level == "low"
# --- BBB leaky, drug permeable -----------------------------------------
def test_leaky_bbb_permeable_drug_reduces_dose(self) -> None:
out = adjust(baseline_dose_mg=100.0, bbb_permeability_score=0.5, drug_bbb_permeable=True)
# factor = max(0.3, 1 - 0.7*0.5) = max(0.3, 0.65) = 0.65
assert out.adjustment_factor == pytest.approx(0.65)
assert out.recommended_dose_mg == pytest.approx(65.0)
assert out.risk_level == "moderate"
def test_severe_leakage_permeable_drug_high_risk(self) -> None:
out = adjust(baseline_dose_mg=100.0, bbb_permeability_score=0.8, drug_bbb_permeable=True)
assert out.risk_level == "high"
# factor = max(0.3, 1 - 0.7*0.8) = max(0.3, 0.44) = 0.44
assert out.adjustment_factor == pytest.approx(0.44)
def test_safety_floor_not_breached(self) -> None:
# At perm=1.0: 1 - 0.7 = 0.3 — the floor.
out = adjust(baseline_dose_mg=100.0, bbb_permeability_score=1.0, drug_bbb_permeable=True)
assert out.adjustment_factor >= 0.3 - 1e-9
# And anything above 1.0 (clipped by validator) — won't get there.
# --- BBB leaky, drug NOT permeable -------------------------------------
def test_leaky_bbb_non_permeable_drug_mild_reduction(self) -> None:
out = adjust(baseline_dose_mg=100.0, bbb_permeability_score=0.5, drug_bbb_permeable=False)
# factor = max(0.6, 1 - 0.4*0.5) = max(0.6, 0.8) = 0.8
assert out.adjustment_factor == pytest.approx(0.8)
assert out.risk_level == "moderate"
def test_non_permeable_drug_safety_floor(self) -> None:
out = adjust(baseline_dose_mg=100.0, bbb_permeability_score=1.0, drug_bbb_permeable=False)
assert out.adjustment_factor == pytest.approx(0.6)
# --- Drug permeability unknown -----------------------------------------
def test_unknown_permeability_treated_as_permeable(self) -> None:
out_unknown = adjust(baseline_dose_mg=100.0, bbb_permeability_score=0.5, drug_bbb_permeable=None)
out_perm = adjust(baseline_dose_mg=100.0, bbb_permeability_score=0.5, drug_bbb_permeable=True)
assert out_unknown.adjustment_factor == pytest.approx(out_perm.adjustment_factor)
def test_unknown_permeability_rationale_says_so(self) -> None:
out = adjust(baseline_dose_mg=100.0, bbb_permeability_score=0.5, drug_bbb_permeable=None)
assert "unknown" in out.rationale.lower()
# --- Validation ---------------------------------------------------------
def test_zero_baseline_raises(self) -> None:
with pytest.raises(ValueError, match="baseline_dose_mg must be > 0"):
adjust(baseline_dose_mg=0.0, bbb_permeability_score=0.5)
def test_negative_baseline_raises(self) -> None:
with pytest.raises(ValueError, match="baseline_dose_mg must be > 0"):
adjust(baseline_dose_mg=-10.0, bbb_permeability_score=0.5)
def test_score_out_of_range_raises(self) -> None:
with pytest.raises(ValueError, match="bbb_permeability_score must be in"):
adjust(baseline_dose_mg=100.0, bbb_permeability_score=1.5)
with pytest.raises(ValueError, match="bbb_permeability_score must be in"):
adjust(baseline_dose_mg=100.0, bbb_permeability_score=-0.1)
# --- Monotonicity ------------------------------------------------------
def test_increasing_leakage_lowers_recommended_dose_for_permeable_drug(self) -> None:
scores = [0.25, 0.4, 0.55, 0.7, 0.85]
doses = [
adjust(100.0, s, drug_bbb_permeable=True).recommended_dose_mg
for s in scores
]
# Strictly non-increasing.
assert all(a >= b - 1e-9 for a, b in zip(doses, doses[1:]))
# --- Rationale always says it's not medical advice --------------------
def test_rationale_disclaims_medical_advice(self) -> None:
for score in [0.0, 0.3, 0.7, 1.0]:
out = adjust(100.0, score, drug_bbb_permeable=True)
assert "research suggestion" in out.rationale.lower()
assert "medical advice" in out.rationale.lower()