All checks were successful
Deploy Development / deploy (push) Successful in 48s
Test Suite / pytest-backend (push) Successful in 3m54s
Test Suite / lint-backend (push) Successful in 3s
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 15s
Epics als Backlog-Container mit Baum-UI, Convert-Guard und pytest-Abdeckung — Roll-up bleibt für P4 offen. Co-authored-by: Cursor <cursoragent@cursor.com>
162 lines
5.0 KiB
Python
162 lines
5.0 KiB
Python
"""AP2.2f — Backlog Epic-Hierarchie (parent_backlog_id + item_kind epic)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from tests.factories import provision_user_in_tenant
|
|
from tests.test_initiatives_actions import _auth, _create_initiative, _login
|
|
|
|
|
|
def _create_product_initiative(client, token, title="Epic Product"):
|
|
created = _create_initiative(
|
|
client,
|
|
token,
|
|
title=title,
|
|
archetype_key="initiative.product",
|
|
)
|
|
assert created.status_code == 201
|
|
return created.json()["id"]
|
|
|
|
|
|
def test_create_epic_and_story_under_epic(client):
|
|
user = provision_user_in_tenant(tenant_role="admin")
|
|
token = _login(client, user)
|
|
initiative_id = _create_product_initiative(client, token)
|
|
|
|
epic = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={"title": "Checkout Epic", "item_kind": "epic", "status": "accepted"},
|
|
headers=_auth(token),
|
|
)
|
|
assert epic.status_code == 201
|
|
epic_id = epic.json()["id"]
|
|
assert epic.json()["item_kind"] == "epic"
|
|
assert epic.json().get("parent_backlog_id") is None
|
|
|
|
story = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={
|
|
"title": "Login Story",
|
|
"item_kind": "story",
|
|
"parent_backlog_id": epic_id,
|
|
"status": "accepted",
|
|
},
|
|
headers=_auth(token),
|
|
)
|
|
assert story.status_code == 201
|
|
assert story.json()["parent_backlog_id"] == epic_id
|
|
|
|
listed = client.get(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
headers=_auth(token),
|
|
)
|
|
assert listed.status_code == 200
|
|
by_id = {item["id"]: item for item in listed.json()}
|
|
assert by_id[story.json()["id"]]["parent_backlog_id"] == epic_id
|
|
|
|
|
|
def test_epic_cannot_convert_to_action(client):
|
|
user = provision_user_in_tenant(tenant_role="admin")
|
|
token = _login(client, user)
|
|
initiative_id = _create_product_initiative(client, token, title="Epic Convert Block")
|
|
|
|
epic = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={"title": "Big Epic", "item_kind": "epic", "status": "accepted"},
|
|
headers=_auth(token),
|
|
)
|
|
epic_id = epic.json()["id"]
|
|
|
|
converted = client.post(
|
|
f"/api/backlog/{epic_id}/convert-to-action",
|
|
json={},
|
|
headers=_auth(token),
|
|
)
|
|
assert converted.status_code == 400
|
|
assert "Epic" in converted.json()["detail"]
|
|
|
|
|
|
def test_story_under_epic_converts_to_action(client):
|
|
user = provision_user_in_tenant(tenant_role="admin")
|
|
token = _login(client, user)
|
|
initiative_id = _create_product_initiative(client, token, title="Story Convert")
|
|
|
|
epic = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={"title": "Epic", "item_kind": "epic", "status": "accepted"},
|
|
headers=_auth(token),
|
|
)
|
|
epic_id = epic.json()["id"]
|
|
|
|
story = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={
|
|
"title": "Story",
|
|
"item_kind": "story",
|
|
"parent_backlog_id": epic_id,
|
|
"status": "accepted",
|
|
},
|
|
headers=_auth(token),
|
|
)
|
|
story_id = story.json()["id"]
|
|
|
|
converted = client.post(
|
|
f"/api/backlog/{story_id}/convert-to-action",
|
|
json={},
|
|
headers=_auth(token),
|
|
)
|
|
assert converted.status_code == 201
|
|
assert converted.json()["action"]["title"] == "Story"
|
|
|
|
|
|
def test_cannot_delete_epic_with_children(client):
|
|
user = provision_user_in_tenant(tenant_role="admin")
|
|
token = _login(client, user)
|
|
initiative_id = _create_product_initiative(client, token, title="Epic Delete Guard")
|
|
|
|
epic = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={"title": "Epic", "item_kind": "epic", "status": "accepted"},
|
|
headers=_auth(token),
|
|
)
|
|
epic_id = epic.json()["id"]
|
|
|
|
client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={
|
|
"title": "Child Story",
|
|
"item_kind": "story",
|
|
"parent_backlog_id": epic_id,
|
|
"status": "new",
|
|
},
|
|
headers=_auth(token),
|
|
)
|
|
|
|
deleted = client.delete(f"/api/backlog/{epic_id}", headers=_auth(token))
|
|
assert deleted.status_code == 400
|
|
assert "untergeordneten" in deleted.json()["detail"]
|
|
|
|
|
|
def test_epic_cannot_have_parent_backlog(client):
|
|
user = provision_user_in_tenant(tenant_role="admin")
|
|
token = _login(client, user)
|
|
initiative_id = _create_product_initiative(client, token, title="Epic Parent Guard")
|
|
|
|
epic_a = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={"title": "Epic A", "item_kind": "epic", "status": "accepted"},
|
|
headers=_auth(token),
|
|
)
|
|
epic_a_id = epic_a.json()["id"]
|
|
|
|
epic_b = client.post(
|
|
f"/api/initiatives/{initiative_id}/backlog",
|
|
json={
|
|
"title": "Epic B",
|
|
"item_kind": "epic",
|
|
"parent_backlog_id": epic_a_id,
|
|
"status": "accepted",
|
|
},
|
|
headers=_auth(token),
|
|
)
|
|
assert epic_b.status_code == 400
|