"""AP2.2e — Unplan, Bug/Issue mit Feature-Referenz, Sprint-Lifecycle.""" from __future__ import annotations from tests.factories import provision_user_in_tenant from tests.test_initiatives_actions import _auth, _create_initiative, _login def test_unplan_action_restores_backlog(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Sprint Planung E2E", archetype_key="initiative.product", ) initiative_id = created.json()["id"] cycle = client.post( f"/api/initiatives/{initiative_id}/work-cycles", json={"title": "Sprint 1", "status": "planned"}, headers=_auth(token), ) assert cycle.status_code == 201 cycle_id = cycle.json()["id"] backlog = client.post( f"/api/initiatives/{initiative_id}/backlog", json={"title": "Story A", "status": "accepted"}, headers=_auth(token), ) assert backlog.status_code == 201 backlog_id = backlog.json()["id"] converted = client.post( f"/api/backlog/{backlog_id}/convert-to-action", json={"work_cycle_id": cycle_id, "assign_active_sprint": False}, headers=_auth(token), ) assert converted.status_code == 201 action_id = converted.json()["action"]["id"] assert converted.json()["backlog_item"]["status"] == "converted" unplan = client.post( f"/api/actions/{action_id}/unplan-to-backlog", headers=_auth(token), ) assert unplan.status_code == 200 body = unplan.json() assert body["backlog_item"]["status"] == "accepted" assert body["backlog_item"]["id"] == backlog_id assert body["backlog_item"]["converted_action_id"] is None assert body["action"]["status"] == "discarded" assert body["action"]["work_cycle_id"] is None def test_unplan_rejects_in_progress(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Unplan Guard", archetype_key="initiative.product", ) initiative_id = created.json()["id"] action = client.post( f"/api/initiatives/{initiative_id}/actions", json={"title": "Laufendes AP", "status": "in_progress"}, headers=_auth(token), ) assert action.status_code == 201 action_id = action.json()["id"] unplan = client.post( f"/api/actions/{action_id}/unplan-to-backlog", headers=_auth(token), ) assert unplan.status_code == 400 def test_backlog_bug_with_feature_reference(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Bug Ref", archetype_key="initiative.product", ) initiative_id = created.json()["id"] cycle = client.post( f"/api/initiatives/{initiative_id}/work-cycles", json={"title": "Sprint 1", "status": "planned"}, headers=_auth(token), ) assert cycle.status_code == 201 cycle_id = cycle.json()["id"] feature = client.post( f"/api/initiatives/{initiative_id}/actions", json={"title": "Login Feature", "action_kind": "delivery"}, headers=_auth(token), ) assert feature.status_code == 201 feature_id = feature.json()["id"] bug = client.post( f"/api/initiatives/{initiative_id}/backlog", json={ "title": "Login-Button kaputt", "status": "accepted", "item_kind": "bug", "parent_action_id": feature_id, }, headers=_auth(token), ) assert bug.status_code == 201 assert bug.json()["item_kind"] == "bug" assert bug.json()["parent_action_id"] == feature_id converted = client.post( f"/api/backlog/{bug.json()['id']}/convert-to-action", json={"work_cycle_id": cycle_id, "assign_active_sprint": False}, headers=_auth(token), ) assert converted.status_code == 201 action = converted.json()["action"] assert action["action_kind"] == "bug" assert action["parent_action_id"] == feature_id assert action["work_cycle_id"] == cycle_id def test_complete_sprint_reports_open_actions(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Complete Stats", archetype_key="initiative.product", ) initiative_id = created.json()["id"] cycle = client.post( f"/api/initiatives/{initiative_id}/work-cycles", json={"title": "Sprint Active", "status": "active"}, headers=_auth(token), ) assert cycle.status_code == 201 cycle_id = cycle.json()["id"] client.post( f"/api/initiatives/{initiative_id}/actions", json={"title": "Offen", "work_cycle_id": cycle_id, "status": "open"}, headers=_auth(token), ) completed = client.post( f"/api/initiatives/{initiative_id}/work-cycles/{cycle_id}/complete", headers=_auth(token), ) assert completed.status_code == 200 assert completed.json()["status"] == "reached" assert completed.json()["open_action_count_at_completion"] == 1 def test_reactivate_completed_sprint(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Reactivate Sprint", archetype_key="initiative.product", ) initiative_id = created.json()["id"] cycle = client.post( f"/api/initiatives/{initiative_id}/work-cycles", json={"title": "Sprint R1", "status": "active"}, headers=_auth(token), ) cycle_id = cycle.json()["id"] completed = client.post( f"/api/initiatives/{initiative_id}/work-cycles/{cycle_id}/complete", headers=_auth(token), ) assert completed.status_code == 200 assert completed.json()["status"] == "reached" reactivated = client.post( f"/api/initiatives/{initiative_id}/work-cycles/{cycle_id}/reactivate", headers=_auth(token), ) assert reactivated.status_code == 200 assert reactivated.json()["status"] == "active" active = client.get( f"/api/initiatives/{initiative_id}/work-cycles/active", headers=_auth(token), ) assert active.status_code == 200 assert active.json()["id"] == cycle_id def test_carryover_unplan_allows_in_progress(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Carryover Unplan", archetype_key="initiative.product", ) initiative_id = created.json()["id"] action = client.post( f"/api/initiatives/{initiative_id}/actions", json={"title": "In Arbeit", "status": "in_progress"}, headers=_auth(token), ) action_id = action.json()["id"] unplan = client.post( f"/api/actions/{action_id}/unplan-to-backlog?carryover=true", headers=_auth(token), ) assert unplan.status_code == 200 assert unplan.json()["backlog_item"]["status"] == "accepted" normal_unplan = client.post( f"/api/actions/{action_id}/unplan-to-backlog", headers=_auth(token), ) assert normal_unplan.status_code in (400, 404)