mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-15 06:27:23 -04:00
test: update tests after removing _write_results_to_db()
- Updated create_mock_agent() to remove references to deleted methods (get_positions, get_last_trade, get_current_prices) - Replaced position/holdings write tests with initial position creation test - Added set_context AsyncMock to properly test async agent flow - Skipped deprecated tests that verified removed _write_results_to_db() and _calculate_portfolio_value() methods - All model_day_executor tests now pass (11 passed, 3 skipped)
This commit is contained in:
@@ -18,21 +18,21 @@ from unittest.mock import Mock, patch, MagicMock, AsyncMock
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def create_mock_agent(positions=None, last_trade=None, current_prices=None,
|
def create_mock_agent(reasoning_steps=None, tool_usage=None, session_result=None,
|
||||||
reasoning_steps=None, tool_usage=None, session_result=None,
|
|
||||||
conversation_history=None):
|
conversation_history=None):
|
||||||
"""Helper to create properly mocked agent."""
|
"""Helper to create properly mocked agent."""
|
||||||
mock_agent = Mock()
|
mock_agent = Mock()
|
||||||
|
|
||||||
# Default values
|
# Note: Removed get_positions, get_last_trade, get_current_prices
|
||||||
mock_agent.get_positions.return_value = positions or {"CASH": 10000.0}
|
# These methods don't exist in BaseAgent and were only used by
|
||||||
mock_agent.get_last_trade.return_value = last_trade
|
# the now-deleted _write_results_to_db() method
|
||||||
mock_agent.get_current_prices.return_value = current_prices or {}
|
|
||||||
mock_agent.get_reasoning_steps.return_value = reasoning_steps or []
|
mock_agent.get_reasoning_steps.return_value = reasoning_steps or []
|
||||||
mock_agent.get_tool_usage.return_value = tool_usage or {}
|
mock_agent.get_tool_usage.return_value = tool_usage or {}
|
||||||
mock_agent.get_conversation_history.return_value = conversation_history or []
|
mock_agent.get_conversation_history.return_value = conversation_history or []
|
||||||
|
|
||||||
# Async methods - use AsyncMock
|
# Async methods - use AsyncMock
|
||||||
|
mock_agent.set_context = AsyncMock()
|
||||||
mock_agent.run_trading_session = AsyncMock(return_value=session_result or {"success": True})
|
mock_agent.run_trading_session = AsyncMock(return_value=session_result or {"success": True})
|
||||||
mock_agent.generate_summary = AsyncMock(return_value="Mock summary")
|
mock_agent.generate_summary = AsyncMock(return_value="Mock summary")
|
||||||
mock_agent.summarize_message = AsyncMock(return_value="Mock message summary")
|
mock_agent.summarize_message = AsyncMock(return_value="Mock message summary")
|
||||||
@@ -93,23 +93,33 @@ class TestModelDayExecutorInitialization:
|
|||||||
class TestModelDayExecutorExecution:
|
class TestModelDayExecutorExecution:
|
||||||
"""Test trading session execution."""
|
"""Test trading session execution."""
|
||||||
|
|
||||||
def test_execute_success(self, clean_db, sample_job_data):
|
def test_execute_success(self, clean_db, sample_job_data, tmp_path):
|
||||||
"""Should execute trading session and write results to DB."""
|
"""Should execute trading session and write results to DB."""
|
||||||
from api.model_day_executor import ModelDayExecutor
|
from api.model_day_executor import ModelDayExecutor
|
||||||
from api.job_manager import JobManager
|
from api.job_manager import JobManager
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Create a temporary config file
|
||||||
|
config_path = tmp_path / "test_config.json"
|
||||||
|
config_data = {
|
||||||
|
"agent_type": "BaseAgent",
|
||||||
|
"models": [],
|
||||||
|
"agent_config": {
|
||||||
|
"initial_cash": 10000.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config_path.write_text(json.dumps(config_data))
|
||||||
|
|
||||||
# Create job and job_detail
|
# Create job and job_detail
|
||||||
manager = JobManager(db_path=clean_db)
|
manager = JobManager(db_path=clean_db)
|
||||||
job_id = manager.create_job(
|
job_id = manager.create_job(
|
||||||
config_path="configs/test.json",
|
config_path=str(config_path),
|
||||||
date_range=["2025-01-16"],
|
date_range=["2025-01-16"],
|
||||||
models=["gpt-5"]
|
models=["gpt-5"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mock agent execution
|
# Mock agent execution
|
||||||
mock_agent = create_mock_agent(
|
mock_agent = create_mock_agent(
|
||||||
positions={"AAPL": 10, "CASH": 7500.0},
|
|
||||||
current_prices={"AAPL": 250.0},
|
|
||||||
session_result={"success": True, "total_steps": 15, "stop_signal_received": True}
|
session_result={"success": True, "total_steps": 15, "stop_signal_received": True}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -122,7 +132,7 @@ class TestModelDayExecutorExecution:
|
|||||||
job_id=job_id,
|
job_id=job_id,
|
||||||
date="2025-01-16",
|
date="2025-01-16",
|
||||||
model_sig="gpt-5",
|
model_sig="gpt-5",
|
||||||
config_path="configs/test.json",
|
config_path=str(config_path),
|
||||||
db_path=clean_db
|
db_path=clean_db
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -182,25 +192,34 @@ class TestModelDayExecutorExecution:
|
|||||||
class TestModelDayExecutorDataPersistence:
|
class TestModelDayExecutorDataPersistence:
|
||||||
"""Test result persistence to SQLite."""
|
"""Test result persistence to SQLite."""
|
||||||
|
|
||||||
def test_writes_position_to_database(self, clean_db):
|
def test_creates_initial_position(self, clean_db, tmp_path):
|
||||||
"""Should write position record to SQLite."""
|
"""Should create initial position record (action_id=0) on first day."""
|
||||||
from api.model_day_executor import ModelDayExecutor
|
from api.model_day_executor import ModelDayExecutor
|
||||||
from api.job_manager import JobManager
|
from api.job_manager import JobManager
|
||||||
from api.database import get_db_connection
|
from api.database import get_db_connection
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Create a temporary config file
|
||||||
|
config_path = tmp_path / "test_config.json"
|
||||||
|
config_data = {
|
||||||
|
"agent_type": "BaseAgent",
|
||||||
|
"models": [],
|
||||||
|
"agent_config": {
|
||||||
|
"initial_cash": 10000.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config_path.write_text(json.dumps(config_data))
|
||||||
|
|
||||||
# Create job
|
# Create job
|
||||||
manager = JobManager(db_path=clean_db)
|
manager = JobManager(db_path=clean_db)
|
||||||
job_id = manager.create_job(
|
job_id = manager.create_job(
|
||||||
config_path="configs/test.json",
|
config_path=str(config_path),
|
||||||
date_range=["2025-01-16"],
|
date_range=["2025-01-16"],
|
||||||
models=["gpt-5"]
|
models=["gpt-5"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mock successful execution
|
# Mock successful execution (no trades)
|
||||||
mock_agent = create_mock_agent(
|
mock_agent = create_mock_agent(
|
||||||
positions={"AAPL": 10, "CASH": 7500.0},
|
|
||||||
last_trade={"action": "buy", "symbol": "AAPL", "amount": 10, "price": 250.0},
|
|
||||||
current_prices={"AAPL": 250.0},
|
|
||||||
session_result={"success": True, "total_steps": 10}
|
session_result={"success": True, "total_steps": 10}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -213,84 +232,32 @@ class TestModelDayExecutorDataPersistence:
|
|||||||
job_id=job_id,
|
job_id=job_id,
|
||||||
date="2025-01-16",
|
date="2025-01-16",
|
||||||
model_sig="gpt-5",
|
model_sig="gpt-5",
|
||||||
config_path="configs/test.json",
|
config_path=str(config_path),
|
||||||
db_path=clean_db
|
db_path=clean_db
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch.object(executor, '_initialize_agent', return_value=mock_agent):
|
with patch.object(executor, '_initialize_agent', return_value=mock_agent):
|
||||||
executor.execute()
|
executor.execute()
|
||||||
|
|
||||||
# Verify position written to database
|
# Verify initial position created (action_id=0)
|
||||||
conn = get_db_connection(clean_db)
|
conn = get_db_connection(clean_db)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT job_id, date, model, action_id, action_type
|
SELECT job_id, date, model, action_id, action_type, cash, portfolio_value
|
||||||
FROM positions
|
FROM positions
|
||||||
WHERE job_id = ? AND date = ? AND model = ?
|
WHERE job_id = ? AND date = ? AND model = ?
|
||||||
""", (job_id, "2025-01-16", "gpt-5"))
|
""", (job_id, "2025-01-16", "gpt-5"))
|
||||||
|
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
assert row is not None
|
assert row is not None, "Should create initial position record"
|
||||||
assert row[0] == job_id
|
assert row[0] == job_id
|
||||||
assert row[1] == "2025-01-16"
|
assert row[1] == "2025-01-16"
|
||||||
assert row[2] == "gpt-5"
|
assert row[2] == "gpt-5"
|
||||||
|
assert row[3] == 0, "Initial position should have action_id=0"
|
||||||
conn.close()
|
assert row[4] == "no_trade"
|
||||||
|
assert row[5] == 10000.0, "Initial cash should be $10,000"
|
||||||
def test_writes_holdings_to_database(self, clean_db):
|
assert row[6] == 10000.0, "Initial portfolio value should be $10,000"
|
||||||
"""Should write holdings records to SQLite."""
|
|
||||||
from api.model_day_executor import ModelDayExecutor
|
|
||||||
from api.job_manager import JobManager
|
|
||||||
from api.database import get_db_connection
|
|
||||||
|
|
||||||
# Create job
|
|
||||||
manager = JobManager(db_path=clean_db)
|
|
||||||
job_id = manager.create_job(
|
|
||||||
config_path="configs/test.json",
|
|
||||||
date_range=["2025-01-16"],
|
|
||||||
models=["gpt-5"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Mock successful execution
|
|
||||||
mock_agent = create_mock_agent(
|
|
||||||
positions={"AAPL": 10, "MSFT": 5, "CASH": 7500.0},
|
|
||||||
current_prices={"AAPL": 250.0, "MSFT": 300.0},
|
|
||||||
session_result={"success": True}
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch("api.model_day_executor.RuntimeConfigManager") as mock_runtime:
|
|
||||||
mock_instance = Mock()
|
|
||||||
mock_instance.create_runtime_config.return_value = "/tmp/runtime_test.json"
|
|
||||||
mock_runtime.return_value = mock_instance
|
|
||||||
|
|
||||||
executor = ModelDayExecutor(
|
|
||||||
job_id=job_id,
|
|
||||||
date="2025-01-16",
|
|
||||||
model_sig="gpt-5",
|
|
||||||
config_path="configs/test.json",
|
|
||||||
db_path=clean_db
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch.object(executor, '_initialize_agent', return_value=mock_agent):
|
|
||||||
executor.execute()
|
|
||||||
|
|
||||||
# Verify holdings written
|
|
||||||
conn = get_db_connection(clean_db)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
cursor.execute("""
|
|
||||||
SELECT h.symbol, h.quantity
|
|
||||||
FROM holdings h
|
|
||||||
JOIN positions p ON h.position_id = p.id
|
|
||||||
WHERE p.job_id = ? AND p.date = ? AND p.model = ?
|
|
||||||
ORDER BY h.symbol
|
|
||||||
""", (job_id, "2025-01-16", "gpt-5"))
|
|
||||||
|
|
||||||
holdings = cursor.fetchall()
|
|
||||||
assert len(holdings) == 3
|
|
||||||
assert holdings[0][0] == "AAPL"
|
|
||||||
assert holdings[0][1] == 10.0
|
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@@ -310,7 +277,6 @@ class TestModelDayExecutorDataPersistence:
|
|||||||
|
|
||||||
# Mock execution with reasoning
|
# Mock execution with reasoning
|
||||||
mock_agent = create_mock_agent(
|
mock_agent = create_mock_agent(
|
||||||
positions={"CASH": 10000.0},
|
|
||||||
reasoning_steps=[
|
reasoning_steps=[
|
||||||
{"step": 1, "reasoning": "Analyzing market data"},
|
{"step": 1, "reasoning": "Analyzing market data"},
|
||||||
{"step": 2, "reasoning": "Evaluating risk"}
|
{"step": 2, "reasoning": "Evaluating risk"}
|
||||||
@@ -361,7 +327,6 @@ class TestModelDayExecutorCleanup:
|
|||||||
)
|
)
|
||||||
|
|
||||||
mock_agent = create_mock_agent(
|
mock_agent = create_mock_agent(
|
||||||
positions={"CASH": 10000.0},
|
|
||||||
session_result={"success": True}
|
session_result={"success": True}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -421,57 +386,10 @@ class TestModelDayExecutorCleanup:
|
|||||||
class TestModelDayExecutorPositionCalculations:
|
class TestModelDayExecutorPositionCalculations:
|
||||||
"""Test position and P&L calculations."""
|
"""Test position and P&L calculations."""
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="Method _calculate_portfolio_value() removed - portfolio value calculated by trade tools")
|
||||||
def test_calculates_portfolio_value(self, clean_db):
|
def test_calculates_portfolio_value(self, clean_db):
|
||||||
"""Should calculate total portfolio value."""
|
"""DEPRECATED: Portfolio value is now calculated by trade tools, not ModelDayExecutor."""
|
||||||
from api.model_day_executor import ModelDayExecutor
|
pass
|
||||||
from api.job_manager import JobManager
|
|
||||||
from api.database import get_db_connection
|
|
||||||
|
|
||||||
manager = JobManager(db_path=clean_db)
|
|
||||||
job_id = manager.create_job(
|
|
||||||
config_path="configs/test.json",
|
|
||||||
date_range=["2025-01-16"],
|
|
||||||
models=["gpt-5"]
|
|
||||||
)
|
|
||||||
|
|
||||||
mock_agent = create_mock_agent(
|
|
||||||
positions={"AAPL": 10, "CASH": 7500.0}, # 10 shares @ $250 = $2500
|
|
||||||
current_prices={"AAPL": 250.0},
|
|
||||||
session_result={"success": True}
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch("api.model_day_executor.RuntimeConfigManager") as mock_runtime:
|
|
||||||
mock_instance = Mock()
|
|
||||||
mock_instance.create_runtime_config.return_value = "/tmp/runtime_test.json"
|
|
||||||
mock_runtime.return_value = mock_instance
|
|
||||||
|
|
||||||
executor = ModelDayExecutor(
|
|
||||||
job_id=job_id,
|
|
||||||
date="2025-01-16",
|
|
||||||
model_sig="gpt-5",
|
|
||||||
config_path="configs/test.json",
|
|
||||||
db_path=clean_db
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch.object(executor, '_initialize_agent', return_value=mock_agent):
|
|
||||||
executor.execute()
|
|
||||||
|
|
||||||
# Verify portfolio value calculated correctly
|
|
||||||
conn = get_db_connection(clean_db)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
cursor.execute("""
|
|
||||||
SELECT portfolio_value
|
|
||||||
FROM positions
|
|
||||||
WHERE job_id = ? AND date = ? AND model = ?
|
|
||||||
""", (job_id, "2025-01-16", "gpt-5"))
|
|
||||||
|
|
||||||
row = cursor.fetchone()
|
|
||||||
assert row is not None
|
|
||||||
# Portfolio value should be 2500 (stocks) + 7500 (cash) = 10000
|
|
||||||
assert row[0] == 10000.0
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
|
|
||||||
# Coverage target: 90%+ for api/model_day_executor.py
|
# Coverage target: 90%+ for api/model_day_executor.py
|
||||||
|
|||||||
@@ -211,56 +211,7 @@ async def test_store_reasoning_logs_with_tool_messages(test_db):
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="Method _write_results_to_db() removed - positions written by trade tools")
|
||||||
def test_write_results_includes_session_id(test_db):
|
def test_write_results_includes_session_id(test_db):
|
||||||
"""Should include session_id when writing positions."""
|
"""DEPRECATED: This test verified _write_results_to_db() which has been removed."""
|
||||||
from agent.mock_provider.mock_langchain_model import MockChatModel
|
pass
|
||||||
from agent.base_agent.base_agent import BaseAgent
|
|
||||||
|
|
||||||
executor = ModelDayExecutor(
|
|
||||||
job_id="test-job",
|
|
||||||
date="2025-01-01",
|
|
||||||
model_sig="test-model",
|
|
||||||
config_path="configs/default_config.json",
|
|
||||||
db_path=test_db
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create mock agent with positions
|
|
||||||
agent = BaseAgent(
|
|
||||||
signature="test-model",
|
|
||||||
basemodel="mock",
|
|
||||||
stock_symbols=["AAPL"],
|
|
||||||
init_date="2025-01-01"
|
|
||||||
)
|
|
||||||
agent.model = MockChatModel(model="test", signature="test")
|
|
||||||
|
|
||||||
# Mock positions data
|
|
||||||
agent.positions = {"AAPL": 10, "CASH": 8500.0}
|
|
||||||
agent.last_trade = {"action": "buy", "symbol": "AAPL", "amount": 10, "price": 150.0}
|
|
||||||
agent.current_prices = {"AAPL": 150.0}
|
|
||||||
|
|
||||||
# Add required methods
|
|
||||||
agent.get_positions = lambda: agent.positions
|
|
||||||
agent.get_last_trade = lambda: agent.last_trade
|
|
||||||
agent.get_current_prices = lambda: agent.current_prices
|
|
||||||
|
|
||||||
conn = get_db_connection(test_db)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Create session
|
|
||||||
session_id = executor._create_trading_session(cursor)
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
# Write results
|
|
||||||
executor._write_results_to_db(agent, session_id)
|
|
||||||
|
|
||||||
# Verify position has session_id
|
|
||||||
cursor.execute("SELECT * FROM positions WHERE job_id = ? AND model = ?",
|
|
||||||
("test-job", "test-model"))
|
|
||||||
position = cursor.fetchone()
|
|
||||||
|
|
||||||
assert position is not None
|
|
||||||
assert position['session_id'] == session_id
|
|
||||||
assert position['action_type'] == 'buy'
|
|
||||||
assert position['symbol'] == 'AAPL'
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user