"""Test fixtures for API endpoint tests. Provides fixtures for testing FastAPI endpoints with mocked dependencies. """ from contextlib import asynccontextmanager from datetime import UTC, datetime from unittest.mock import MagicMock, patch from uuid import uuid4 import fakeredis.aioredis import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from sqlalchemy.ext.asyncio import AsyncSession from app.api import deps as api_deps from app.api.auth import router as auth_router from app.api.users import router as users_router from app.db.models import User from app.services.jwt_service import create_access_token, create_refresh_token @pytest.fixture def fake_redis(): """Provide a fake Redis instance for testing.""" return fakeredis.aioredis.FakeRedis(decode_responses=True) @pytest.fixture def mock_get_redis(fake_redis): """Mock the get_redis context manager to use fake Redis.""" @asynccontextmanager async def _mock_get_redis(): yield fake_redis return _mock_get_redis @pytest.fixture def test_user(): """Create a test user object. Returns a User model instance that can be used in tests. The user is not persisted to database. """ user = User( email="test@example.com", display_name="Test User", avatar_url="https://example.com/avatar.jpg", oauth_provider="google", oauth_id="google-123", is_premium=False, premium_until=None, ) # Manually set the ID since we're not using database user.id = str(uuid4()) user.created_at = datetime.now(UTC) user.updated_at = datetime.now(UTC) user.last_login = None user.linked_accounts = [] return user @pytest.fixture def premium_user(test_user): """Create a premium test user.""" from datetime import timedelta test_user.is_premium = True test_user.premium_until = datetime.now(UTC) + timedelta(days=30) return test_user @pytest.fixture def access_token(test_user): """Create a valid access token for the test user.""" from uuid import UUID user_id = UUID(test_user.id) if isinstance(test_user.id, str) else test_user.id return create_access_token(user_id) @pytest.fixture def refresh_token_data(test_user): """Create a valid refresh token and JTI for the test user.""" from uuid import UUID user_id = UUID(test_user.id) if isinstance(test_user.id, str) else test_user.id token, jti = create_refresh_token(user_id) return {"token": token, "jti": jti, "user_id": user_id} @pytest.fixture def mock_db_session(): """Create a mock database session.""" return MagicMock(spec=AsyncSession) @pytest.fixture def app(mock_get_redis, mock_db_session): """Create a test FastAPI app with mocked Redis and DB. This creates a minimal app with just the auth and users routers, with Redis and database mocked. """ # Create test app (no lifespan since we're mocking everything) test_app = FastAPI() test_app.include_router(auth_router, prefix="/api") test_app.include_router(users_router, prefix="/api") # Override get_db dependency to return mock session async def override_get_db(): yield mock_db_session test_app.dependency_overrides[api_deps.get_db] = override_get_db # Patch get_redis globally for this app with ( patch("app.api.auth.get_redis", mock_get_redis), patch("app.services.token_store.get_redis", mock_get_redis), ): yield test_app # Clean up overrides test_app.dependency_overrides.clear() @pytest.fixture def client(app): """Create a test client for the app.""" return TestClient(app)