170 lines
4.4 KiB
Plaintext
170 lines
4.4 KiB
Plaintext
"""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
|