Skip to main content
Glama
conftest.py10 kB
"""Shared pytest fixtures for all tests. Provides common fixtures for testing Hostaway API client, authentication, and MCP protocol endpoints. """ from datetime import UTC from typing import Any from unittest.mock import AsyncMock, MagicMock, patch import httpx import pytest from src.mcp.config import HostawayConfig from src.services.hostaway_client import HostawayClient @pytest.fixture def mock_supabase_client(): """Create a mock Supabase client for usage tracking. This fixture prevents tests from trying to connect to a real Supabase instance. """ mock_client = MagicMock() mock_client.table.return_value.insert.return_value.execute = AsyncMock(return_value=MagicMock()) mock_client.table.return_value.select.return_value.eq.return_value.single.return_value.execute = AsyncMock( return_value=MagicMock(data={"id": "test-org-123", "name": "Test Org"}) ) return mock_client @pytest.fixture(autouse=True) def mock_supabase_for_all_tests(mock_supabase_client, monkeypatch): """Automatically mock Supabase for all tests to prevent connection errors. This autouse fixture ensures no test tries to connect to a real Supabase instance. """ # Set cursor secret for pagination tests monkeypatch.setenv("CURSOR_SECRET", "test-cursor-secret-for-pagination") with patch( "src.services.supabase_client.get_supabase_client", return_value=mock_supabase_client ): with patch( "src.api.middleware.usage_tracking.get_supabase_client", return_value=mock_supabase_client, ): yield @pytest.fixture def test_config(monkeypatch: pytest.MonkeyPatch) -> HostawayConfig: """Create test configuration with environment variables. Returns: HostawayConfig configured for testing """ monkeypatch.setenv("HOSTAWAY_ACCOUNT_ID", "test_account_123") monkeypatch.setenv("HOSTAWAY_SECRET_KEY", "test_secret_key_456") monkeypatch.setenv("HOSTAWAY_API_BASE_URL", "https://api.hostaway.com/v1") return HostawayConfig() # type: ignore[call-arg] @pytest.fixture def mock_http_client() -> AsyncMock: """Create mock httpx.AsyncClient. Returns: Mock AsyncClient for testing HTTP interactions """ mock = AsyncMock(spec=httpx.AsyncClient) mock.aclose = AsyncMock() return mock @pytest.fixture def mock_response() -> MagicMock: """Create mock httpx.Response. Returns: Mock Response for testing API responses """ response = MagicMock(spec=httpx.Response) response.status_code = 200 response.json.return_value = {"status": "success", "result": {}} response.raise_for_status = MagicMock() response.text = "" return response @pytest.fixture def mock_token_manager() -> AsyncMock: """Create mock TokenManager for testing. Returns: Mock TokenManager that returns valid access token """ from src.models.auth import AccessToken mock = AsyncMock() mock.get_token.return_value = AccessToken( access_token="test_token_abc123_xyz789_20chars", # Must be >= 20 chars token_type="Bearer", expires_in=63072000, scope="general", ) mock.invalidate_token = AsyncMock() return mock @pytest.fixture def mock_rate_limiter() -> MagicMock: """Create mock RateLimiter for testing. Returns: Mock RateLimiter that always allows requests """ class MockAcquire: """Mock async context manager for rate limiter.""" async def __aenter__(self) -> None: """Enter context.""" async def __aexit__(self, *args: Any) -> None: """Exit context.""" mock = MagicMock() mock.acquire.return_value = MockAcquire() return mock @pytest.fixture def mock_client( test_config: HostawayConfig, mock_token_manager: AsyncMock, mock_rate_limiter: MagicMock, mock_http_client: AsyncMock, ) -> HostawayClient: """Create mock HostawayClient for testing. Args: test_config: Test configuration mock_token_manager: Mock token manager mock_rate_limiter: Mock rate limiter mock_http_client: Mock HTTP client Returns: HostawayClient configured with mocks for testing """ client = HostawayClient( config=test_config, token_manager=mock_token_manager, rate_limiter=mock_rate_limiter, ) # Replace the internal HTTP client with mock client._client = mock_http_client return client # Financial test data fixtures @pytest.fixture def mock_financial_report_response() -> dict[str, Any]: """Create mock Hostaway financial report response. Returns: Complete financial report data structure """ return { "periodStart": "2025-10-01", "periodEnd": "2025-10-31", "periodType": "monthly", "listingId": None, # All properties "revenue": { "totalRevenue": 12500.00, "directBookings": 3000.00, "airbnb": 6000.00, "vrbo": 2500.00, "bookingCom": 1000.00, "other": 0.00, }, "expenses": { "totalExpenses": 3250.00, "cleaning": 1200.00, "maintenance": 500.00, "utilities": 300.00, "platformFees": 1000.00, "supplies": 200.00, "other": 50.00, }, "netIncome": 9250.00, "totalBookings": 15, "totalNightsBooked": 75, "averageDailyRate": 166.67, "occupancyRate": 80.65, "currency": "USD", } @pytest.fixture def mock_property_financial_response() -> dict[str, Any]: """Create mock property-specific financial report. Returns: Property-specific financial report data """ return { "periodStart": "2025-10-01", "periodEnd": "2025-10-31", "periodType": "monthly", "listingId": 12345, "listingName": "Cozy Downtown Apartment", "revenue": { "totalRevenue": 4500.00, "directBookings": 1500.00, "airbnb": 2500.00, "vrbo": 500.00, "bookingCom": 0.00, "other": 0.00, }, "expenses": { "totalExpenses": 1200.00, "cleaning": 600.00, "maintenance": 200.00, "utilities": 150.00, "platformFees": 200.00, "supplies": 50.00, "other": 0.00, }, "netIncome": 3300.00, "totalBookings": 6, "totalNightsBooked": 28, "averageDailyRate": 160.71, "occupancyRate": 90.32, "currency": "USD", } # Listing test data fixtures @pytest.fixture def mock_listing_response() -> dict[str, Any]: """Create mock listing response. Returns: Complete listing data structure """ return { "id": 12345, "name": "Cozy Downtown Apartment", "listingName": "Cozy Downtown Apartment", "internalName": "Apartment A", "address": "123 Main St, San Francisco, CA 94102", "bedrooms": 2, "bathrooms": 1, "maxGuests": 4, "status": "active", "currency": "USD", "basePrice": 150.00, } @pytest.fixture def mock_listings_response() -> list[dict[str, Any]]: """Create mock listings list response. Returns: List of listing data structures """ return [ { "id": 12345, "name": "Cozy Downtown Apartment", "bedrooms": 2, "bathrooms": 1, "maxGuests": 4, "status": "active", }, { "id": 12346, "name": "Beachfront Villa", "bedrooms": 4, "bathrooms": 3, "maxGuests": 8, "status": "active", }, ] # Booking test data fixtures @pytest.fixture def mock_booking_response() -> dict[str, Any]: """Create mock booking response. Returns: Complete booking data structure """ return { "id": 98765, "listingId": 12345, "channelId": 2001, "channelName": "airbnb", "guestName": "John Doe", "guestEmail": "john.doe@example.com", "checkIn": "2025-10-15", "checkOut": "2025-10-20", "numberOfGuests": 2, "status": "confirmed", "totalPrice": 750.00, "currency": "USD", } @pytest.fixture def mock_bookings_response() -> list[dict[str, Any]]: """Create mock bookings list response. Returns: List of booking data structures """ return [ { "id": 98765, "listingId": 12345, "guestName": "John Doe", "checkIn": "2025-10-15", "checkOut": "2025-10-20", "status": "confirmed", }, { "id": 98766, "listingId": 12346, "guestName": "Jane Smith", "checkIn": "2025-10-22", "checkOut": "2025-10-27", "status": "confirmed", }, ] # Middleware testing fixtures for 008-fix-issues-identified @pytest.fixture def mock_request(): """Create mock FastAPI Request for middleware testing. Returns: Mock Request with correlation_id and state """ from unittest.mock import MagicMock from starlette.requests import Request request = MagicMock(spec=Request) request.state = MagicMock() request.state.correlation_id = "test-correlation-id-12345" request.url = MagicMock() request.url.path = "/api/test" request.headers = {} return request @pytest.fixture def mock_rate_limit_state(): """Create mock rate limit state for testing header calculations. Returns: Dictionary with rate limit state (count, window_start) """ from datetime import datetime return { "count": 5, "window_start": datetime.now(UTC).timestamp(), "limit": 15, }

Latest Blog Posts

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/darrentmorgan/hostaway-mcp'

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