Kairo-Jinkendo/backend/tests/test_ap24_steering_elements.py
Lars a3b14d9c00
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 3m56s
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 13s
feat(steering): Backlog-Vokabular aus Methoden-Vertrag (ADP P3)
backlog_epic_hierarchy als Steuerungselement, effektive steering_elements im Operating Context, Backend-Guards und UI ohne Slice-Ifs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 10:44:38 +02:00

173 lines
5.7 KiB
Python

"""AP2.4 — Steering elements and method contract."""
from __future__ import annotations
from steering.elements.registry import validate_steering_element_keys
from steering.methods.registry import get_method, list_compatible_methods, ui_features_as_dict
from steering.graph.profiles import get_graph_profile
def test_all_methods_have_valid_steering_elements():
from steering.methods.registry import list_methods
for method in list_methods():
validate_steering_element_keys(method.steering_elements)
def test_sequential_dependency_contract():
method = get_method("sequential_dependency")
assert method is not None
assert "critical_path" in method.steering_elements
assert "criticalPathControl" in method.ui_features
assert get_graph_profile("sequential_dependency").enforce_gate_blocking is True
def test_continuous_product_ui_features_from_method():
method = get_method("continuous_product")
assert method is not None
features = ui_features_as_dict(method.ui_features)
assert features["continuousProductWorkMode"] is True
assert features["steeringSnapshotOnWorkSprint"] is True
def test_list_compatible_methods_linear_excludes_queue_pull():
methods = list_compatible_methods("initiative.linear_project")
keys = {m.key for m in methods}
assert "sequential_dependency" in keys
assert "queue_pull" not in keys
assert "agile_iteration" not in keys # modifier, not primary switch target
def test_agile_iteration_composes_with_matrix():
method = get_method("agile_iteration")
assert method is not None
assert method.composes_with == frozenset(
{
"continuous_product",
"sequential_dependency",
"recurring_control",
}
)
assert "program_delivery" not in method.composes_with
assert "backlog_epic_hierarchy" in method.steering_elements
def test_list_composable_modifiers_for_linear():
from steering.methods.registry import list_composable_modifiers
modifiers = list_composable_modifiers("sequential_dependency")
assert len(modifiers) == 1
assert modifiers[0].key == "agile_iteration"
def test_list_compatible_methods_product_excludes_maturity():
methods = list_compatible_methods("initiative.product")
keys = {m.key for m in methods}
assert "continuous_product" in keys
assert "maturity_progression" not in keys
def test_product_milestone_driven_compatible_with_generic():
from steering.methods.registry import list_compatible_methods
methods = list_compatible_methods("initiative.generic")
keys = {m.key for m in methods}
assert "product_milestone_driven" in keys
def test_operating_context_includes_method_contract(client):
from tests.factories import provision_user_in_tenant
from tests.test_initiatives_actions import _auth, _create_initiative, _login
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
created = _create_initiative(
client,
token,
title="Linear Contract",
archetype_key="initiative.linear_project",
)
initiative_id = created.json()["id"]
res = client.get(
f"/api/initiatives/{initiative_id}/operating-context",
headers=_auth(token),
)
assert res.status_code == 200
body = res.json()
assert "critical_path" in body["steering_elements"]
assert body["ui_features"].get("criticalPathControl") is True
assert "graph_profile" in body
assert body["compatible_methods"]
assert any(m["key"] == "sequential_dependency" for m in body["compatible_methods"])
def test_steering_methods_filtered_by_archetype(client):
from tests.factories import provision_user_in_tenant
from tests.test_initiatives_actions import _auth, _login
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
res = client.get(
"/api/steering/methods",
params={"archetype_key": "initiative.product"},
headers=_auth(token),
)
assert res.status_code == 200
keys = {m["key"] for m in res.json()}
assert "continuous_product" in keys
assert "maturity_progression" not in keys
product = next(m for m in res.json() if m["key"] == "continuous_product")
assert "steering_elements" in product
assert product["is_default"] is True
def test_queue_pull_rejected_on_linear(client):
from tests.factories import provision_user_in_tenant
from tests.test_initiatives_actions import _auth, _create_initiative, _login
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
created = _create_initiative(
client,
token,
title="Linear Block Queue",
archetype_key="initiative.linear_project",
)
initiative_id = created.json()["id"]
res = client.patch(
f"/api/steering/initiatives/{initiative_id}/context",
json={"method_key": "queue_pull"},
headers=_auth(token),
)
assert res.status_code == 400
assert "kompatibel" in res.json()["detail"].lower()
def test_operating_context_composable_modifiers_linear(client):
from tests.factories import provision_user_in_tenant
from tests.test_initiatives_actions import _auth, _create_initiative, _login
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
created = _create_initiative(
client,
token,
title="Linear Modifiers",
archetype_key="initiative.linear_project",
)
initiative_id = created.json()["id"]
ctx = client.get(
f"/api/initiatives/{initiative_id}/operating-context",
headers=_auth(token),
)
assert ctx.status_code == 200
modifiers = ctx.json().get("composable_modifiers") or []
assert any(m["key"] == "agile_iteration" for m in modifiers)