"""Tests: _run_pipeline halts on first failure and returns False.""" from __future__ import annotations from unittest.mock import AsyncMock, MagicMock, patch import pytest def _make_mock_proc(returncode: int) -> AsyncMock: proc = AsyncMock() proc.returncode = returncode proc.communicate = AsyncMock(return_value=(b"", None)) return proc @pytest.mark.asyncio async def test_pipeline_halts_on_first_failure(): """If first subprocess fails, downstream commands must not run.""" from apps.web.paper_trading_service import AutoScheduler sched = AutoScheduler() sched._log = lambda msg: None sched._dry_run = False create_calls = [] async def fake_create_subprocess(*args, **kwargs): create_calls.append(args[0]) # First call fails, others would succeed rc = 1 if len(create_calls) == 1 else 0 return _make_mock_proc(rc) with patch("asyncio.create_subprocess_exec", side_effect=fake_create_subprocess): cmds = [["python", "-m", "step1"], ["python", "-m", "step2"], ["python", "-m", "step3"]] ok = await sched._run_pipeline(cmds) assert ok is False # Only first command ran assert len(create_calls) == 1 @pytest.mark.asyncio async def test_pipeline_returns_true_on_all_success(): """All steps succeed → returns True.""" from apps.web.paper_trading_service import AutoScheduler sched = AutoScheduler() sched._log = lambda msg: None sched._dry_run = False async def fake_create_subprocess(*args, **kwargs): return _make_mock_proc(0) with patch("asyncio.create_subprocess_exec", side_effect=fake_create_subprocess): cmds = [["python", "-m", "step1"], ["python", "-m", "step2"]] ok = await sched._run_pipeline(cmds) assert ok is True @pytest.mark.asyncio async def test_pipeline_exception_treated_as_failure(): """If create_subprocess_exec raises, pipeline returns False and stops.""" from apps.web.paper_trading_service import AutoScheduler sched = AutoScheduler() sched._log = lambda msg: None sched._dry_run = False call_count = 0 async def fake_create_subprocess(*args, **kwargs): nonlocal call_count call_count += 1 if call_count == 1: raise OSError("boom") return _make_mock_proc(0) with patch("asyncio.create_subprocess_exec", side_effect=fake_create_subprocess): cmds = [["python", "-m", "step1"], ["python", "-m", "step2"]] ok = await sched._run_pipeline(cmds) assert ok is False assert call_count == 1