"""Shared fixtures for all backend tests. CRITICAL: The database mock must be injected into sys.modules BEFORE any other imports that might trigger database.py to load. This is why the mock is created at module level (lines 13-27) before any imports. """ import asyncio import sys import types from unittest.mock import AsyncMock import pytest from httpx import AsyncClient, ASGITransport # ── Mock database module (MUST be before any imports) ────────────── _db_mock = AsyncMock() _db_mock.execute_query = AsyncMock(return_value=[]) _db_mock.execute_command = AsyncMock(return_value=None) _db_mock.execute_one = AsyncMock(return_value=None) _db_mock.init_db = AsyncMock() _db_mock.close_db = AsyncMock() _db_mock.get_connection = AsyncMock() _db_mock.release_connection = AsyncMock() _db_mod = types.ModuleType("database") _db_mod.execute_query = _db_mock.execute_query _db_mod.execute_command = _db_mock.execute_command _db_mod.execute_one = _db_mock.execute_one _db_mod.init_db = _db_mock.init_db _db_mod.close_db = _db_mock.close_db _db_mod.get_connection = _db_mock.get_connection _db_mod.release_connection = _db_mock.release_connection sys.modules["database"] = _db_mod # ──────────────────────────────────────────────────────────────────── @pytest.fixture def event_loop(): """Create a fresh event loop for each test.""" loop = asyncio.new_event_loop() yield loop loop.close() @pytest.fixture def mock_db(): """Return the shared mock database object.""" return _db_mock @pytest.fixture def mock_user_data(): """Valid user registration data.""" return { "email": "test@example.com", "password": "SecurePass123!", "name": "Test User", } @pytest.fixture def mock_watchlist_data(): """Valid watchlist creation data.""" return { "name": "My Watchlist", "description": "Test watchlist for AAPL", } @pytest.fixture def mock_watchlist_item(): """Valid watchlist item data.""" return { "ticker": "AAPL", "type": "stock", "notes": "Apple Inc.", } @pytest.fixture def mock_stock_profile(): """Mock stock profile data.""" return { "ticker": "AAPL", "name": "Apple Inc.", "sector": "Technology", "industry": "Consumer Electronics", "pe_ratio": 32.5, "market_cap": 3_000_000_000_000, "price": 180.0, "change_pct": 2.5, "volume": 50_000_000, "description": "Apple Inc. designs, manufactures, and sells consumer electronics.", } @pytest.fixture def mock_price_data(): """Mock price history data.""" return [ { "date": "2024-01-02", "open": 185.0, "high": 187.0, "low": 183.0, "close": 186.5, "volume": 50_000_000, }, { "date": "2024-01-03", "open": 186.5, "high": 189.0, "low": 185.0, "close": 187.0, "volume": 52_000_000, }, ] @pytest.fixture def mock_search_results(): """Mock search results.""" return [ { "ticker": "AAPL", "name": "Apple Inc.", "sector": "Technology", "exchange": "NASDAQ", }, { "ticker": "AAPL34.SA", "name": "Apple Inc. BDR", "sector": "Technology", "exchange": "BVMF", }, ] @pytest.fixture def test_user(mock_db): """Create a test user with a valid JWT token.""" from services.auth import create_access_token token = create_access_token( data={"sub": "0000-01", "email": "test@example.com"}, ) return { "token": token, "user_id": "0000-01", "email": "test@example.com", "name": "Test User", } @pytest.fixture def app(mock_db): """Create a FastAPI app with mocked database.""" # At this point, sys.modules["database"] is already patched (module-level). # Import main — it will pick up our mock. import main return main.app @pytest.fixture async def client(app, mock_db): """Create an async test client with mocked database.""" transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as ac: yield ac