Local changes: Updated model training, removed debug instrumentation, and configuration improvements
This commit is contained in:
2
tests/integration/__init__.py
Normal file
2
tests/integration/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Integration tests."""
|
||||
|
||||
2
tests/integration/backend/__init__.py
Normal file
2
tests/integration/backend/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Backend integration tests."""
|
||||
|
||||
95
tests/integration/backend/test_api_workflows.py
Normal file
95
tests/integration/backend/test_api_workflows.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""Integration tests for API workflows."""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from backend.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Test client fixture."""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestTradingWorkflow:
|
||||
"""Test complete trading workflow through API."""
|
||||
|
||||
@patch('backend.api.trading.get_trading_engine')
|
||||
@patch('backend.api.trading.get_db')
|
||||
def test_complete_trading_workflow(self, mock_get_db, mock_get_engine, client):
|
||||
"""Test: Place order → Check order status → Get positions."""
|
||||
# Setup mocks
|
||||
mock_engine = Mock()
|
||||
mock_order = Mock()
|
||||
mock_order.id = 1
|
||||
mock_order.symbol = "BTC/USD"
|
||||
mock_order.side = "buy"
|
||||
mock_order.status = "filled"
|
||||
mock_engine.execute_order.return_value = mock_order
|
||||
mock_get_engine.return_value = mock_engine
|
||||
|
||||
mock_db = Mock()
|
||||
mock_session = Mock()
|
||||
mock_db.get_session.return_value = mock_session
|
||||
mock_get_db.return_value = mock_db
|
||||
|
||||
# Mock order query
|
||||
mock_session.query.return_value.filter_by.return_value.order_by.return_value.limit.return_value.all.return_value = [mock_order]
|
||||
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_order
|
||||
|
||||
# Place order
|
||||
order_data = {
|
||||
"exchange_id": 1,
|
||||
"symbol": "BTC/USD",
|
||||
"side": "buy",
|
||||
"order_type": "market",
|
||||
"quantity": "0.1",
|
||||
"paper_trading": True
|
||||
}
|
||||
create_response = client.post("/api/trading/orders", json=order_data)
|
||||
assert create_response.status_code == 200
|
||||
|
||||
order_id = create_response.json()["id"]
|
||||
|
||||
# Get order status
|
||||
status_response = client.get(f"/api/trading/orders/{order_id}")
|
||||
assert status_response.status_code == 200
|
||||
assert status_response.json()["id"] == order_id
|
||||
|
||||
# Get orders list
|
||||
orders_response = client.get("/api/trading/orders")
|
||||
assert orders_response.status_code == 200
|
||||
assert isinstance(orders_response.json(), list)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestPortfolioWorkflow:
|
||||
"""Test portfolio workflow through API."""
|
||||
|
||||
@patch('backend.api.portfolio.get_portfolio_tracker')
|
||||
def test_portfolio_workflow(self, mock_get_tracker, client):
|
||||
"""Test: Get current portfolio → Get portfolio history."""
|
||||
mock_tracker = Mock()
|
||||
mock_tracker.get_current_portfolio.return_value = {
|
||||
"positions": [],
|
||||
"performance": {"total_return": 0.1},
|
||||
"timestamp": "2025-01-01T00:00:00"
|
||||
}
|
||||
mock_tracker.get_portfolio_history.return_value = [
|
||||
{"timestamp": "2025-01-01T00:00:00", "total_value": 1000.0, "total_pnl": 0.0}
|
||||
]
|
||||
mock_get_tracker.return_value = mock_tracker
|
||||
|
||||
# Get current portfolio
|
||||
current_response = client.get("/api/portfolio/current?paper_trading=true")
|
||||
assert current_response.status_code == 200
|
||||
assert "positions" in current_response.json()
|
||||
|
||||
# Get portfolio history
|
||||
history_response = client.get("/api/portfolio/history?paper_trading=true&days=30")
|
||||
assert history_response.status_code == 200
|
||||
assert "dates" in history_response.json()
|
||||
|
||||
323
tests/integration/backend/test_frontend_api_workflows.py
Normal file
323
tests/integration/backend/test_frontend_api_workflows.py
Normal file
@@ -0,0 +1,323 @@
|
||||
"""Integration tests for frontend API workflows.
|
||||
|
||||
These tests verify that all frontend-accessible API endpoints work correctly
|
||||
and return data in the expected format for the React frontend.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from backend.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Create test client."""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_strategy():
|
||||
"""Mock strategy data."""
|
||||
return {
|
||||
"id": 1,
|
||||
"name": "Test RSI Strategy",
|
||||
"strategy_type": "rsi",
|
||||
"class_name": "rsi",
|
||||
"parameters": {
|
||||
"symbol": "BTC/USD",
|
||||
"exchange_id": 1,
|
||||
"rsi_period": 14,
|
||||
"oversold": 30,
|
||||
"overbought": 70
|
||||
},
|
||||
"timeframes": ["1h"],
|
||||
"enabled": False,
|
||||
"paper_trading": True,
|
||||
"version": "1.0.0",
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"updated_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_exchange():
|
||||
"""Mock exchange data."""
|
||||
return {
|
||||
"id": 1,
|
||||
"name": "coinbase",
|
||||
"sandbox": False,
|
||||
"read_only": True,
|
||||
"enabled": True,
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"updated_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
|
||||
class TestStrategyManagementAPI:
|
||||
"""Test strategy management API endpoints."""
|
||||
|
||||
def test_list_strategies(self, client):
|
||||
"""Test listing all strategies."""
|
||||
with patch('backend.api.strategies.get_db') as mock_db:
|
||||
mock_session = Mock()
|
||||
mock_strategy = Mock()
|
||||
mock_strategy.id = 1
|
||||
mock_strategy.name = "Test Strategy"
|
||||
mock_strategy.strategy_type = "rsi"
|
||||
mock_strategy.class_name = "rsi"
|
||||
mock_strategy.parameters = {}
|
||||
mock_strategy.timeframes = ["1h"]
|
||||
mock_strategy.enabled = False
|
||||
mock_strategy.paper_trading = True
|
||||
mock_strategy.version = "1.0.0"
|
||||
mock_strategy.description = None
|
||||
mock_strategy.schedule = None
|
||||
mock_strategy.created_at = datetime.now()
|
||||
mock_strategy.updated_at = datetime.now()
|
||||
|
||||
mock_session.query.return_value.order_by.return_value.all.return_value = [mock_strategy]
|
||||
mock_db.return_value.get_session.return_value.__enter__.return_value = mock_session
|
||||
mock_db.return_value.get_session.return_value.__exit__ = Mock(return_value=None)
|
||||
|
||||
response = client.get("/api/strategies/")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
|
||||
def test_get_available_strategies(self, client):
|
||||
"""Test getting available strategy types."""
|
||||
with patch('backend.api.strategies.get_strategy_registry') as mock_registry:
|
||||
mock_registry.return_value.list_available.return_value = [
|
||||
"rsi", "macd", "moving_average", "dca", "grid", "momentum"
|
||||
]
|
||||
|
||||
response = client.get("/api/strategies/available")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "strategies" in data
|
||||
assert isinstance(data["strategies"], list)
|
||||
|
||||
def test_create_strategy(self, client, mock_strategy):
|
||||
"""Test creating a new strategy."""
|
||||
with patch('backend.api.strategies.get_db') as mock_db:
|
||||
mock_session = Mock()
|
||||
mock_db.return_value.get_session.return_value.__enter__.return_value = mock_session
|
||||
mock_db.return_value.get_session.return_value.__exit__ = Mock(return_value=None)
|
||||
mock_session.add = Mock()
|
||||
mock_session.commit = Mock()
|
||||
mock_session.refresh = Mock()
|
||||
|
||||
# Create strategy instance
|
||||
created_strategy = Mock()
|
||||
created_strategy.id = 1
|
||||
created_strategy.name = mock_strategy["name"]
|
||||
created_strategy.strategy_type = mock_strategy["strategy_type"]
|
||||
created_strategy.class_name = mock_strategy["class_name"]
|
||||
created_strategy.parameters = mock_strategy["parameters"]
|
||||
created_strategy.timeframes = mock_strategy["timeframes"]
|
||||
created_strategy.enabled = False
|
||||
created_strategy.paper_trading = mock_strategy["paper_trading"]
|
||||
created_strategy.version = "1.0.0"
|
||||
created_strategy.description = None
|
||||
created_strategy.schedule = None
|
||||
created_strategy.created_at = datetime.now()
|
||||
created_strategy.updated_at = datetime.now()
|
||||
mock_session.refresh.side_effect = lambda x: setattr(x, 'id', 1)
|
||||
|
||||
response = client.post(
|
||||
"/api/strategies/",
|
||||
json={
|
||||
"name": mock_strategy["name"],
|
||||
"strategy_type": mock_strategy["strategy_type"],
|
||||
"class_name": mock_strategy["class_name"],
|
||||
"parameters": mock_strategy["parameters"],
|
||||
"timeframes": mock_strategy["timeframes"],
|
||||
"paper_trading": mock_strategy["paper_trading"]
|
||||
}
|
||||
)
|
||||
# May return 200 or 500 depending on implementation
|
||||
assert response.status_code in [200, 201, 500]
|
||||
|
||||
|
||||
class TestTradingAPI:
|
||||
"""Test trading API endpoints."""
|
||||
|
||||
def test_get_positions(self, client):
|
||||
"""Test getting positions."""
|
||||
with patch('src.trading.paper_trading.get_paper_trading') as mock_pt:
|
||||
mock_pt.return_value.get_positions.return_value = []
|
||||
|
||||
response = client.get("/api/trading/positions?paper_trading=true")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
|
||||
def test_get_orders(self, client):
|
||||
"""Test getting orders."""
|
||||
with patch('backend.api.trading.get_db') as mock_db:
|
||||
mock_session = Mock()
|
||||
mock_session.query.return_value.filter_by.return_value.order_by.return_value.limit.return_value.all.return_value = []
|
||||
mock_db.return_value.get_session.return_value.__enter__.return_value = mock_session
|
||||
mock_db.return_value.get_session.return_value.__exit__ = Mock(return_value=None)
|
||||
|
||||
response = client.get("/api/trading/orders?paper_trading=true&limit=10")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
|
||||
def test_get_balance(self, client):
|
||||
"""Test getting balance."""
|
||||
with patch('src.trading.paper_trading.get_paper_trading') as mock_pt:
|
||||
mock_pt.return_value.get_balance.return_value = Decimal("100.00")
|
||||
mock_pt.return_value.get_performance.return_value = {}
|
||||
|
||||
response = client.get("/api/trading/balance?paper_trading=true")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "balance" in data
|
||||
|
||||
|
||||
class TestPortfolioAPI:
|
||||
"""Test portfolio API endpoints."""
|
||||
|
||||
def test_get_current_portfolio(self, client):
|
||||
"""Test getting current portfolio."""
|
||||
with patch('backend.api.portfolio.get_portfolio_tracker') as mock_tracker:
|
||||
mock_tracker.return_value.get_current_portfolio.return_value = {
|
||||
"positions": [],
|
||||
"performance": {
|
||||
"current_value": 100.0,
|
||||
"unrealized_pnl": 0.0,
|
||||
"realized_pnl": 0.0
|
||||
},
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
response = client.get("/api/portfolio/current?paper_trading=true")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "positions" in data
|
||||
assert "performance" in data
|
||||
|
||||
def test_get_portfolio_history(self, client):
|
||||
"""Test getting portfolio history."""
|
||||
with patch('backend.api.portfolio.get_portfolio_tracker') as mock_tracker:
|
||||
mock_tracker.return_value.get_portfolio_history.return_value = {
|
||||
"dates": [datetime.now().isoformat()],
|
||||
"values": [100.0],
|
||||
"pnl": [0.0]
|
||||
}
|
||||
|
||||
response = client.get("/api/portfolio/history?days=30&paper_trading=true")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "dates" in data
|
||||
assert "values" in data
|
||||
|
||||
|
||||
class TestBacktestingAPI:
|
||||
"""Test backtesting API endpoints."""
|
||||
|
||||
def test_run_backtest(self, client):
|
||||
"""Test running a backtest."""
|
||||
with patch('backend.api.backtesting.get_backtesting_engine') as mock_engine, \
|
||||
patch('backend.api.backtesting.get_db') as mock_db:
|
||||
|
||||
mock_session = Mock()
|
||||
mock_strategy = Mock()
|
||||
mock_strategy.id = 1
|
||||
mock_strategy.class_name = "rsi"
|
||||
mock_strategy.parameters = {}
|
||||
mock_strategy.timeframes = ["1h"]
|
||||
mock_session.query.return_value.filter_by.return_value.first.return_value = mock_strategy
|
||||
mock_db.return_value.get_session.return_value.__enter__.return_value = mock_session
|
||||
mock_db.return_value.get_session.return_value.__exit__ = Mock(return_value=None)
|
||||
|
||||
mock_engine.return_value.run_backtest.return_value = {
|
||||
"total_return": 0.1,
|
||||
"sharpe_ratio": 1.5,
|
||||
"max_drawdown": -0.05,
|
||||
"win_rate": 0.6,
|
||||
"total_trades": 10,
|
||||
"final_value": 110.0
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/backtesting/run",
|
||||
json={
|
||||
"strategy_id": 1,
|
||||
"symbol": "BTC/USD",
|
||||
"exchange": "coinbase",
|
||||
"timeframe": "1h",
|
||||
"start_date": (datetime.now() - timedelta(days=30)).isoformat(),
|
||||
"end_date": datetime.now().isoformat(),
|
||||
"initial_capital": 100.0,
|
||||
"slippage": 0.001,
|
||||
"fee_rate": 0.001
|
||||
}
|
||||
)
|
||||
# May return 200 or error depending on implementation
|
||||
assert response.status_code in [200, 400, 500]
|
||||
|
||||
|
||||
class TestAlertsAPI:
|
||||
"""Test alerts API endpoints."""
|
||||
|
||||
def test_list_alerts(self, client):
|
||||
"""Test listing alerts."""
|
||||
with patch('backend.api.alerts.get_alert_manager') as mock_manager:
|
||||
mock_manager.return_value.list_alerts.return_value = []
|
||||
|
||||
response = client.get("/api/alerts/?enabled_only=false")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
|
||||
def test_create_alert(self, client):
|
||||
"""Test creating an alert."""
|
||||
with patch('backend.api.alerts.get_alert_manager') as mock_manager:
|
||||
mock_alert = Mock()
|
||||
mock_alert.id = 1
|
||||
mock_alert.name = "Test Alert"
|
||||
mock_alert.alert_type = "price"
|
||||
mock_alert.condition = {"symbol": "BTC/USD", "price_threshold": 50000}
|
||||
mock_alert.enabled = True
|
||||
mock_alert.triggered = False
|
||||
mock_alert.triggered_at = None
|
||||
mock_alert.created_at = datetime.now()
|
||||
mock_alert.updated_at = datetime.now()
|
||||
|
||||
mock_manager.return_value.create_alert.return_value = mock_alert
|
||||
|
||||
response = client.post(
|
||||
"/api/alerts/",
|
||||
json={
|
||||
"name": "Test Alert",
|
||||
"alert_type": "price",
|
||||
"condition": {"symbol": "BTC/USD", "price_threshold": 50000}
|
||||
}
|
||||
)
|
||||
# May return 200 or 500 depending on implementation
|
||||
assert response.status_code in [200, 201, 500]
|
||||
|
||||
|
||||
class TestExchangesAPI:
|
||||
"""Test exchanges API endpoints."""
|
||||
|
||||
def test_list_exchanges(self, client):
|
||||
"""Test listing exchanges."""
|
||||
with patch('backend.api.exchanges.get_db') as mock_db:
|
||||
mock_session = Mock()
|
||||
mock_session.query.return_value.all.return_value = []
|
||||
mock_db.return_value.get_session.return_value.__enter__.return_value = mock_session
|
||||
mock_db.return_value.get_session.return_value.__exit__ = Mock(return_value=None)
|
||||
|
||||
response = client.get("/api/exchanges/")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
|
||||
32
tests/integration/backend/test_websocket_connections.py
Normal file
32
tests/integration/backend/test_websocket_connections.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""Integration tests for WebSocket connections."""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from backend.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Test client fixture."""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestWebSocketConnection:
|
||||
"""Test WebSocket connections."""
|
||||
|
||||
def test_websocket_connection(self, client):
|
||||
"""Test WebSocket connection."""
|
||||
with client.websocket_connect("/ws/") as websocket:
|
||||
# Connection should be established
|
||||
assert websocket is not None
|
||||
|
||||
def test_websocket_message_handling(self, client):
|
||||
"""Test WebSocket message handling."""
|
||||
with client.websocket_connect("/ws/") as websocket:
|
||||
# Send a test message
|
||||
websocket.send_json({"type": "ping"})
|
||||
# WebSocket should accept the connection
|
||||
# Note: Actual message handling depends on implementation
|
||||
|
||||
138
tests/integration/test_autopilot_workflow.py
Normal file
138
tests/integration/test_autopilot_workflow.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""Integration tests for autopilot workflows."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, AsyncMock
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from backend.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Test client fixture."""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_intelligent_autopilot():
|
||||
"""Mock intelligent autopilot."""
|
||||
autopilot = Mock()
|
||||
autopilot.symbol = "BTC/USD"
|
||||
autopilot.is_running = False
|
||||
autopilot.enable_auto_execution = False
|
||||
autopilot.get_status.return_value = {
|
||||
"symbol": "BTC/USD",
|
||||
"timeframe": "1h",
|
||||
"running": False,
|
||||
"selected_strategy": None,
|
||||
"trades_today": 0,
|
||||
"max_trades_per_day": 10,
|
||||
"min_confidence_threshold": 0.75,
|
||||
"enable_auto_execution": False,
|
||||
"last_analysis": None,
|
||||
"model_info": {},
|
||||
}
|
||||
return autopilot
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestAutopilotWorkflow:
|
||||
"""Integration tests for autopilot workflows."""
|
||||
|
||||
@patch('backend.api.autopilot.get_intelligent_autopilot')
|
||||
def test_start_intelligent_mode_workflow(
|
||||
self, mock_get_intelligent, client, mock_intelligent_autopilot
|
||||
):
|
||||
"""Test complete workflow for starting intelligent mode autopilot."""
|
||||
mock_get_intelligent.return_value = mock_intelligent_autopilot
|
||||
|
||||
# Start autopilot
|
||||
response = client.post(
|
||||
"/api/autopilot/start-unified",
|
||||
json={
|
||||
"symbol": "BTC/USD",
|
||||
"mode": "intelligent",
|
||||
"auto_execute": True,
|
||||
"exchange_id": 1,
|
||||
"timeframe": "1h",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert mock_intelligent_autopilot.enable_auto_execution is True
|
||||
|
||||
# Check status
|
||||
response = client.get(
|
||||
"/api/autopilot/status-unified/BTC/USD?mode=intelligent&timeframe=1h"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["mode"] == "intelligent"
|
||||
|
||||
# Stop autopilot
|
||||
response = client.post(
|
||||
"/api/autopilot/stop-unified?symbol=BTC/USD&mode=intelligent&timeframe=1h"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert mock_intelligent_autopilot.stop.called
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestTrainingConfigWorkflow:
|
||||
"""Integration tests for training configuration workflow."""
|
||||
|
||||
def test_configure_and_verify_bootstrap(self, client):
|
||||
"""Test configuring bootstrap settings and verifying they persist."""
|
||||
# Set custom config
|
||||
custom_config = {
|
||||
"days": 180,
|
||||
"timeframe": "4h",
|
||||
"min_samples_per_strategy": 25,
|
||||
"symbols": ["BTC/USD", "ETH/USD", "SOL/USD", "DOGE/USD"]
|
||||
}
|
||||
|
||||
response = client.put("/api/autopilot/bootstrap-config", json=custom_config)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify it was saved
|
||||
response = client.get("/api/autopilot/bootstrap-config")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
assert data["days"] == 180
|
||||
assert data["timeframe"] == "4h"
|
||||
assert data["min_samples_per_strategy"] == 25
|
||||
assert len(data["symbols"]) == 4
|
||||
assert "DOGE/USD" in data["symbols"]
|
||||
|
||||
@patch('backend.api.autopilot.train_model_task')
|
||||
def test_training_uses_configured_symbols(self, mock_task, client):
|
||||
"""Test that training uses the configured symbols."""
|
||||
# Setup mock
|
||||
mock_result = Mock()
|
||||
mock_result.id = "test-task-123"
|
||||
mock_task.delay.return_value = mock_result
|
||||
|
||||
# Configure with specific symbols
|
||||
config = {
|
||||
"days": 90,
|
||||
"timeframe": "1h",
|
||||
"min_samples_per_strategy": 10,
|
||||
"symbols": ["BTC/USD", "ETH/USD", "XRP/USD"]
|
||||
}
|
||||
client.put("/api/autopilot/bootstrap-config", json=config)
|
||||
|
||||
# Trigger training
|
||||
response = client.post("/api/autopilot/intelligent/retrain")
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify the symbols were passed
|
||||
call_kwargs = mock_task.delay.call_args.kwargs
|
||||
assert call_kwargs["symbols"] == ["BTC/USD", "ETH/USD", "XRP/USD"]
|
||||
assert call_kwargs["days"] == 90
|
||||
assert call_kwargs["timeframe"] == "1h"
|
||||
assert call_kwargs["min_samples_per_strategy"] == 10
|
||||
|
||||
|
||||
|
||||
|
||||
18
tests/integration/test_backtesting_workflow.py
Normal file
18
tests/integration/test_backtesting_workflow.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Integration tests for backtesting workflow."""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime, timedelta
|
||||
from src.backtesting.engine import get_backtest_engine
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestBacktestingWorkflow:
|
||||
"""Integration tests for backtesting workflow."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backtesting_workflow(self):
|
||||
"""Test complete backtesting workflow."""
|
||||
engine = get_backtest_engine()
|
||||
assert engine is not None
|
||||
# Full workflow test would require strategy and data setup
|
||||
|
||||
21
tests/integration/test_data_pipeline.py
Normal file
21
tests/integration/test_data_pipeline.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""Integration tests for data pipeline."""
|
||||
|
||||
import pytest
|
||||
from src.data.collector import get_data_collector
|
||||
from src.data.storage import get_data_storage
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestDataPipeline:
|
||||
"""Integration tests for data collection and storage."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_data_collection_storage(self):
|
||||
"""Test data collection and storage pipeline."""
|
||||
collector = get_data_collector()
|
||||
storage = get_data_storage()
|
||||
|
||||
# Test components exist
|
||||
assert collector is not None
|
||||
assert storage is not None
|
||||
|
||||
31
tests/integration/test_portfolio_tracking.py
Normal file
31
tests/integration/test_portfolio_tracking.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""Integration tests for portfolio tracking."""
|
||||
|
||||
import pytest
|
||||
from decimal import Decimal
|
||||
from src.portfolio.tracker import get_portfolio_tracker
|
||||
from src.trading.paper_trading import get_paper_trading
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestPortfolioTracking:
|
||||
"""Integration tests for portfolio tracking."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_portfolio_tracking_workflow(self):
|
||||
"""Test portfolio tracking workflow."""
|
||||
tracker = get_portfolio_tracker()
|
||||
paper_trading = get_paper_trading()
|
||||
|
||||
# Get initial portfolio
|
||||
portfolio1 = await tracker.get_current_portfolio(paper_trading=True)
|
||||
|
||||
# Portfolio should have structure
|
||||
assert "positions" in portfolio1
|
||||
assert "performance" in portfolio1
|
||||
|
||||
# Get updated portfolio
|
||||
portfolio2 = await tracker.get_current_portfolio(paper_trading=True)
|
||||
|
||||
# Should return valid portfolio
|
||||
assert portfolio2 is not None
|
||||
|
||||
71
tests/integration/test_pricing_providers.py
Normal file
71
tests/integration/test_pricing_providers.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""Integration tests for pricing provider system."""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
|
||||
from src.data.pricing_service import get_pricing_service
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestPricingProviderIntegration:
|
||||
"""Integration tests for pricing providers."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
"""Set up test fixtures."""
|
||||
# Reset global service instance
|
||||
import src.data.pricing_service
|
||||
src.data.pricing_service._pricing_service = None
|
||||
|
||||
def test_service_initialization(self):
|
||||
"""Test that pricing service initializes correctly."""
|
||||
service = get_pricing_service()
|
||||
assert service is not None
|
||||
assert service.cache is not None
|
||||
assert service.health_monitor is not None
|
||||
|
||||
@pytest.mark.skip(reason="Requires network access - run manually")
|
||||
def test_get_ticker_integration(self):
|
||||
"""Test getting ticker data from real providers."""
|
||||
service = get_pricing_service()
|
||||
|
||||
# This will try to connect to real providers
|
||||
ticker = service.get_ticker("BTC/USD", use_cache=False)
|
||||
|
||||
# Should get some data if providers are available
|
||||
if ticker:
|
||||
assert 'symbol' in ticker
|
||||
assert 'last' in ticker
|
||||
assert ticker['last'] > 0
|
||||
|
||||
@pytest.mark.skip(reason="Requires network access - run manually")
|
||||
def test_provider_failover(self):
|
||||
"""Test provider failover mechanism."""
|
||||
service = get_pricing_service()
|
||||
|
||||
# Get active provider
|
||||
active = service.get_active_provider()
|
||||
assert active is not None or len(service._providers) == 0
|
||||
|
||||
def test_cache_integration(self):
|
||||
"""Test cache integration with service."""
|
||||
service = get_pricing_service()
|
||||
|
||||
# Set a value
|
||||
service.cache.set("test:key", "test_value", ttl=60)
|
||||
|
||||
# Get it back
|
||||
value = service.cache.get("test:key")
|
||||
assert value == "test_value"
|
||||
|
||||
def test_health_monitoring(self):
|
||||
"""Test health monitoring integration."""
|
||||
service = get_pricing_service()
|
||||
|
||||
# Record some metrics
|
||||
service.health_monitor.record_success("test_provider", 0.5)
|
||||
service.health_monitor.record_failure("test_provider")
|
||||
|
||||
# Check health
|
||||
is_healthy = service.health_monitor.is_healthy("test_provider")
|
||||
assert isinstance(is_healthy, bool)
|
||||
36
tests/integration/test_strategy_execution.py
Normal file
36
tests/integration/test_strategy_execution.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Integration tests for strategy execution."""
|
||||
|
||||
import pytest
|
||||
from src.strategies.technical.rsi_strategy import RSIStrategy
|
||||
from src.trading.engine import get_trading_engine
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestStrategyExecution:
|
||||
"""Integration tests for strategy execution."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_strategy_execution_workflow(self):
|
||||
"""Test complete strategy execution workflow."""
|
||||
engine = get_trading_engine()
|
||||
await engine.initialize()
|
||||
|
||||
# Create strategy
|
||||
strategy = RSIStrategy(
|
||||
strategy_id=1,
|
||||
name="test_rsi",
|
||||
symbol="BTC/USD",
|
||||
timeframe="1h",
|
||||
parameters={"rsi_period": 14}
|
||||
)
|
||||
|
||||
# Start strategy
|
||||
await engine.start_strategy(strategy)
|
||||
assert strategy.is_active
|
||||
|
||||
# Stop strategy
|
||||
await engine.stop_strategy(1)
|
||||
assert not strategy.is_active
|
||||
|
||||
await engine.shutdown()
|
||||
|
||||
47
tests/integration/test_trading_workflow.py
Normal file
47
tests/integration/test_trading_workflow.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Integration tests for trading workflow."""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from src.trading.engine import get_trading_engine
|
||||
from src.strategies.technical.rsi_strategy import RSIStrategy
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestTradingWorkflow:
|
||||
"""Integration tests for complete trading workflow."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_complete_trading_workflow(self, mock_database):
|
||||
"""Test complete trading workflow."""
|
||||
# Initialize trading engine
|
||||
engine = get_trading_engine()
|
||||
await engine.initialize()
|
||||
|
||||
# Create strategy
|
||||
strategy = RSIStrategy(
|
||||
strategy_id=1,
|
||||
name="test_rsi",
|
||||
symbol="BTC/USD",
|
||||
timeframe="1h",
|
||||
parameters={"rsi_period": 14}
|
||||
)
|
||||
|
||||
# Start strategy
|
||||
await engine.start_strategy(strategy)
|
||||
|
||||
# Execute trade (paper trading)
|
||||
result = await engine.execute_trade(
|
||||
exchange_name="paper_trading",
|
||||
strategy_id=1,
|
||||
symbol="BTC/USD",
|
||||
side="buy",
|
||||
order_type="market",
|
||||
amount=0.01,
|
||||
is_paper_trade=True
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
|
||||
# Cleanup
|
||||
await engine.shutdown()
|
||||
|
||||
65
tests/integration/test_ui_backtest_workflow.py
Normal file
65
tests/integration/test_ui_backtest_workflow.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Integration tests for UI backtest workflow."""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime, timedelta
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from unittest.mock import Mock, patch
|
||||
from src.ui.widgets.backtest_view import BacktestViewWidget
|
||||
from src.strategies.technical.rsi_strategy import RSIStrategy
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
"""Create QApplication for tests."""
|
||||
if not QApplication.instance():
|
||||
return QApplication([])
|
||||
return QApplication.instance()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backtest_view(app):
|
||||
"""Create BacktestViewWidget."""
|
||||
view = BacktestViewWidget()
|
||||
|
||||
# Mock backtest engine
|
||||
mock_engine = Mock()
|
||||
mock_engine.run_backtest.return_value = {
|
||||
'total_return': 10.5,
|
||||
'sharpe_ratio': 1.2,
|
||||
'max_drawdown': -5.0,
|
||||
'win_rate': 0.55,
|
||||
'total_trades': 50,
|
||||
'final_value': 110.5,
|
||||
'trades': []
|
||||
}
|
||||
view.backtest_engine = mock_engine
|
||||
|
||||
return view
|
||||
|
||||
|
||||
def test_backtest_configuration(backtest_view):
|
||||
"""Test backtest configuration form."""
|
||||
assert backtest_view.strategy_combo is not None
|
||||
assert backtest_view.symbol_input is not None
|
||||
assert backtest_view.start_date is not None
|
||||
assert backtest_view.end_date is not None
|
||||
|
||||
|
||||
def test_backtest_results_display(backtest_view):
|
||||
"""Test backtest results are displayed."""
|
||||
results = {
|
||||
'total_return': 10.5,
|
||||
'sharpe_ratio': 1.2,
|
||||
'max_drawdown': -5.0,
|
||||
'win_rate': 0.55,
|
||||
'total_trades': 50,
|
||||
'final_value': 110.5,
|
||||
'trades': []
|
||||
}
|
||||
|
||||
backtest_view._display_results(results)
|
||||
|
||||
# Verify metrics text contains results
|
||||
metrics_text = backtest_view.metrics_text.toPlainText()
|
||||
assert "10.5" in metrics_text # Total return
|
||||
assert "1.2" in metrics_text # Sharpe ratio
|
||||
62
tests/integration/test_ui_strategy_workflow.py
Normal file
62
tests/integration/test_ui_strategy_workflow.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""Integration tests for UI strategy workflow."""
|
||||
|
||||
import pytest
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from unittest.mock import Mock, patch
|
||||
from src.ui.widgets.strategy_manager import StrategyManagerWidget, StrategyDialog
|
||||
from src.core.database import Strategy
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
"""Create QApplication for tests."""
|
||||
if not QApplication.instance():
|
||||
return QApplication([])
|
||||
return QApplication.instance()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def strategy_manager(app):
|
||||
"""Create StrategyManagerWidget."""
|
||||
return StrategyManagerWidget()
|
||||
|
||||
|
||||
def test_strategy_creation_workflow(strategy_manager, app):
|
||||
"""Test creating strategy via UI."""
|
||||
# Mock database
|
||||
with patch.object(strategy_manager.db, 'get_session') as mock_session:
|
||||
mock_session.return_value.__enter__.return_value.add = Mock()
|
||||
mock_session.return_value.__enter__.return_value.commit = Mock()
|
||||
|
||||
# Simulate add strategy
|
||||
dialog = StrategyDialog(strategy_manager)
|
||||
dialog.name_input.setText("Test Strategy")
|
||||
dialog.symbol_input.setText("BTC/USD")
|
||||
dialog.type_combo.setCurrentText("rsi")
|
||||
|
||||
# Would need to set exchange combo data
|
||||
# For now, just verify dialog structure
|
||||
assert dialog.name_input.text() == "Test Strategy"
|
||||
dialog.close()
|
||||
|
||||
|
||||
def test_strategy_table_population(strategy_manager):
|
||||
"""Test strategies table is populated from database."""
|
||||
# Mock database query
|
||||
mock_strategy = Mock(spec=Strategy)
|
||||
mock_strategy.id = 1
|
||||
mock_strategy.name = "Test Strategy"
|
||||
mock_strategy.strategy_type = "rsi"
|
||||
mock_strategy.parameters = {"symbol": "BTC/USD"}
|
||||
mock_strategy.enabled = True
|
||||
|
||||
with patch.object(strategy_manager.db, 'get_session') as mock_session:
|
||||
mock_query = Mock()
|
||||
mock_query.all.return_value = [mock_strategy]
|
||||
mock_session.return_value.__enter__.return_value.query.return_value.filter_by.return_value.all = Mock(return_value=[mock_strategy])
|
||||
mock_session.return_value.__enter__.return_value.query.return_value.all = Mock(return_value=[mock_strategy])
|
||||
|
||||
strategy_manager._refresh_strategies()
|
||||
|
||||
# Verify table has data
|
||||
# Note: Actual implementation would verify table contents
|
||||
77
tests/integration/test_ui_trading_workflow.py
Normal file
77
tests/integration/test_ui_trading_workflow.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""Integration tests for UI trading workflow."""
|
||||
|
||||
import pytest
|
||||
from decimal import Decimal
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from unittest.mock import Mock, patch
|
||||
from src.ui.widgets.trading_view import TradingView
|
||||
from src.core.database import Order, OrderStatus, OrderSide, OrderType
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
"""Create QApplication for tests."""
|
||||
if not QApplication.instance():
|
||||
return QApplication([])
|
||||
return QApplication.instance()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trading_view(app):
|
||||
"""Create TradingView with mocked backend."""
|
||||
view = TradingView()
|
||||
|
||||
# Mock trading engine
|
||||
mock_engine = Mock()
|
||||
mock_order = Mock(spec=Order)
|
||||
mock_order.id = 1
|
||||
mock_engine.execute_order.return_value = mock_order
|
||||
view.trading_engine = mock_engine
|
||||
|
||||
return view
|
||||
|
||||
|
||||
def test_order_placement_workflow(trading_view):
|
||||
"""Test complete order placement workflow."""
|
||||
# Set up form
|
||||
trading_view.current_exchange_id = 1
|
||||
trading_view.current_symbol = "BTC/USD"
|
||||
trading_view.order_type_combo.setCurrentText("Market")
|
||||
trading_view.side_combo.setCurrentText("Buy")
|
||||
trading_view.quantity_input.setValue(0.1)
|
||||
|
||||
# Place order
|
||||
trading_view._place_order()
|
||||
|
||||
# Verify engine was called
|
||||
trading_view.trading_engine.execute_order.assert_called_once()
|
||||
call_args = trading_view.trading_engine.execute_order.call_args
|
||||
assert call_args[1]['symbol'] == "BTC/USD"
|
||||
assert call_args[1]['side'] == OrderSide.BUY
|
||||
|
||||
|
||||
def test_position_table_update(trading_view):
|
||||
"""Test positions table updates with portfolio data."""
|
||||
# Mock portfolio data
|
||||
mock_portfolio = {
|
||||
'positions': [
|
||||
{
|
||||
'symbol': 'BTC/USD',
|
||||
'quantity': 0.1,
|
||||
'entry_price': 50000,
|
||||
'current_price': 51000,
|
||||
'unrealized_pnl': 100
|
||||
}
|
||||
],
|
||||
'performance': {
|
||||
'current_value': 5100,
|
||||
'unrealized_pnl': 100,
|
||||
'realized_pnl': 0
|
||||
}
|
||||
}
|
||||
|
||||
trading_view.portfolio_tracker.get_current_portfolio = Mock(return_value=mock_portfolio)
|
||||
trading_view._update_positions()
|
||||
|
||||
assert trading_view.positions_table.rowCount() == 1
|
||||
assert trading_view.positions_table.item(0, 0).text() == "BTC/USD"
|
||||
Reference in New Issue
Block a user