feat(orchestrator): runner async submit + on_stage 回调
This commit is contained in:
@@ -3,7 +3,7 @@ Tests for sanguo_orchestrator.runner module
|
||||
Tests Orchestrator task coordination
|
||||
"""
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from sanguo_orchestrator.runner import Orchestrator
|
||||
from sanguo_orchestrator.task import TaskState
|
||||
|
||||
@@ -18,12 +18,14 @@ class TestOrchestrator:
|
||||
assert orchestrator.pool.max_workers == 2
|
||||
assert orchestrator._pending == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('sanguo_orchestrator.runner.TaskPool')
|
||||
def test_submit_cta_creates_task(self, mock_pool_class):
|
||||
"""Test submit_cta() creates task and stores spec"""
|
||||
async def test_submit_cta_creates_task(self, mock_pool_class):
|
||||
"""Test async submit_cta() creates task and stores spec per task_id"""
|
||||
mock_pool = Mock()
|
||||
mock_pool_class.return_value = mock_pool
|
||||
mock_pool.submit.return_value = Mock(task_id="test_1")
|
||||
mock_pool.submit_work.return_value = Mock()
|
||||
|
||||
orchestrator = Orchestrator(db_path="test.db", max_workers=2)
|
||||
strategy_class = Mock
|
||||
@@ -33,7 +35,7 @@ class TestOrchestrator:
|
||||
end = "2024-12-31"
|
||||
cfg = Mock()
|
||||
|
||||
task_id = orchestrator.submit_cta(strategy_class, symbol, params, start, end, cfg)
|
||||
task_id = await orchestrator.submit_cta(strategy_class, symbol, params, start, end, cfg)
|
||||
|
||||
# Verify task was submitted to pool
|
||||
mock_pool.submit.assert_called_once()
|
||||
@@ -41,13 +43,14 @@ class TestOrchestrator:
|
||||
assert call_args[0][0] == task_id # task_id
|
||||
assert call_args[0][1] == "cta" # task_type
|
||||
|
||||
# Verify pending spec was stored
|
||||
assert orchestrator._pending["strategy_class"] == strategy_class
|
||||
assert orchestrator._pending["symbol"] == symbol
|
||||
assert orchestrator._pending["params"] == params
|
||||
assert orchestrator._pending["start"] == start
|
||||
assert orchestrator._pending["end"] == end
|
||||
assert orchestrator._pending["cfg"] == cfg
|
||||
# Verify pending spec was stored per task_id (not global)
|
||||
assert task_id in orchestrator._pending
|
||||
assert orchestrator._pending[task_id]["strategy_class"] == strategy_class
|
||||
assert orchestrator._pending[task_id]["symbol"] == symbol
|
||||
assert orchestrator._pending[task_id]["params"] == params
|
||||
assert orchestrator._pending[task_id]["start"] == start
|
||||
assert orchestrator._pending[task_id]["end"] == end
|
||||
assert orchestrator._pending[task_id]["cfg"] == cfg
|
||||
|
||||
assert task_id.startswith("cta_AAPL_")
|
||||
|
||||
@@ -122,4 +125,44 @@ class TestOrchestrator:
|
||||
orchestrator = Orchestrator(db_path="test.db", max_workers=2)
|
||||
result = orchestrator.get_result("nonexistent")
|
||||
|
||||
assert result is None
|
||||
assert result is None
|
||||
|
||||
|
||||
class TestOrchestratorAsync:
|
||||
"""Test async orchestrator submit and on_stage callback"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_submit_cta_returns_task_id_and_submits(self):
|
||||
"""Test async submit_cta() returns task_id and submits to pool"""
|
||||
orchestrator = Orchestrator(db_path="/tmp/t.db")
|
||||
orchestrator.pool.executor = Mock()
|
||||
mock_future = Mock()
|
||||
orchestrator.pool.executor.submit.return_value = mock_future
|
||||
|
||||
with patch("sanguo_backtest.cta_engine.run_cta_backtest"):
|
||||
task_id = await orchestrator.submit_cta(
|
||||
Mock(), "600000", {}, "2024-01-01", "2024-06-30", cfg=Mock()
|
||||
)
|
||||
|
||||
assert task_id.startswith("cta_600000")
|
||||
orchestrator.pool.executor.submit.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_on_stage_callback_called(self):
|
||||
"""Test set_on_stage() callback fires on task completion"""
|
||||
from sanguo_orchestrator.runner import Orchestrator
|
||||
from sanguo_orchestrator.task import TaskState
|
||||
|
||||
orchestrator = Orchestrator(db_path="/tmp/t.db")
|
||||
orchestrator.pool.executor = Mock()
|
||||
mock_future = Mock()
|
||||
orchestrator.pool.executor.submit.return_value = mock_future
|
||||
|
||||
cb = AsyncMock()
|
||||
orchestrator.set_on_stage(cb)
|
||||
|
||||
# Simulate task completion callback (with None-guard for unknown task)
|
||||
await orchestrator._on_done("t1", {"statistics": {}})
|
||||
|
||||
# Callback should fire even though task t1 was never submitted
|
||||
cb.assert_called_once()
|
||||
Reference in New Issue
Block a user