"""Unit tests for PubMed tool."""
from unittest.mock import AsyncMock, MagicMock
import pytest
from src.tools.pubmed import PubMedTool
# Sample PubMed XML response for mocking
SAMPLE_PUBMED_XML = """
12345678
Testosterone Therapy for HSDD
Testosterone shows efficacy in HSDD...
Smith
John
2024
01
"""
class TestPubMedTool:
"""Tests for PubMedTool."""
@pytest.mark.asyncio
async def test_search_returns_evidence(self, mocker):
"""PubMedTool should return Evidence objects from search."""
# Mock the HTTP responses
mock_search_response = MagicMock()
mock_search_response.json.return_value = {"esearchresult": {"idlist": ["12345678"]}}
mock_search_response.raise_for_status = MagicMock()
mock_fetch_xml = """
12345678
Testosterone and Libido
Testosterone improves libido.
DoeJohn
2024
12345678
"""
mock_fetch_response = MagicMock()
mock_fetch_response.text = mock_fetch_xml
mock_fetch_response.raise_for_status = MagicMock()
mock_client = AsyncMock()
mock_client.get = AsyncMock(side_effect=[mock_search_response, mock_fetch_response])
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mocker.patch("httpx.AsyncClient", return_value=mock_client)
# Act
tool = PubMedTool()
results = await tool.search("testosterone libido")
# Assert
assert len(results) == 1
assert results[0].citation.source == "pubmed"
assert "Testosterone" in results[0].citation.title
assert "12345678" in results[0].citation.url
@pytest.mark.asyncio
async def test_search_empty_results(self, mocker):
"""PubMedTool should return empty list when no results."""
mock_response = MagicMock()
mock_response.json.return_value = {"esearchresult": {"idlist": []}}
mock_response.raise_for_status = MagicMock()
mock_client = AsyncMock()
mock_client.get = AsyncMock(return_value=mock_response)
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mocker.patch("httpx.AsyncClient", return_value=mock_client)
tool = PubMedTool()
results = await tool.search("xyznonexistentquery123")
assert results == []
def test_parse_pubmed_xml(self):
"""PubMedTool should correctly parse XML."""
tool = PubMedTool()
results = tool._parse_pubmed_xml(SAMPLE_PUBMED_XML)
assert len(results) == 1
assert results[0].citation.source == "pubmed"
assert "Smith John" in results[0].citation.authors
@pytest.mark.asyncio
async def test_search_preprocesses_query(self, mocker):
"""Test that queries are preprocessed before search."""
mock_search_response = MagicMock()
mock_search_response.json.return_value = {"esearchresult": {"idlist": []}}
mock_search_response.raise_for_status = MagicMock()
mock_client = AsyncMock()
mock_client.get = AsyncMock(return_value=mock_search_response)
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mocker.patch("httpx.AsyncClient", return_value=mock_client)
tool = PubMedTool()
await tool.search("What medications help with Low Libido?")
# Verify call args
call_args = mock_client.get.call_args
params = call_args[1]["params"]
term = params["term"]
# "what" and "help" should be stripped
assert "what" not in term.lower()
assert "help" not in term.lower()
# "low libido" should be expanded
assert "HSDD" in term or "hypoactive" in term