Kairo-Jinkendo/backend/tests/test_ap15c_project_hierarchy.py
Lars 4ca0a2c460
All checks were successful
Deploy Development / deploy (push) Successful in 53s
Test Suite / pytest-backend (push) Successful in 1m42s
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 21s
Test Suite / playwright-smoke (push) Successful in 14s
AP1.5c: Rekursive Projects fuer Mega-Programme ohne Stream-Typ.
parent_project_id und container_kind ermoeglichen verschachtelte Projektstruktur; Arbeitspakete bleiben an Blatt-Projekten gebunden.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 08:38:58 +02:00

112 lines
3.5 KiB
Python

"""AP1.5c — Rekursive Projects (parent_project_id)."""
from __future__ import annotations
from tests.factories import provision_user_in_tenant
from tests.test_initiatives_actions import (
_auth,
_create_initiative,
_login,
)
def test_nested_projects_and_depth(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
root = client.post(
f"/api/initiatives/{initiative_id}/projects",
json={"title": "Programm", "container_kind": "project"},
headers=_auth(token),
)
assert root.status_code == 201
root_id = root.json()["id"]
stream = client.post(
f"/api/initiatives/{initiative_id}/projects",
json={
"title": "Backend Stream",
"parent_project_id": root_id,
"container_kind": "stream",
},
headers=_auth(token),
)
assert stream.status_code == 201
stream_id = stream.json()["id"]
leaf = client.post(
f"/api/initiatives/{initiative_id}/projects",
json={"title": "API", "parent_project_id": stream_id},
headers=_auth(token),
)
assert leaf.status_code == 201
leaf_id = leaf.json()["id"]
listed = client.get(
f"/api/initiatives/{initiative_id}/projects",
headers=_auth(token),
)
assert listed.status_code == 200
items = {p["id"]: p for p in listed.json()}
assert items[stream_id]["parent_project_id"] == root_id
assert items[leaf_id]["depth"] == 3
assert items[leaf_id]["is_leaf"] is True
assert items[root_id]["has_children"] is True
def test_action_only_on_leaf_project(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
parent = client.post(
f"/api/initiatives/{initiative_id}/projects",
json={"title": "Container"},
headers=_auth(token),
).json()
child = client.post(
f"/api/initiatives/{initiative_id}/projects",
json={"title": "Blatt", "parent_project_id": parent["id"]},
headers=_auth(token),
).json()
blocked = client.post(
f"/api/initiatives/{initiative_id}/actions",
json={"title": "Am Container", "project_id": parent["id"]},
headers=_auth(token),
)
assert blocked.status_code == 400
assert "Blatt" in blocked.json()["detail"]
ok = client.post(
f"/api/initiatives/{initiative_id}/actions",
json={"title": "Am Blatt", "project_id": child["id"]},
headers=_auth(token),
)
assert ok.status_code == 201
def test_cannot_delete_project_with_children(client):
user = provision_user_in_tenant(tenant_role="member")
token = _login(client, user)
initiative_id = _create_initiative(client, token).json()["id"]
parent = client.post(
f"/api/initiatives/{initiative_id}/projects",
json={"title": "Parent"},
headers=_auth(token),
).json()
child = client.post(
f"/api/initiatives/{initiative_id}/projects",
json={"title": "Child", "parent_project_id": parent["id"]},
headers=_auth(token),
).json()
resp = client.delete(f"/api/projects/{parent['id']}", headers=_auth(token))
assert resp.status_code == 400
client.delete(f"/api/projects/{child['id']}", headers=_auth(token))
ok = client.delete(f"/api/projects/{parent['id']}", headers=_auth(token))
assert ok.status_code == 204