| """Tests for FDA alternatives tool (mocked HTTP).""" |
|
|
| from __future__ import annotations |
|
|
| from unittest.mock import patch |
|
|
| from app.tools.medication_alternatives import build_alternatives_response |
|
|
|
|
| def test_build_alternatives_empty_input() -> None: |
| out = build_alternatives_response(None, [], max_suggestions=5) |
| assert "errors" in out |
| assert out["source"] in {"none", "external"} |
|
|
|
|
| def test_multi_regimen_without_query_drug_returns_error() -> None: |
| """Do not silently use regimen[0] when several drugs are listed.""" |
| out = build_alternatives_response(None, ["benzodiazepine_like", "warfarin_like"], max_suggestions=5) |
| assert out["suggestions"] == [] |
| assert out["errors"] |
| assert "first" in out["errors"][0].lower() or "pick" in out["errors"][0].lower() |
|
|
|
|
| @patch("app.tools.medication_alternatives._external_suggestions", return_value=None) |
| @patch("app.tools.medication_alternatives._fda_get") |
| def test_build_alternatives_simulator_token_class_probe(mock_fda, _mock_ext) -> None: |
| """benzodiazepine_like normalizes and hits direct pharm_class_epc search.""" |
|
|
| def side_effect(search: str, limit: int) -> dict | None: |
| if "pharm_class_epc" in search and "enzodiazepine" in search.lower(): |
| return { |
| "results": [ |
| { |
| "openfda": { |
| "brand_name": ["VALIUM"], |
| "generic_name": ["DIAZEPAM"], |
| "pharm_class_epc": ["Benzodiazepine"], |
| "route": ["ORAL"], |
| }, |
| "adverse_reactions": ["Drowsiness."], |
| } |
| ] |
| } |
| return None |
|
|
| mock_fda.side_effect = side_effect |
| out = build_alternatives_response("benzodiazepine_like", [], max_suggestions=5) |
| assert out["therapeutic_class"] == "Benzodiazepine" |
| assert len(out["suggestions"]) >= 1 |
|
|
|
|
| @patch("app.tools.medication_alternatives._external_suggestions", return_value=None) |
| @patch("app.tools.medication_alternatives._fda_get") |
| def test_build_alternatives_openfda_path(mock_fda, _mock_ext) -> None: |
| """Resolve class from first label, then class search returns neighbors.""" |
| nsaid = "Nonsteroidal Anti-inflammatory Drug" |
|
|
| def side_effect(search: str, limit: int) -> dict | None: |
| if "openfda.generic_name" in search and "ibuprofen" in search.lower(): |
| return { |
| "results": [ |
| { |
| "openfda": { |
| "generic_name": ["IBUPROFEN"], |
| "pharm_class_epc": [nsaid], |
| }, |
| } |
| ] |
| } |
| if "pharm_class_epc" in search and nsaid in search: |
| return { |
| "results": [ |
| { |
| "openfda": { |
| "brand_name": ["OTHER NSAID"], |
| "generic_name": ["KETOPROFEN"], |
| "pharm_class_epc": [nsaid], |
| "route": ["ORAL"], |
| }, |
| "adverse_reactions": ["GI bleeding risk in some patients."], |
| } |
| ] |
| } |
| return None |
|
|
| mock_fda.side_effect = side_effect |
| out = build_alternatives_response("ibuprofen", [], max_suggestions=5) |
| assert out["focus_drug"] == "ibuprofen" |
| assert out["therapeutic_class"] == nsaid |
| assert len(out["suggestions"]) >= 1 |
| assert out["suggestions"][0]["display_name"] |
|
|