Kairo-Jinkendo/backend/tests/test_ap14_roadmap.py
Lars bd64340ca1
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 2m13s
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 18s
Test Suite / playwright-smoke (push) Successful in 12s
AP1.4c: Gate-Checkliste statt Freitext-DoD.
Kriterien-Fortschritt in der Gate-Liste, Modal-UX fuer Checklisten und optionales Erst-Kriterium beim Anlegen.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-11 07:53:38 +02:00

221 lines
7.1 KiB
Python

"""AP1.4 — RoadmapItem & Quality Gate tests."""
from __future__ import annotations
from tests.factories import provision_user_in_tenant
from tests.test_initiatives_actions import (
_auth,
_create_initiative,
_login,
)
def _create_roadmap_item(client, token, initiative_id, **kwargs):
body = {"title": "Gate A", "item_type": "milestone", **kwargs}
return client.post(
f"/api/initiatives/{initiative_id}/roadmap/items",
json=body,
headers=_auth(token),
)
def test_roadmap_item_crud(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
created = _create_roadmap_item(
client,
token,
initiative_id,
sequencing_mode="parallel",
item_type="maturity_stage",
)
assert created.status_code == 201
item = created.json()
assert item["item_type"] == "maturity_stage"
assert item["sequencing_mode"] == "parallel"
listed = client.get(
f"/api/initiatives/{initiative_id}/roadmap/items",
headers=_auth(token),
)
assert listed.status_code == 200
assert len(listed.json()) >= 1
patched = client.patch(
f"/api/roadmap-items/{item['id']}",
json={"status": "at_risk"},
headers=_auth(token),
)
assert patched.status_code == 200
assert patched.json()["status"] == "at_risk"
deleted = client.delete(f"/api/roadmap-items/{item['id']}", headers=_auth(token))
assert deleted.status_code == 204
def test_verify_reached_requires_evidence(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
item = _create_roadmap_item(client, token, initiative_id, status="active").json()
fail = client.post(
f"/api/roadmap-items/{item['id']}/verify-reached",
headers=_auth(token),
)
assert fail.status_code == 400
evidence = client.post(
f"/api/initiatives/{initiative_id}/evidence",
json={
"title": "Nachweis",
"roadmap_item_id": item["id"],
"status": "accepted",
},
headers=_auth(token),
)
assert evidence.status_code == 201
ok = client.post(
f"/api/roadmap-items/{item['id']}/verify-reached",
headers=_auth(token),
)
assert ok.status_code == 200
assert ok.json()["status"] == "reached"
assert ok.json()["verify_reason"] in ("criteria_ready", "evidence_accepted")
def test_milestone_compat_api_uses_roadmap(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
created = client.post(
f"/api/initiatives/{initiative_id}/milestones",
json={"title": "Legacy MS", "status": "planned"},
headers=_auth(token),
)
assert created.status_code == 201
ms_id = created.json()["id"]
roadmap = client.get(
f"/api/initiatives/{initiative_id}/roadmap/items",
headers=_auth(token),
).json()
assert any(i["id"] == ms_id and i["item_type"] == "milestone" for i in roadmap)
def test_steering_snapshot_reads_roadmap_items(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
_create_roadmap_item(client, token, initiative_id, status="active", target_date="2026-12-31")
snap = client.get(
f"/api/initiatives/{initiative_id}/steering-snapshot",
headers=_auth(token),
).json()
assert snap.get("roadmap_items")
assert snap.get("upcoming_milestones")
assert len(snap["upcoming_roadmap_items"]) >= 1
def test_initiative_roadmap_dependencies_list(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
gate_a = _create_roadmap_item(client, token, initiative_id, title="Gate A").json()
gate_b = _create_roadmap_item(client, token, initiative_id, title="Gate B").json()
created = client.post(
f"/api/roadmap-items/{gate_a['id']}/dependencies",
json={"to_item_id": gate_b["id"], "dependency_type": "requires"},
headers=_auth(token),
)
assert created.status_code == 201
listed = client.get(
f"/api/initiatives/{initiative_id}/roadmap/dependencies",
headers=_auth(token),
)
assert listed.status_code == 200
deps = listed.json()
assert len(deps) == 1
assert deps[0]["from_item_id"] == gate_a["id"]
assert deps[0]["to_item_id"] == gate_b["id"]
assert deps[0]["dependency_type"] == "requires"
def test_roadmap_dependency_delete(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
gate_a = _create_roadmap_item(client, token, initiative_id, title="Gate A").json()
gate_b = _create_roadmap_item(client, token, initiative_id, title="Gate B").json()
created = client.post(
f"/api/roadmap-items/{gate_a['id']}/dependencies",
json={"to_item_id": gate_b["id"], "dependency_type": "requires"},
headers=_auth(token),
)
dep_id = created.json()["id"]
deleted = client.delete(
f"/api/roadmap-dependencies/{dep_id}",
headers=_auth(token),
)
assert deleted.status_code == 204
listed = client.get(
f"/api/initiatives/{initiative_id}/roadmap/dependencies",
headers=_auth(token),
)
assert listed.json() == []
def test_roadmap_dependency_duplicate_rejected(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
gate_a = _create_roadmap_item(client, token, initiative_id, title="Gate A").json()
gate_b = _create_roadmap_item(client, token, initiative_id, title="Gate B").json()
body = {"to_item_id": gate_b["id"], "dependency_type": "requires"}
assert client.post(
f"/api/roadmap-items/{gate_a['id']}/dependencies",
json=body,
headers=_auth(token),
).status_code == 201
dup = client.post(
f"/api/roadmap-items/{gate_a['id']}/dependencies",
json=body,
headers=_auth(token),
)
assert dup.status_code == 400
assert "existiert bereits" in dup.json()["detail"]
def test_initiative_roadmap_criteria_progress(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
item = _create_roadmap_item(
client,
token,
initiative_id,
title="Gate mit Kriterium",
initial_criterion_title="Erstes Kriterium",
).json()
progress = client.get(
f"/api/initiatives/{initiative_id}/roadmap/criteria-progress",
headers=_auth(token),
)
assert progress.status_code == 200
body = progress.json()
assert body[item["id"]]["total"] >= 1
assert body[item["id"]]["closed"] == 0