import os import sys import unittest from unittest.mock import patch, MagicMock # Ensure project root is in path sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) class TestArchBoundaryPhase3(unittest.TestCase): def setUp(self) -> None: pass def test_cascade_blocks_simple(self) -> None: """Test that a blocked dependency blocks its immediate dependent.""" from src.models import Ticket, Track t1 = Ticket(id="T1", description="d1", status="blocked") t2 = Ticket(id="T2", description="d2", status="todo", depends_on=["T1"]) track = Track(id="TR1", description="track", tickets=[t1, t2]) # ExecutionEngine should identify T2 as blocked during tick from src.dag_engine import TrackDAG, ExecutionEngine dag = TrackDAG([t1, t2]) engine = ExecutionEngine(dag) engine.tick() self.assertEqual(t2.status, "blocked") self.assertIn("T1", t2.blocked_reason) def test_cascade_blocks_multi_hop(self) -> None: """Test that blocking cascades through multiple dependencies.""" from src.models import Ticket, Track from src.dag_engine import TrackDAG, ExecutionEngine t1 = Ticket(id="T1", description="d1", status="blocked") t2 = Ticket(id="T2", description="d2", status="todo", depends_on=["T1"]) t3 = Ticket(id="T3", description="d3", status="todo", depends_on=["T2"]) dag = TrackDAG([t1, t2, t3]) engine = ExecutionEngine(dag) engine.tick() self.assertEqual(t2.status, "blocked") self.assertEqual(t3.status, "blocked") def test_manual_unblock_restores_todo(self) -> None: """Test that unblocking a task manually works if dependencies are met.""" from src.models import Ticket, Track from src.dag_engine import TrackDAG, ExecutionEngine t1 = Ticket(id="T1", description="d1", status="completed") t2 = Ticket(id="T2", description="d2", status="blocked", blocked_reason="manual") dag = TrackDAG([t1, t2]) engine = ExecutionEngine(dag) # Update status to todo engine.update_task_status("T2", "todo") self.assertEqual(t2.status, "todo") # Next tick should keep it todo (ready) ready = engine.tick() self.assertIn(t2, ready) def test_in_progress_not_blocked(self) -> None: """Test that in_progress tasks are not blocked automatically (only todo).""" from src.models import Ticket, Track from src.dag_engine import TrackDAG, ExecutionEngine t1 = Ticket(id="T1", description="d1", status="blocked") t2 = Ticket(id="T2", description="d2", status="in_progress", depends_on=["T1"]) dag = TrackDAG([t1, t2]) engine = ExecutionEngine(dag) engine.tick() # T2 should remain in_progress because it's already running self.assertEqual(t2.status, "in_progress") def test_execution_engine_tick_cascades_blocks(self) -> None: """Test that ExecutionEngine.tick() triggers the cascading blocks.""" from src.models import Ticket, Track from src.dag_engine import TrackDAG, ExecutionEngine t1 = Ticket(id="T1", description="d1", status="blocked") t2 = Ticket(id="T2", description="d2", status="todo", depends_on=["T1"]) dag = TrackDAG([t1, t2]) engine = ExecutionEngine(dag) engine.tick() self.assertEqual(t2.status, "blocked")