diff --git a/.gitignore b/.gitignore index 88b813c..1914dc0 100644 --- a/.gitignore +++ b/.gitignore @@ -70,4 +70,8 @@ yarn-error.log* # Frontend build frontend/dist/ frontend/build/ -frontend/.astro/ \ No newline at end of file +frontend/.astro/ + +# Temporary refactoring documentation +REFACTORING_*.md +TOOLS_REFACTORING_CHECKLIST.md \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 2c87e59..f3f7e9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,25 @@ mcrentcast-mock-api = "mcrentcast.mock_api:run_mock_server" requires = ["hatchling"] build-backend = "hatchling.build" +[tool.hatch.build.targets.sdist] +exclude = [ + "/.claude", + "/.env", + "/.venv", + "/.git", + "/.pytest_cache", + "/.coverage", + "/reports", + "/data", + "/dist", + "/frontend", + "/REFACTORING_*.md", + "/CLAUDE.md", + "/docker-compose.yml", + "/Dockerfile", + "/Makefile", +] + [tool.ruff] target-version = "py313" line-length = 88 diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index d2c7a8f..e4ad40b 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -50,7 +50,7 @@ from mcrentcast.rentcast_client import ( ) -class TestReporter: +class ReportGenerator: """Enhanced test reporter with syntax highlighting for comprehensive test output.""" def __init__(self, test_name: str): @@ -199,7 +199,7 @@ class TestApiKeyManagement: @pytest.mark.asyncio async def test_set_api_key_success(self, mock_db_manager): """Test successful API key setting.""" - reporter = TestReporter("set_api_key_success") + reporter = ReportGenerator("set_api_key_success") api_key = "test_rentcast_key_123" request = SetApiKeyRequest(api_key=api_key) @@ -229,7 +229,7 @@ class TestApiKeyManagement: @pytest.mark.asyncio async def test_set_api_key_empty(self, mock_db_manager): """Test setting empty API key.""" - reporter = TestReporter("set_api_key_empty") + reporter = ReportGenerator("set_api_key_empty") request = SetApiKeyRequest(api_key="") @@ -256,7 +256,7 @@ class TestPropertySearch: @pytest.mark.asyncio async def test_search_properties_no_api_key(self): """Test property search without API key configured.""" - reporter = TestReporter("search_properties_no_api_key") + reporter = ReportGenerator("search_properties_no_api_key") request = PropertySearchRequest(city="Austin", state="TX") reporter.log_input("search_request", request.model_dump(), "Property search without API key") @@ -279,7 +279,7 @@ class TestPropertySearch: @pytest.mark.asyncio async def test_search_properties_cached_hit(self, mock_db_manager, mock_rentcast_client, sample_property): """Test property search with cache hit.""" - reporter = TestReporter("search_properties_cached_hit") + reporter = ReportGenerator("search_properties_cached_hit") request = PropertySearchRequest(city="Austin", state="TX", limit=5) cache_key = "mock_cache_key_123" @@ -317,7 +317,7 @@ class TestPropertySearch: @pytest.mark.asyncio async def test_search_properties_cache_miss_confirmation(self, mock_db_manager, mock_rentcast_client): """Test property search with cache miss requiring confirmation.""" - reporter = TestReporter("search_properties_cache_miss_confirmation") + reporter = ReportGenerator("search_properties_cache_miss_confirmation") request = PropertySearchRequest(city="Dallas", state="TX") @@ -355,7 +355,7 @@ class TestPropertySearch: @pytest.mark.asyncio async def test_search_properties_confirmed_api_call(self, mock_db_manager, mock_rentcast_client, sample_property): """Test property search with confirmed API call.""" - reporter = TestReporter("search_properties_confirmed_api_call") + reporter = ReportGenerator("search_properties_confirmed_api_call") request = PropertySearchRequest(city="Houston", state="TX", force_refresh=True) @@ -390,7 +390,7 @@ class TestPropertySearch: @pytest.mark.asyncio async def test_search_properties_rate_limit_error(self, mock_db_manager, mock_rentcast_client): """Test property search with rate limit exceeded.""" - reporter = TestReporter("search_properties_rate_limit_error") + reporter = ReportGenerator("search_properties_rate_limit_error") request = PropertySearchRequest(zipCode="90210") @@ -427,7 +427,7 @@ class TestPropertyDetails: @pytest.mark.asyncio async def test_get_property_success(self, mock_db_manager, mock_rentcast_client, sample_property): """Test successful property details retrieval.""" - reporter = TestReporter("get_property_success") + reporter = ReportGenerator("get_property_success") property_id = "prop_123" request = PropertyByIdRequest(property_id=property_id) @@ -461,7 +461,7 @@ class TestPropertyDetails: @pytest.mark.asyncio async def test_get_property_not_found(self, mock_db_manager, mock_rentcast_client): """Test property not found scenario.""" - reporter = TestReporter("get_property_not_found") + reporter = ReportGenerator("get_property_not_found") request = PropertyByIdRequest(property_id="nonexistent_123") @@ -494,7 +494,7 @@ class TestValueEstimation: @pytest.mark.asyncio async def test_get_value_estimate_success(self, mock_db_manager, mock_rentcast_client): """Test successful value estimation.""" - reporter = TestReporter("get_value_estimate_success") + reporter = ReportGenerator("get_value_estimate_success") address = "456 Oak Ave, Austin, TX" request = ValueEstimateRequest(address=address) @@ -539,7 +539,7 @@ class TestValueEstimation: @pytest.mark.asyncio async def test_get_value_estimate_unavailable(self, mock_db_manager, mock_rentcast_client): """Test value estimation when data is unavailable.""" - reporter = TestReporter("get_value_estimate_unavailable") + reporter = ReportGenerator("get_value_estimate_unavailable") request = ValueEstimateRequest(address="999 Unknown St, Middle, NV") @@ -572,7 +572,7 @@ class TestRentEstimation: @pytest.mark.asyncio async def test_get_rent_estimate_full_params(self, mock_db_manager, mock_rentcast_client): """Test rent estimation with full parameters.""" - reporter = TestReporter("get_rent_estimate_full_params") + reporter = ReportGenerator("get_rent_estimate_full_params") request = RentEstimateRequest( address="789 Elm St, Dallas, TX", @@ -620,7 +620,7 @@ class TestRentEstimation: @pytest.mark.asyncio async def test_get_rent_estimate_minimal_params(self, mock_db_manager, mock_rentcast_client): """Test rent estimation with minimal parameters.""" - reporter = TestReporter("get_rent_estimate_minimal_params") + reporter = ReportGenerator("get_rent_estimate_minimal_params") request = RentEstimateRequest(address="321 Pine St, Austin, TX") @@ -663,7 +663,7 @@ class TestListings: @pytest.mark.asyncio async def test_search_sale_listings(self, mock_db_manager, mock_rentcast_client): """Test searching sale listings.""" - reporter = TestReporter("search_sale_listings") + reporter = ReportGenerator("search_sale_listings") request = ListingSearchRequest(city="San Antonio", state="TX", limit=3) @@ -722,7 +722,7 @@ class TestListings: @pytest.mark.asyncio async def test_search_rental_listings(self, mock_db_manager, mock_rentcast_client): """Test searching rental listings.""" - reporter = TestReporter("search_rental_listings") + reporter = ReportGenerator("search_rental_listings") request = ListingSearchRequest(zipCode="78701", limit=2) @@ -774,7 +774,7 @@ class TestMarketStatistics: @pytest.mark.asyncio async def test_get_market_statistics_city(self, mock_db_manager, mock_rentcast_client): """Test market statistics by city.""" - reporter = TestReporter("get_market_statistics_city") + reporter = ReportGenerator("get_market_statistics_city") request = MarketStatsRequest(city="Austin", state="TX") @@ -821,7 +821,7 @@ class TestMarketStatistics: @pytest.mark.asyncio async def test_get_market_statistics_zipcode(self, mock_db_manager, mock_rentcast_client): """Test market statistics by ZIP code.""" - reporter = TestReporter("get_market_statistics_zipcode") + reporter = ReportGenerator("get_market_statistics_zipcode") request = MarketStatsRequest(zipCode="90210") @@ -868,7 +868,7 @@ class TestCacheManagement: @pytest.mark.asyncio async def test_get_cache_stats_comprehensive(self, mock_db_manager, sample_cache_stats): """Test comprehensive cache statistics retrieval.""" - reporter = TestReporter("get_cache_stats_comprehensive") + reporter = ReportGenerator("get_cache_stats_comprehensive") reporter.log_input("cache_request", "get_cache_stats", "Comprehensive cache statistics") @@ -898,7 +898,7 @@ class TestCacheManagement: @pytest.mark.asyncio async def test_expire_cache_specific_key(self, mock_db_manager): """Test expiring specific cache key.""" - reporter = TestReporter("expire_cache_specific_key") + reporter = ReportGenerator("expire_cache_specific_key") cache_key = "property_records_austin_tx_123456" request = ExpireCacheRequest(cache_key=cache_key) @@ -926,7 +926,7 @@ class TestCacheManagement: @pytest.mark.asyncio async def test_expire_cache_all(self, mock_db_manager): """Test expiring all cache entries.""" - reporter = TestReporter("expire_cache_all") + reporter = ReportGenerator("expire_cache_all") request = ExpireCacheRequest(all=True) @@ -953,7 +953,7 @@ class TestCacheManagement: @pytest.mark.asyncio async def test_expire_cache_nonexistent_key(self, mock_db_manager): """Test expiring nonexistent cache key.""" - reporter = TestReporter("expire_cache_nonexistent_key") + reporter = ReportGenerator("expire_cache_nonexistent_key") request = ExpireCacheRequest(cache_key="nonexistent_key_999") @@ -983,7 +983,7 @@ class TestUsageAndLimits: @pytest.mark.asyncio async def test_get_usage_stats_default(self, mock_db_manager): """Test getting usage statistics with default period.""" - reporter = TestReporter("get_usage_stats_default") + reporter = ReportGenerator("get_usage_stats_default") reporter.log_input("usage_request", {"days": 30}, "Default 30-day usage statistics") @@ -1024,7 +1024,7 @@ class TestUsageAndLimits: @pytest.mark.asyncio async def test_set_api_limits_comprehensive(self, mock_db_manager): """Test setting comprehensive API limits.""" - reporter = TestReporter("set_api_limits_comprehensive") + reporter = ReportGenerator("set_api_limits_comprehensive") request = SetLimitsRequest( daily_limit=200, @@ -1068,7 +1068,7 @@ class TestUsageAndLimits: @pytest.mark.asyncio async def test_get_api_limits_with_usage(self, mock_db_manager): """Test getting API limits with current usage.""" - reporter = TestReporter("get_api_limits_with_usage") + reporter = ReportGenerator("get_api_limits_with_usage") reporter.log_input("limits_request", "get_api_limits", "Current limits and usage") @@ -1106,7 +1106,7 @@ class TestUsageAndLimits: @pytest.mark.asyncio async def test_set_api_limits_partial(self, mock_db_manager): """Test setting partial API limits.""" - reporter = TestReporter("set_api_limits_partial") + reporter = ReportGenerator("set_api_limits_partial") request = SetLimitsRequest(requests_per_minute=10) # Only update rate limit @@ -1142,7 +1142,7 @@ class TestErrorHandling: @pytest.mark.asyncio async def test_api_error_handling(self, mock_db_manager, mock_rentcast_client): """Test API error handling.""" - reporter = TestReporter("api_error_handling") + reporter = ReportGenerator("api_error_handling") request = PropertySearchRequest(city="TestCity", state="TX") @@ -1174,7 +1174,7 @@ class TestErrorHandling: @pytest.mark.asyncio async def test_database_error_handling(self, mock_db_manager): """Test database error handling.""" - reporter = TestReporter("database_error_handling") + reporter = ReportGenerator("database_error_handling") reporter.log_input("db_error_request", "get_cache_stats", "Database error simulation") @@ -1203,7 +1203,7 @@ class TestRateLimiting: @pytest.mark.asyncio async def test_rate_limit_backoff(self, mock_db_manager, mock_rentcast_client): """Test exponential backoff on rate limits.""" - reporter = TestReporter("rate_limit_backoff") + reporter = ReportGenerator("rate_limit_backoff") request = PropertySearchRequest(city="TestCity", state="CA") @@ -1233,7 +1233,7 @@ class TestRateLimiting: @pytest.mark.asyncio async def test_concurrent_requests_rate_limiting(self, mock_db_manager, mock_rentcast_client): """Test rate limiting with concurrent requests.""" - reporter = TestReporter("concurrent_requests_rate_limiting") + reporter = ReportGenerator("concurrent_requests_rate_limiting") # Create multiple concurrent requests requests = [ @@ -1279,7 +1279,7 @@ class TestMockApiMode: @pytest.mark.asyncio async def test_mock_api_mode_property_search(self, mock_db_manager): """Test property search in mock API mode.""" - reporter = TestReporter("mock_api_mode_property_search") + reporter = ReportGenerator("mock_api_mode_property_search") request = PropertySearchRequest(city="MockCity", state="TX") @@ -1321,7 +1321,7 @@ class TestSmokeTests: @pytest.mark.asyncio async def test_all_tools_exist(self): """Test that all 13 expected tools exist.""" - reporter = TestReporter("all_tools_exist") + reporter = ReportGenerator("all_tools_exist") expected_tools = [ "set_api_key", @@ -1367,7 +1367,7 @@ class TestSmokeTests: @pytest.mark.asyncio async def test_basic_server_functionality(self): """Test basic server functionality without external dependencies.""" - reporter = TestReporter("basic_server_functionality") + reporter = ReportGenerator("basic_server_functionality") reporter.log_processing_step("server_check", "Verifying basic server setup") diff --git a/tests/test_server.py b/tests/test_server.py index 3eeb011..07454b9 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -3,13 +3,13 @@ import pytest from unittest.mock import AsyncMock, MagicMock, patch -from src.mcrentcast.server import ( +from mcrentcast.server import ( app, SetApiKeyRequest, PropertySearchRequest, ExpireCacheRequest, ) -from src.mcrentcast.models import PropertyRecord +from mcrentcast.models import PropertyRecord @pytest.mark.asyncio @@ -17,7 +17,7 @@ async def test_set_api_key(): """Test setting API key.""" request = SetApiKeyRequest(api_key="test_api_key_123") - with patch("src.mcrentcast.server.db_manager") as mock_db: + with patch("mcrentcast.server.db_manager") as mock_db: mock_db.set_config = AsyncMock() result = await app.tools["set_api_key"](request) @@ -32,7 +32,7 @@ async def test_search_properties_no_api_key(): """Test searching properties without API key.""" request = PropertySearchRequest(city="Austin", state="TX") - with patch("src.mcrentcast.server.check_api_key", return_value=False): + with patch("mcrentcast.server.check_api_key", return_value=False): result = await app.tools["search_properties"](request) assert "error" in result @@ -52,15 +52,15 @@ async def test_search_properties_cached(): zipCode="78701" ) - with patch("src.mcrentcast.server.check_api_key", return_value=True), \ - patch("src.mcrentcast.server.get_rentcast_client") as mock_client_getter: + with patch("mcrentcast.server.check_api_key", return_value=True), \ + patch("mcrentcast.server.get_rentcast_client") as mock_client_getter: mock_client = MagicMock() mock_client._create_cache_key.return_value = "test_cache_key" mock_client.get_property_records = AsyncMock(return_value=([mock_property], True, 12.5)) mock_client_getter.return_value = mock_client - with patch("src.mcrentcast.server.db_manager") as mock_db: + with patch("mcrentcast.server.db_manager") as mock_db: mock_db.get_cache_entry = AsyncMock(return_value=MagicMock()) result = await app.tools["search_properties"](request) @@ -76,7 +76,7 @@ async def test_expire_cache(): """Test expiring cache entries.""" request = ExpireCacheRequest(cache_key="test_key") - with patch("src.mcrentcast.server.db_manager") as mock_db: + with patch("mcrentcast.server.db_manager") as mock_db: mock_db.expire_cache_entry = AsyncMock(return_value=True) result = await app.tools["expire_cache"](request) @@ -88,7 +88,7 @@ async def test_expire_cache(): @pytest.mark.asyncio async def test_get_cache_stats(): """Test getting cache statistics.""" - from src.mcrentcast.models import CacheStats + from mcrentcast.models import CacheStats mock_stats = CacheStats( total_entries=100, @@ -98,7 +98,7 @@ async def test_get_cache_stats(): hit_rate=80.0 ) - with patch("src.mcrentcast.server.db_manager") as mock_db: + with patch("mcrentcast.server.db_manager") as mock_db: mock_db.get_cache_stats = AsyncMock(return_value=mock_stats) result = await app.tools["get_cache_stats"]() @@ -111,9 +111,9 @@ async def test_get_cache_stats(): @pytest.mark.asyncio async def test_health_check(): """Test health check endpoint.""" - from src.mcrentcast.server import health_check + from mcrentcast.server import health_check - with patch("src.mcrentcast.server.settings") as mock_settings: + with patch("mcrentcast.server.settings") as mock_settings: mock_settings.validate_api_key.return_value = True mock_settings.mode = "development"