Files
manual_slop/tests/test_dag_engine.py

108 lines
4.6 KiB
Python

"""
ANTI-SIMPLIFICATION: These tests verify the core Directed Acyclic Graph (DAG) execution engine logic.
They MUST NOT be simplified. They ensure that dependency resolution, cycle detection,
and topological sorting work perfectly to prevent catastrophic orchestrator deadlocks.
"""
import pytest
from src.models import Ticket
from src.dag_engine import TrackDAG
def test_get_ready_tasks_linear():
"""
Verifies ready tasks detection in a simple linear dependency chain.
"""
t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1")
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
dag = TrackDAG([t1, t2])
ready = dag.get_ready_tasks()
assert len(ready) == 1
assert ready[0].id == "T1"
def test_get_ready_tasks_branching():
"""
Verifies ready tasks detection in a branching dependency graph where multiple tasks
are unlocked simultaneously after a prerequisite is met.
"""
t1 = Ticket(id="T1", description="desc", status="completed", assigned_to="worker1")
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
t3 = Ticket(id="T3", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
dag = TrackDAG([t1, t2, t3])
ready = dag.get_ready_tasks()
assert len(ready) == 2
ids = [t.id for t in ready]
assert "T2" in ids
assert "T3" in ids
def test_has_cycle_no_cycle():
"""
Validates that an acyclic graph is correctly identified as not having cycles.
"""
t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1")
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
dag = TrackDAG([t1, t2])
assert dag.has_cycle() is False
def test_has_cycle_direct_cycle():
"""
Validates that a direct cycle (A depends on B, B depends on A) is correctly detected.
"""
t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1", depends_on=["T2"])
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
dag = TrackDAG([t1, t2])
assert dag.has_cycle() is True
def test_has_cycle_indirect_cycle():
"""
Validates that an indirect cycle (A->B->C->A) is correctly detected.
"""
t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1", depends_on=["T3"])
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
t3 = Ticket(id="T3", description="desc", status="todo", assigned_to="worker1", depends_on=["T2"])
dag = TrackDAG([t1, t2, t3])
assert dag.has_cycle() is True
def test_has_cycle_complex_no_cycle():
"""
Validates cycle detection in a complex graph that merges branches but remains acyclic.
"""
t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1")
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
t3 = Ticket(id="T3", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
t4 = Ticket(id="T4", description="desc", status="todo", assigned_to="worker1", depends_on=["T2", "T3"])
dag = TrackDAG([t1, t2, t3, t4])
assert dag.has_cycle() is False
def test_get_ready_tasks_multiple_deps():
"""
Validates that a task is not marked ready until ALL of its dependencies are completed.
"""
t1 = Ticket(id="T1", description="desc", status="completed", assigned_to="worker1")
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1")
t3 = Ticket(id="T3", description="desc", status="todo", assigned_to="worker1", depends_on=["T1", "T2"])
dag = TrackDAG([t1, t2, t3])
# Only T2 is ready because T3 depends on T2 (todo)
ready = dag.get_ready_tasks()
assert len(ready) == 1
assert ready[0].id == "T2"
def test_topological_sort():
"""
Verifies that tasks are correctly ordered by dependencies regardless of input order.
"""
t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1")
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
dag = TrackDAG([t2, t1]) # Out of order input
sorted_tasks = dag.topological_sort()
# Topological sort returns list of IDs in current implementation
assert sorted_tasks == ["T1", "T2"]
def test_topological_sort_cycle():
"""
Verifies that topological sorting safely aborts and raises ValueError when a cycle is present.
"""
t1 = Ticket(id="T1", description="desc", status="todo", assigned_to="worker1", depends_on=["T2"])
t2 = Ticket(id="T2", description="desc", status="todo", assigned_to="worker1", depends_on=["T1"])
dag = TrackDAG([t1, t2])
with pytest.raises(ValueError, match="Dependency cycle detected"):
dag.topological_sort()