Initial commit: invest-copilot app
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user