8f11340b38
Per post_module_taxonomy_de_cruft_20260627 Phase 2 (FR7). Each
'from src.models import X' for a moved class is rewritten to
'from src.<destination> import X':
Ticket, Track, WorkerContext, TrackState, TrackMetadata,
ThinkingSegment, EMPTY_TRACK_STATE -> src.mma
ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles,
ProjectScreenshots, ProjectDiscussion, EMPTY_PROJECT_CONTEXT -> src.project
FileItem, Preset, ContextPreset, ContextFileEntry,
NamedViewPreset -> src.project_files
Tool, ToolPreset -> src.tool_presets
BiasProfile -> src.tool_bias
TextEditorConfig, ExternalEditorConfig,
EMPTY_TEXT_EDITOR_CONFIG -> src.external_editor
Persona -> src.personas
WorkspaceProfile -> src.workspace_manager
MCPServerConfig, MCPConfiguration, VectorStoreConfig,
RAGConfig, load_mcp_config -> src.mcp_client
NOT touched (kept on src.models; Phase 3 or Phase 4 will move them):
GenerateRequest, ConfirmRequest, DEFAULT_TOOL_CATEGORIES, Metadata, PROVIDERS
Migration was performed by the one-time script
scripts/tier2/artifacts/post_module_taxonomy_de_cruft_20260627/migrate_imports.py
which uses a class-to-module map and re.sub() to rewrite each
'from src.models import X' line.
Total: 85 import lines rewritten across 71 files.
Note: this commit depends on the v2 SHIPPED work
(origin/tier2/module_taxonomy_refactor_20260627) being merged into
this branch NEXT. On master (without the v2 SHIPPED commits), the
destination modules do not exist and these imports would fail.
178 lines
5.7 KiB
Python
178 lines
5.7 KiB
Python
from src.mma 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"] |