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
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>
203 lines
6.3 KiB
Python
203 lines
6.3 KiB
Python
"""AP2.3a — Operating Context API."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from auth import AUTH_HEADER
|
|
from services import operating_context as operating_context_service
|
|
from tests.factories import provision_user_in_tenant
|
|
from tests.test_initiatives_actions import _auth, _create_initiative, _login
|
|
|
|
|
|
def test_operating_context_product(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title="Product Alpha",
|
|
archetype_key="initiative.product",
|
|
)
|
|
assert created.status_code == 201
|
|
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 body["initiative_id"] == initiative_id
|
|
assert body["archetype_key"] == "initiative.product"
|
|
assert body["method_key"] == "continuous_product"
|
|
assert "backlog_epic_hierarchy" in body["steering_elements"]
|
|
assert body["ui_profile"]["planDefaultRoute"] == "/plan/inbox"
|
|
assert body["ui_profile"]["workDefaultRoute"] == "/work/sprint"
|
|
assert "work_cycles" in body["data_slices"]
|
|
assert body["ui_profile"]["processSteps"]
|
|
assert body["method_capabilities"]["next_action_strategy_key"]
|
|
|
|
|
|
def test_operating_context_linear(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title="Linear Proj",
|
|
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 body["method_key"] == "sequential_dependency"
|
|
assert "critical_path" in body["steering_elements"]
|
|
assert body["ui_profile"]["planDefaultRoute"] == "/plan/gates"
|
|
assert "work_cycles" not in body["data_slices"]
|
|
|
|
|
|
def test_operating_context_maturity(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title="Maturity",
|
|
archetype_key="initiative.maturity_journey",
|
|
)
|
|
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 body["method_key"] == "maturity_progression"
|
|
assert any(s["key"] == "journey" for s in body["ui_profile"]["processSteps"])
|
|
|
|
|
|
def test_operating_context_program(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title="Program",
|
|
archetype_key="initiative.program",
|
|
)
|
|
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 body["method_key"] == "program_delivery"
|
|
assert body["ui_profile"]["planOutlineKeys"] is not None
|
|
|
|
|
|
def test_operating_context_dispute_case(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title="Dispute",
|
|
archetype_key="initiative.dispute_case",
|
|
)
|
|
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 body["method_key"] == "dispute_procedure"
|
|
assert body["ui_profile"]["planDefaultRoute"] == "/plan/profile"
|
|
assert {s["key"] for s in body["ui_profile"]["processSteps"]} == {"work", "control"}
|
|
|
|
|
|
def test_operating_context_generic_fallback(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title="Generic",
|
|
archetype_key="initiative.generic",
|
|
)
|
|
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 body["method_key"] == "generic_operating"
|
|
assert body["ui_profile"]["planDefaultRoute"] == "/plan/profile"
|
|
assert body["ui_profile"]["processSteps"] == []
|
|
|
|
|
|
def test_operating_context_support_queue_no_work_cycles(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title="Support Queue",
|
|
archetype_key="initiative.support_queue",
|
|
)
|
|
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 body["method_key"] == "queue_pull"
|
|
assert "work_cycles" not in body["data_slices"]
|
|
assert "sprint" not in (body["ui_profile"].get("planOutlineKeys") or [])
|
|
|
|
|
|
def test_operating_context_not_found(client):
|
|
user = provision_user_in_tenant(tenant_role="member")
|
|
token = _login(client, user)
|
|
|
|
res = client.get(
|
|
"/api/initiatives/00000000-0000-0000-0000-000000000099/operating-context",
|
|
headers=_auth(token),
|
|
)
|
|
assert res.status_code == 404
|
|
|
|
|
|
def test_resolve_ui_profile_merges_seed_slices_when_db_partial(monkeypatch):
|
|
monkeypatch.setattr(
|
|
operating_context_service,
|
|
"_load_ui_profile_from_db",
|
|
lambda _key: {
|
|
"planOutlineKeys": ["profile", "inbox", "sprint", "gates"],
|
|
"planDefaultRoute": "/plan/inbox",
|
|
},
|
|
)
|
|
profile = operating_context_service._resolve_ui_profile("initiative.product")
|
|
assert profile["planOutlineKeys"] == ["profile", "inbox", "sprint", "gates"]
|
|
assert "backlog" in profile["dataSlices"]
|
|
assert "work_cycles" in profile["dataSlices"]
|
|
assert profile["omCapabilities"]
|