Private
Public Access
0
0
Files
manual_slop/tests/test_mma_models.py
ed ca254bac41 fix(imports): break models<->dag_engine circular dependency
Track.get_executable_tickets (in models.py) called TrackDAG at
runtime, forcing a top-level import of src.dag_engine into models.py
and creating a 2-cycle that broke whichever module loaded second
(Ticket was not yet defined when models.py loaded first; TrackDAG
was not yet defined when dag_engine.py loaded first).

Fix: hoist the method out of the Track dataclass and into a free
function get_executable_tickets(track) in dag_engine.py. models.py
no longer needs TrackDAG at all, so the cycle is one-directional
(models -> dag_engine) and resolves cleanly in any import order.

Tests updated:
- tests/test_mma_models.py: import get_executable_tickets and call
  it instead of track.get_executable_tickets() (4 call sites)
- tests/test_conductor_engine_v2.py: comment update

Verified both import orders resolve cleanly:
  forward:  import src.models; import src.dag_engine  -> OK
  reverse:  import src.dag_engine; import src.models  -> OK
34 tests pass (test_mma_models, test_dag_engine, test_execution_engine,
test_arch_boundary_phase3, test_track_state_schema).
2026-06-06 13:30:18 -04:00

178 lines
5.7 KiB
Python

from src.models import Ticket, Track, WorkerContext
from src.dag_engine import get_executable_tickets
def test_ticket_instantiation() -> None:
"""
Verifies that a Ticket can be instantiated with its required fields:
id, description, status, assigned_to.
"""
ticket_id = "T1"
description = "Implement surgical code changes"
status = "todo"
assigned_to = "tier3-worker"
ticket = Ticket(
id=ticket_id,
description=description,
status=status,
assigned_to=assigned_to
)
assert ticket.id == ticket_id
assert ticket.description == description
assert ticket.status == status
assert ticket.assigned_to == assigned_to
assert ticket.depends_on == []
def test_ticket_with_dependencies() -> None:
"""
Verifies that a Ticket can store dependencies.
"""
ticket = Ticket(
id="T2",
description="Write code",
status="todo",
assigned_to="worker-1",
depends_on=["T1"]
)
assert ticket.depends_on == ["T1"]
def test_track_instantiation() -> None:
"""
Verifies that a Track can be instantiated with its required fields:
id, description, and a list of Tickets.
"""
ticket1 = Ticket(id="T1", description="Task 1", status="todo", assigned_to="a")
ticket2 = Ticket(id="T2", description="Task 2", status="todo", assigned_to="b")
track_id = "TRACK-1"
track_desc = "Implement MMA Models"
tickets = [ticket1, ticket2]
track = Track(
id=track_id,
description=track_desc,
tickets=tickets
)
assert track.id == track_id
assert track.description == track_desc
assert len(track.tickets) == 2
assert track.tickets[0].id == "T1"
assert track.tickets[1].id == "T2"
def test_track_can_handle_empty_tickets() -> None:
"""
Verifies that a Track can be instantiated with an empty list of tickets.
"""
track = Track(id="TRACK-2", description="Empty Track", tickets=[])
assert track.tickets == []
def test_worker_context_instantiation() -> None:
"""
Verifies that a WorkerContext can be instantiated with ticket_id,
model_name, and messages.
"""
ticket_id = "T1"
model_name = "gemini-2.0-flash-lite"
messages = [
{"role": "user", "content": "Hello"},
{"role": "assistant", "content": "Hi there!"}
]
context = WorkerContext(
ticket_id=ticket_id,
model_name=model_name,
messages=messages
)
assert context.ticket_id == ticket_id
assert context.model_name == model_name
assert context.messages == messages
def test_ticket_mark_blocked() -> None:
"""
Verifies that ticket.mark_blocked(reason) sets the status to 'blocked'.
Note: The reason field might need to be added to the Ticket class.
"""
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="a")
ticket.mark_blocked("Waiting for API key")
assert ticket.status == "blocked"
def test_ticket_mark_complete() -> None:
"""
Verifies that ticket.mark_complete() sets the status to 'completed'.
"""
ticket = Ticket(id="T1", description="Task 1", status="todo", assigned_to="a")
ticket.mark_complete()
assert ticket.status == "completed"
def test_track_get_executable_tickets() -> None:
"""
Verifies that get_executable_tickets(track) returns only 'todo' tickets
whose dependencies are all 'completed'.
"""
# T1: todo, no deps -> executable
t1 = Ticket(id="T1", description="T1", status="todo", assigned_to="a")
# T2: todo, deps [T1] -> not executable (T1 is todo)
t2 = Ticket(id="T2", description="T2", status="todo", assigned_to="a", depends_on=["T1"])
# T3: todo, deps [T4] -> not executable (T4 is blocked)
t3 = Ticket(id="T3", description="T3", status="todo", assigned_to="a", depends_on=["T4"])
# T4: blocked, no deps -> not executable (not 'todo')
t4 = Ticket(id="T4", description="T4", status="blocked", assigned_to="a")
# T5: completed, no deps -> not executable (not 'todo')
t5 = Ticket(id="T5", description="T5", status="completed", assigned_to="a")
# T6: todo, deps [T5] -> executable (T5 is completed)
t6 = Ticket(id="T6", description="T6", status="todo", assigned_to="a", depends_on=["T5"])
track = Track(id="TR1", description="Track 1", tickets=[t1, t2, t3, t4, t5, t6])
executable = get_executable_tickets(track)
executable_ids = [t.id for t in executable]
assert "T1" in executable_ids
assert "T6" in executable_ids
assert len(executable_ids) == 2
def test_track_get_executable_tickets_complex() -> None:
"""
Verifies executable tickets with complex dependency chains.
Chain: T1 (comp) -> T2 (todo) -> T3 (todo)
T4 (comp) -> T3
T5 (todo) -> T3
"""
t1 = Ticket(id="T1", description="T1", status="completed", assigned_to="a")
t2 = Ticket(id="T2", description="T2", status="todo", assigned_to="a", depends_on=["T1"])
t3 = Ticket(id="T3", description="T3", status="todo", assigned_to="a", depends_on=["T2", "T4", "T5"])
t4 = Ticket(id="T4", description="T4", status="completed", assigned_to="a")
t5 = Ticket(id="T5", description="T5", status="todo", assigned_to="a")
track = Track(id="TR1", description="Track 1", tickets=[t1, t2, t3, t4, t5])
# At this point:
# T1 is completed
# T4 is completed
# T2 is todo, depends on T1 (completed) -> Executable
# T5 is todo, no deps -> Executable
# T3 is todo, depends on T2 (todo), T4 (completed), T5 (todo) -> Not executable
executable = get_executable_tickets(track)
executable_ids = sorted([t.id for t in executable])
assert executable_ids == ["T2", "T5"]
# Mark T2 complete
t2.mark_complete()
# T3 still depends on T5
executable = get_executable_tickets(track)
executable_ids = sorted([t.id for t in executable])
assert executable_ids == ["T5"]
# Mark T5 complete
t5.mark_complete()
# Now T3 should be executable
executable = get_executable_tickets(track)
executable_ids = sorted([t.id for t in executable])
assert executable_ids == ["T3"]