test_drug_recalls.py•13.3 kB
"""Tests for FDA drug recall search and retrieval."""
from unittest.mock import patch
import pytest
from biomcp.openfda.drug_recalls import (
get_drug_recall,
search_drug_recalls,
)
class TestDrugRecalls:
"""Test FDA drug recall functions."""
@pytest.mark.asyncio
async def test_search_drug_recalls_success(self):
"""Test successful drug recall search."""
mock_response = {
"meta": {"results": {"skip": 0, "limit": 10, "total": 2}},
"results": [
{
"recall_number": "D-123-2024",
"status": "Ongoing",
"classification": "Class II",
"product_description": "Metformin HCl Extended Release Tablets, 500mg",
"reason_for_recall": "Presence of N-Nitrosodimethylamine (NDMA) impurity above acceptable limits",
"recalling_firm": "Generic Pharma Inc",
"city": "New York",
"state": "NY",
"country": "United States",
"recall_initiation_date": "20240115",
"center_classification_date": "20240120",
"termination_date": "",
"report_date": "20240125",
"code_info": "Lot# ABC123, EXP 06/2025",
"product_quantity": "50,000 bottles",
"distribution_pattern": "Nationwide",
"voluntary_mandated": "Voluntary: Firm Initiated",
"initial_firm_notification": "Letter",
},
{
"recall_number": "D-456-2024",
"status": "Terminated",
"classification": "Class I",
"product_description": "Valsartan Tablets, 160mg",
"reason_for_recall": "Contamination with carcinogenic impurity",
"recalling_firm": "BigPharma Corp",
"city": "Los Angeles",
"state": "CA",
"country": "United States",
"recall_initiation_date": "20240101",
"termination_date": "20240201",
"report_date": "20240105",
},
],
}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
result = await search_drug_recalls(drug="metformin", limit=10)
# Check that result contains expected recall information
assert "D-123-2024" in result
assert "Metformin" in result
assert "Class II" in result
assert "NDMA" in result
assert "Generic Pharma Inc" in result
# Check for disclaimer
assert "FDA Data Notice" in result
# Check summary statistics
assert "Total Recalls Found**: 2 recalls" in result
assert "Ongoing" in result
@pytest.mark.asyncio
async def test_search_drug_recalls_by_classification(self):
"""Test drug recall search filtered by classification."""
mock_response = {
"meta": {"results": {"skip": 0, "limit": 10, "total": 3}},
"results": [
{
"recall_number": "D-001-2024",
"classification": "Class I",
"product_description": "Critical Drug A",
"reason_for_recall": "Life-threatening contamination",
"status": "Ongoing",
},
{
"recall_number": "D-002-2024",
"classification": "Class I",
"product_description": "Critical Drug B",
"reason_for_recall": "Severe adverse reactions",
"status": "Ongoing",
},
],
}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
result = await search_drug_recalls(
recall_class="Class I", limit=10
)
assert "Class I" in result
assert "Total Recalls Found**: 3 recalls" in result
assert "Life-threatening" in result
assert "🔴 **Class I**" in result # High severity indicator
@pytest.mark.asyncio
async def test_search_drug_recalls_no_results(self):
"""Test drug recall search with no results."""
mock_response = {
"meta": {"results": {"skip": 0, "limit": 10, "total": 0}},
"results": [],
}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
result = await search_drug_recalls(
drug="nonexistentdrug999", limit=10
)
assert "No drug recall records found" in result
@pytest.mark.asyncio
async def test_get_drug_recall_success(self):
"""Test successful retrieval of specific drug recall."""
mock_response = {
"results": [
{
"recall_number": "D-123-2024",
"status": "Ongoing",
"classification": "Class II",
"product_description": "Metformin HCl Extended Release Tablets, 500mg, 90 count bottles",
"reason_for_recall": "Presence of N-Nitrosodimethylamine (NDMA) impurity above the acceptable daily intake limit of 96 ng/day",
"recalling_firm": "Generic Pharma Inc",
"address1": "123 Pharma Street",
"city": "New York",
"state": "NY",
"postal_code": "10001",
"country": "United States",
"recall_initiation_date": "20240115",
"center_classification_date": "20240120",
"report_date": "20240125",
"code_info": "Lot Numbers: ABC123 (EXP 06/2025), DEF456 (EXP 07/2025), GHI789 (EXP 08/2025)",
"product_quantity": "50,000 bottles",
"distribution_pattern": "Nationwide distribution to pharmacies and distributors",
"voluntary_mandated": "Voluntary: Firm Initiated",
"initial_firm_notification": "Letter",
"openfda": {
"application_number": ["ANDA123456"],
"brand_name": ["METFORMIN HCL ER"],
"generic_name": ["METFORMIN HYDROCHLORIDE"],
"manufacturer_name": ["GENERIC PHARMA INC"],
"product_ndc": ["12345-678-90"],
"product_type": ["HUMAN PRESCRIPTION DRUG"],
"route": ["ORAL"],
"substance_name": ["METFORMIN HYDROCHLORIDE"],
},
}
]
}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
result = await get_drug_recall("D-123-2024")
# Check basic information
assert "D-123-2024" in result
assert "Class II" in result
assert "Metformin" in result
assert "NDMA" in result
# Check detailed information
assert "Generic Pharma Inc" in result
assert "New York, NY" in result
assert "ABC123" in result
assert "50,000 bottles" in result
assert "Nationwide" in result
# Check dates (should be formatted)
assert "2024-01-15" in result # Formatted date
# Check OpenFDA enrichment
assert "METFORMIN HYDROCHLORIDE" in result
assert "ORAL" in result
# Check disclaimer
assert "FDA Data Notice" in result
@pytest.mark.asyncio
async def test_get_drug_recall_not_found(self):
"""Test retrieval of non-existent drug recall."""
mock_response = {"results": []}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
result = await get_drug_recall("INVALID-RECALL-999")
assert "No recall record found" in result
assert "INVALID-RECALL-999" in result
@pytest.mark.asyncio
async def test_search_drug_recalls_api_error(self):
"""Test drug recall search with API error."""
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (None, "Connection timeout")
result = await search_drug_recalls(drug="aspirin")
assert "Error searching drug recalls" in result
assert "Connection timeout" in result
@pytest.mark.asyncio
async def test_search_by_recalling_firm(self):
"""Test drug recall search by recalling firm."""
mock_response = {
"meta": {"results": {"skip": 0, "limit": 10, "total": 5}},
"results": [
{
"recall_number": f"D-{i:03d}-2024",
"recalling_firm": "Pfizer Inc",
"product_description": f"Product {i}",
"classification": "Class II",
"status": "Ongoing",
}
for i in range(1, 6)
],
}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
# Function doesn't support recalling_firm parameter
# Test with drug parameter instead
result = await search_drug_recalls(drug="aspirin", limit=10)
# Just verify the results format
assert "Pfizer Inc" in result # From mock data
assert "Total Recalls Found**: 5 recalls" in result
@pytest.mark.asyncio
async def test_search_ongoing_recalls(self):
"""Test search for ongoing recalls only."""
mock_response = {
"meta": {"results": {"skip": 0, "limit": 10, "total": 8}},
"results": [
{
"recall_number": "D-100-2024",
"status": "Ongoing",
"classification": "Class II",
"product_description": "Active Recall Product",
"recall_initiation_date": "20240201",
}
],
}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
result = await search_drug_recalls(status="Ongoing", limit=10)
assert "Ongoing" in result
assert "Total Recalls Found**: 8 recalls" in result
assert "Active Recall Product" in result
def test_recall_classification_validation(self):
"""Test validation of recall classification values."""
from biomcp.openfda.validation import validate_recall
# Valid recall with proper classification
valid_recall = {
"recall_number": "D-123-2024",
"classification": "Class II",
"product_description": "Test Product",
}
assert validate_recall(valid_recall) is True
# Invalid classification should log warning but not fail
invalid_recall = {
"recall_number": "D-456-2024",
"classification": "Class IV", # Invalid class
"product_description": "Test Product",
}
# Should still return True but log warning
assert validate_recall(invalid_recall) is True
@pytest.mark.asyncio
async def test_recall_summary_statistics(self):
"""Test that recall search provides proper summary statistics."""
mock_response = {
"meta": {"results": {"skip": 0, "limit": 100, "total": 15}},
"results": [
{"classification": "Class I", "status": "Ongoing"}
for _ in range(3)
]
+ [
{"classification": "Class II", "status": "Ongoing"}
for _ in range(7)
]
+ [
{"classification": "Class III", "status": "Terminated"}
for _ in range(5)
],
}
with patch(
"biomcp.openfda.drug_recalls.make_openfda_request"
) as mock_request:
mock_request.return_value = (mock_response, None)
result = await search_drug_recalls(limit=100)
# Should show classification breakdown
assert "Class I" in result
assert "Class II" in result
assert "Class III" in result
# Should show status summary
assert "Ongoing" in result
assert "Terminated" in result