109 lines
3.9 KiB
Python
109 lines
3.9 KiB
Python
|
|
"""Tests for main.py visualization workflow."""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import sqlite3
|
||
|
|
import tempfile
|
||
|
|
from pathlib import Path
|
||
|
|
from unittest.mock import patch, MagicMock
|
||
|
|
|
||
|
|
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||
|
|
|
||
|
|
# Mock typer to avoid import issues in tests
|
||
|
|
sys.modules['typer'] = MagicMock()
|
||
|
|
|
||
|
|
from storage import Storage
|
||
|
|
from strategies import DefaultStrategy
|
||
|
|
from visualizer import Visualizer
|
||
|
|
|
||
|
|
|
||
|
|
def test_main_workflow_separation():
|
||
|
|
"""Test that main.py workflow properly separates strategy and visualization."""
|
||
|
|
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as tmp_file:
|
||
|
|
db_path = Path(tmp_file.name)
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Create minimal test database
|
||
|
|
with sqlite3.connect(str(db_path)) as conn:
|
||
|
|
conn.execute("""
|
||
|
|
CREATE TABLE book (
|
||
|
|
id INTEGER PRIMARY KEY,
|
||
|
|
bids TEXT NOT NULL,
|
||
|
|
asks TEXT NOT NULL,
|
||
|
|
timestamp INTEGER NOT NULL
|
||
|
|
)
|
||
|
|
""")
|
||
|
|
conn.execute("""
|
||
|
|
CREATE TABLE trades (
|
||
|
|
id INTEGER PRIMARY KEY,
|
||
|
|
trade_id REAL NOT NULL,
|
||
|
|
price REAL NOT NULL,
|
||
|
|
size REAL NOT NULL,
|
||
|
|
side TEXT NOT NULL,
|
||
|
|
timestamp INTEGER NOT NULL
|
||
|
|
)
|
||
|
|
""")
|
||
|
|
|
||
|
|
# Insert minimal test data
|
||
|
|
bids = "[(50000.0, 10.0, 0, 1)]"
|
||
|
|
asks = "[(50001.0, 5.0, 0, 1)]"
|
||
|
|
conn.execute("INSERT INTO book (id, bids, asks, timestamp) VALUES (?, ?, ?, ?)",
|
||
|
|
(1, bids, asks, 1000))
|
||
|
|
conn.execute("INSERT INTO trades (id, trade_id, price, size, side, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
|
||
|
|
(1, 1.0, 50000.0, 3.0, "buy", 1000))
|
||
|
|
conn.commit()
|
||
|
|
|
||
|
|
# Test the new main.py workflow
|
||
|
|
storage = Storage("BTC-USDT")
|
||
|
|
strategy = DefaultStrategy("BTC-USDT") # No visualization parameter
|
||
|
|
|
||
|
|
# Mock visualizer to avoid GUI issues in tests
|
||
|
|
with patch('matplotlib.pyplot.subplots') as mock_subplots:
|
||
|
|
mock_fig = type('MockFig', (), {'canvas': type('MockCanvas', (), {'draw_idle': lambda: None})()})()
|
||
|
|
mock_axes = [type('MockAx', (), {'clear': lambda: None})() for _ in range(4)]
|
||
|
|
mock_subplots.return_value = (mock_fig, tuple(mock_axes))
|
||
|
|
|
||
|
|
visualizer = Visualizer(window_seconds=60, max_bars=500)
|
||
|
|
|
||
|
|
# This simulates the new main.py workflow
|
||
|
|
strategy.set_db_path(db_path)
|
||
|
|
visualizer.set_db_path(db_path)
|
||
|
|
|
||
|
|
storage.build_booktick_from_db(db_path, None)
|
||
|
|
|
||
|
|
# Strategy analyzes metrics (no visualization)
|
||
|
|
strategy.on_booktick(storage.book)
|
||
|
|
|
||
|
|
# Verify strategy has database path but no visualizer
|
||
|
|
assert strategy._db_path == db_path
|
||
|
|
assert not hasattr(strategy, 'visualizer') or strategy.visualizer is None
|
||
|
|
|
||
|
|
# Verify visualizer can access database
|
||
|
|
assert visualizer._db_path == db_path
|
||
|
|
|
||
|
|
# Verify visualizer can load metrics
|
||
|
|
metrics = visualizer._load_stored_metrics(1000, 1000)
|
||
|
|
assert len(metrics) == 1
|
||
|
|
|
||
|
|
# Test visualization update (should work independently)
|
||
|
|
with patch.object(visualizer, '_draw') as mock_draw:
|
||
|
|
visualizer.update_from_book(storage.book)
|
||
|
|
mock_draw.assert_called_once()
|
||
|
|
|
||
|
|
finally:
|
||
|
|
db_path.unlink(missing_ok=True)
|
||
|
|
|
||
|
|
|
||
|
|
def test_strategy_has_no_visualization_dependency():
|
||
|
|
"""Test that strategy no longer depends on visualization."""
|
||
|
|
strategy = DefaultStrategy("BTC-USDT")
|
||
|
|
|
||
|
|
# Strategy should not have visualizer attribute
|
||
|
|
assert not hasattr(strategy, 'visualizer') or strategy.visualizer is None
|
||
|
|
|
||
|
|
# Strategy should work without any visualization setup
|
||
|
|
from models import Book
|
||
|
|
book = Book()
|
||
|
|
|
||
|
|
# Should not raise any errors
|
||
|
|
strategy.on_booktick(book)
|