All checks were successful
Deploy Development / deploy (push) Successful in 46s
Test Suite / pytest-backend (push) Successful in 2m18s
Test Suite / lint-backend (push) Successful in 2s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 19s
Test Suite / playwright-smoke (push) Successful in 12s
Attention-Regeln planning_debt/execution_waiting; Portfolio-Kacheln zeigen Steuerungssignale. Co-authored-by: Cursor <cursoragent@cursor.com>
161 lines
5.0 KiB
Python
161 lines
5.0 KiB
Python
"""Unit tests for execution graph engine (AP1.16b)."""
|
|
|
|
from steering.graph.execution_engine import (
|
|
compute_execution_graph_state,
|
|
compute_planning_debt,
|
|
execution_waiting_to_attention_items,
|
|
planning_debt_to_attention_items,
|
|
)
|
|
|
|
|
|
def _action(action_id: str, status: str = "open", **kwargs):
|
|
return {
|
|
"id": action_id,
|
|
"status": status,
|
|
"sort_order": kwargs.get("sort_order", 0),
|
|
"title": kwargs.get("title", action_id),
|
|
"action_kind": kwargs.get("action_kind", "delivery"),
|
|
"roadmap_item_id": kwargs.get("roadmap_item_id"),
|
|
}
|
|
|
|
|
|
def test_requires_blocks_until_predecessor_done():
|
|
actions = [_action("b", "open"), _action("a", "open")]
|
|
deps = [
|
|
{
|
|
"predecessor_action_id": "b",
|
|
"successor_action_id": "a",
|
|
"dependency_kind": "requires",
|
|
}
|
|
]
|
|
state = compute_execution_graph_state(actions=actions, dependencies=deps)
|
|
assert state["items"]["a"]["blocked"] is True
|
|
assert state["items"]["a"]["blocked_by"] == ["b"]
|
|
assert "a" in state["blocked_actions"]
|
|
assert "a" not in state["ready_actions"]
|
|
|
|
actions[0]["status"] = "done"
|
|
state = compute_execution_graph_state(actions=actions, dependencies=deps)
|
|
assert state["items"]["a"]["ready"] is True
|
|
assert "a" in state["ready_actions"]
|
|
|
|
|
|
def test_sequential_fallback_without_edges():
|
|
actions = [
|
|
_action("first", "open", sort_order=0),
|
|
_action("second", "open", sort_order=1),
|
|
]
|
|
state = compute_execution_graph_state(actions=actions, dependencies=[])
|
|
assert state["items"]["first"]["ready"] is True
|
|
assert state["items"]["second"]["blocked"] is True
|
|
assert state["items"]["second"]["blocked_by"] == ["first"]
|
|
|
|
actions[0]["status"] = "done"
|
|
state = compute_execution_graph_state(actions=actions, dependencies=[])
|
|
assert state["items"]["second"]["ready"] is True
|
|
|
|
|
|
def test_blocks_edge():
|
|
actions = [_action("blocker", "in_progress"), _action("blocked", "open")]
|
|
deps = [
|
|
{
|
|
"predecessor_action_id": "blocker",
|
|
"successor_action_id": "blocked",
|
|
"dependency_kind": "blocks",
|
|
}
|
|
]
|
|
state = compute_execution_graph_state(actions=actions, dependencies=deps)
|
|
assert state["items"]["blocked"]["blocked"] is True
|
|
|
|
actions[0]["status"] = "done"
|
|
state = compute_execution_graph_state(actions=actions, dependencies=deps)
|
|
assert state["items"]["blocked"]["ready"] is True
|
|
|
|
|
|
def test_critical_path():
|
|
actions = [
|
|
_action("a", "done", sort_order=0),
|
|
_action("b", "open", sort_order=1),
|
|
_action("c", "open", sort_order=2),
|
|
]
|
|
deps = [
|
|
{
|
|
"predecessor_action_id": "a",
|
|
"successor_action_id": "b",
|
|
"dependency_kind": "requires",
|
|
},
|
|
{
|
|
"predecessor_action_id": "b",
|
|
"successor_action_id": "c",
|
|
"dependency_kind": "requires",
|
|
},
|
|
]
|
|
state = compute_execution_graph_state(actions=actions, dependencies=deps)
|
|
assert state["critical_path"] == ["a", "b", "c"]
|
|
|
|
|
|
def test_planning_debt_active_gate_without_actions():
|
|
actions = [_action("x", roadmap_item_id="gate-2")]
|
|
roadmap_items = [
|
|
{"id": "gate-1", "status": "active", "title": "G5"},
|
|
{"id": "gate-2", "status": "active", "title": "G6"},
|
|
]
|
|
debts = compute_planning_debt(actions=actions, roadmap_items=roadmap_items)
|
|
assert len(debts) == 1
|
|
assert debts[0]["roadmap_item_id"] == "gate-1"
|
|
|
|
|
|
def test_scope_filters_to_gate():
|
|
actions = [
|
|
_action("a", roadmap_item_id="g1", sort_order=0),
|
|
_action("b", roadmap_item_id="g2", sort_order=0),
|
|
]
|
|
state = compute_execution_graph_state(
|
|
actions=actions,
|
|
dependencies=[],
|
|
scope_roadmap_item_id="g1",
|
|
)
|
|
assert set(state["items"].keys()) == {"a"}
|
|
|
|
|
|
def test_planning_debt_attention_items():
|
|
items = planning_debt_to_attention_items(
|
|
initiative_id="init-1",
|
|
debts=[
|
|
{
|
|
"roadmap_item_id": "gate-1",
|
|
"title": "G5 Plan",
|
|
"message": "Aktiver Zielzustand ohne Durchführungsplan",
|
|
}
|
|
],
|
|
)
|
|
assert len(items) == 1
|
|
assert items[0]["kind"] == "planning_debt"
|
|
assert items[0]["initiative_id"] == "init-1"
|
|
assert items[0]["milestone_id"] == "gate-1"
|
|
|
|
|
|
def test_execution_waiting_attention_items():
|
|
actions = [
|
|
_action("a", "open", sort_order=0),
|
|
_action("b", "open", sort_order=1),
|
|
]
|
|
state = compute_execution_graph_state(
|
|
actions=actions,
|
|
dependencies=[
|
|
{
|
|
"predecessor_action_id": "a",
|
|
"successor_action_id": "b",
|
|
"dependency_kind": "requires",
|
|
}
|
|
],
|
|
)
|
|
items = execution_waiting_to_attention_items(
|
|
initiative_id="init-1",
|
|
actions=actions,
|
|
graph_state=state,
|
|
)
|
|
assert len(items) == 1
|
|
assert items[0]["kind"] == "execution_waiting"
|
|
assert items[0]["action_id"] == "b"
|