Skip to main content
Glama
by zachegner
test_search_facilities.py19.7 kB
"""Unit tests for search facilities tool.""" import pytest from unittest.mock import AsyncMock, patch from src.tools.search_facilities import search_facilities from src.models.facility import FacilityInfo, FacilityType from src.client import EPAAPIError class TestSearchFacilities: """Test cases for search_facilities function.""" @pytest.mark.asyncio async def test_successful_search_by_facility_name(self): """Test successful search by facility name.""" mock_facilities = [ FacilityInfo( registry_id="12345", name="Test Chemical Plant", address="123 Main St, Test City, NY 12345", city="Test City", state="NY", zip_code="12345", programs=[FacilityType.FRS], naics_code="325199", naics_description="All Other Basic Organic Chemical Manufacturing" ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) result = await search_facilities(facility_name="Chemical") assert len(result) == 1 assert result[0].name == "Test Chemical Plant" assert result[0].state == "NY" # Verify the client was called with correct parameters mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name="Chemical", naics_code=None, state=None, zip_code=None, city=None, limit=100 ) @pytest.mark.asyncio async def test_successful_search_by_state(self): """Test successful search by state.""" mock_facilities = [ FacilityInfo( registry_id="67890", name="NY Manufacturing Co", address="456 Oak Ave, Albany, NY 12201", city="Albany", state="NY", zip_code="12201", programs=[FacilityType.FRS] ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) result = await search_facilities(state="NY") assert len(result) == 1 assert result[0].state == "NY" mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name=None, naics_code=None, state="NY", zip_code=None, city=None, limit=100 ) @pytest.mark.asyncio async def test_successful_search_by_zip_code(self): """Test successful search by ZIP code.""" mock_facilities = [ FacilityInfo( registry_id="11111", name="Local Facility", address="789 Pine St, Boston, MA 02101", city="Boston", state="MA", zip_code="02101", programs=[FacilityType.FRS] ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) result = await search_facilities(zip_code="02101") assert len(result) == 1 assert result[0].zip_code == "02101" mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name=None, naics_code=None, state=None, zip_code="02101", city=None, limit=100 ) @pytest.mark.asyncio async def test_successful_search_by_city(self): """Test successful search by city.""" mock_facilities = [ FacilityInfo( registry_id="22222", name="Chicago Industrial", address="321 Lake St, Chicago, IL 60601", city="Chicago", state="IL", zip_code="60601", programs=[FacilityType.FRS] ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) result = await search_facilities(city="Chicago") assert len(result) == 1 assert result[0].city == "Chicago" mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name=None, naics_code=None, state=None, zip_code=None, city="Chicago", limit=100 ) @pytest.mark.asyncio async def test_successful_search_by_naics_code(self): """Test successful search by NAICS code.""" mock_facilities = [ FacilityInfo( registry_id="33333", name="Chemical Manufacturer", address="555 Industrial Blvd, Houston, TX 77001", city="Houston", state="TX", zip_code="77001", programs=[FacilityType.FRS], naics_code="325199", naics_description="All Other Basic Organic Chemical Manufacturing" ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) result = await search_facilities(naics_code="325199") assert len(result) == 1 assert result[0].naics_code == "325199" mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name=None, naics_code="325199", state=None, zip_code=None, city=None, limit=100 ) @pytest.mark.asyncio async def test_successful_search_multiple_parameters(self): """Test successful search with multiple parameters.""" mock_facilities = [ FacilityInfo( registry_id="44444", name="CA Chemical Plant", address="777 Sunset Blvd, Los Angeles, CA 90001", city="Los Angeles", state="CA", zip_code="90001", programs=[FacilityType.FRS] ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) result = await search_facilities(facility_name="Chemical", state="CA", city="Los Angeles") assert len(result) == 1 assert result[0].name == "CA Chemical Plant" mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name="Chemical", naics_code=None, state="CA", zip_code=None, city="Los Angeles", limit=100 ) @pytest.mark.asyncio async def test_no_search_parameters(self): """Test validation error when no search parameters provided.""" with pytest.raises(ValueError, match="At least one search parameter must be provided"): await search_facilities() @pytest.mark.asyncio async def test_empty_facility_name(self): """Test validation error with empty facility name.""" with pytest.raises(ValueError, match="Facility name cannot be empty"): await search_facilities(facility_name="") with pytest.raises(ValueError, match="Facility name cannot be empty"): await search_facilities(facility_name=" ") @pytest.mark.asyncio async def test_empty_naics_code(self): """Test validation error with empty NAICS code.""" with pytest.raises(ValueError, match="NAICS code cannot be empty"): await search_facilities(naics_code="") with pytest.raises(ValueError, match="NAICS code cannot be empty"): await search_facilities(naics_code=" ") @pytest.mark.asyncio async def test_empty_city(self): """Test validation error with empty city.""" with pytest.raises(ValueError, match="City cannot be empty"): await search_facilities(city="") with pytest.raises(ValueError, match="City cannot be empty"): await search_facilities(city=" ") @pytest.mark.asyncio async def test_invalid_state_code(self): """Test validation error with invalid state code.""" with pytest.raises(ValueError, match="State code must be 2 letters"): await search_facilities(state="CALIFORNIA") with pytest.raises(ValueError, match="State code must be 2 letters"): await search_facilities(state="C") with pytest.raises(ValueError, match="State code must be 2 letters"): await search_facilities(state="123") @pytest.mark.asyncio async def test_invalid_zip_code(self): """Test validation error with invalid ZIP code.""" with pytest.raises(ValueError, match="ZIP code must be 5 digits or less"): await search_facilities(zip_code="123456") with pytest.raises(ValueError, match="ZIP code must be 5 digits or less"): await search_facilities(zip_code="123456789") @pytest.mark.asyncio async def test_invalid_limit(self): """Test validation error with invalid limit.""" with pytest.raises(ValueError, match="Limit must be between 1 and 1000"): await search_facilities(facility_name="Test", limit=0) with pytest.raises(ValueError, match="Limit must be between 1 and 1000"): await search_facilities(facility_name="Test", limit=1001) @pytest.mark.asyncio async def test_state_normalization(self): """Test that state codes are normalized to uppercase.""" with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=[] ) await search_facilities(state="ny") mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name=None, naics_code=None, state="NY", zip_code=None, city=None, limit=100 ) @pytest.mark.asyncio async def test_parameter_trimming(self): """Test that parameters are trimmed of whitespace.""" with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=[] ) await search_facilities( facility_name=" Chemical ", naics_code=" 325199 ", state=" NY ", zip_code=" 12345 ", city=" Test City " ) mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name="Chemical", naics_code="325199", state="NY", zip_code="12345", city="Test City", limit=100 ) @pytest.mark.asyncio async def test_empty_results(self): """Test handling of empty results.""" with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=[] ) result = await search_facilities(facility_name="Nonexistent Facility") assert result == [] @pytest.mark.asyncio async def test_epa_api_error(self): """Test handling of EPA API errors.""" with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( side_effect=EPAAPIError("API connection failed") ) with pytest.raises(Exception, match="Failed to search facilities"): await search_facilities(facility_name="Test") @pytest.mark.asyncio async def test_unexpected_error(self): """Test handling of unexpected errors.""" with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( side_effect=RuntimeError("Unexpected error") ) with pytest.raises(Exception, match="Failed to search facilities"): await search_facilities(facility_name="Test") @pytest.mark.asyncio async def test_zip_code_with_leading_zeros(self): """Test ZIP codes with leading zeros.""" mock_facilities = [ FacilityInfo( registry_id="55555", name="MA Facility", address="123 Main St, Worcester, MA 01510", city="Worcester", state="MA", zip_code="01510", programs=[FacilityType.FRS] ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) # Test with string ZIP code with leading zero result = await search_facilities(zip_code="01510") assert len(result) == 1 assert result[0].zip_code == "01510" mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_with( facility_name=None, naics_code=None, state=None, zip_code="01510", city=None, limit=100 ) @pytest.mark.asyncio async def test_numeric_zip_code_input(self): """Test numeric ZIP code input gets zero-padded.""" mock_facilities = [ FacilityInfo( registry_id="66666", name="Test Facility", address="456 Oak St, Test City, MA 01510", city="Test City", state="MA", zip_code="01510", programs=[FacilityType.FRS] ) ] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) # Test with numeric ZIP code (1510) - should become "01510" result = await search_facilities(zip_code=1510) assert len(result) == 1 mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_with( facility_name=None, naics_code=None, state=None, zip_code="01510", city=None, limit=100 ) @pytest.mark.asyncio async def test_zip_code_edge_cases(self): """Test ZIP code edge cases.""" mock_facilities = [] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) # Test 4-digit ZIP code (should be padded to 5 digits) await search_facilities(zip_code="1234") mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_with( facility_name=None, naics_code=None, state=None, zip_code="01234", city=None, limit=100 ) # Test single digit ZIP code await search_facilities(zip_code="1") mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_with( facility_name=None, naics_code=None, state=None, zip_code="00001", city=None, limit=100 ) # Test ZIP code with non-digit characters await search_facilities(zip_code="123-45") mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_with( facility_name=None, naics_code=None, state=None, zip_code="12345", city=None, limit=100 ) @pytest.mark.asyncio async def test_zip_code_00000(self): """Test edge case ZIP code 00000.""" mock_facilities = [] with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=mock_facilities ) await search_facilities(zip_code="00000") mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_with( facility_name=None, naics_code=None, state=None, zip_code="00000", city=None, limit=100 ) @pytest.mark.asyncio async def test_custom_limit(self): """Test custom limit parameter.""" with patch('src.tools.search_facilities.FRSClient') as mock_client: mock_client.return_value.__aenter__.return_value.search_facilities = AsyncMock( return_value=[] ) await search_facilities(facility_name="Test", limit=50) mock_client.return_value.__aenter__.return_value.search_facilities.assert_called_once_with( facility_name="Test", naics_code=None, state=None, zip_code=None, city=None, limit=50 )

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zachegner/envirofacts-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server