Files
webui-develop/tests/test_sprint15.py
2026-04-20 10:43:30 +02:00

235 lines
7.7 KiB
Python

"""
Sprint 15 Tests: session projects (CRUD, move, backward compat).
"""
import json, urllib.error, urllib.request
from tests._pytest_port import BASE
def get(path):
with urllib.request.urlopen(BASE + path, timeout=10) as r:
return json.loads(r.read()), r.status
def post(path, body=None):
data = json.dumps(body or {}).encode()
req = urllib.request.Request(BASE + path, data=data,
headers={"Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=10) as r:
return json.loads(r.read()), r.status
except urllib.error.HTTPError as e:
return json.loads(e.read()), e.code
def make_session(created_list):
d, _ = post("/api/session/new", {})
sid = d["session"]["session_id"]
created_list.append(sid)
return sid, d["session"]
def make_project(created_list, name="Test Project", color=None):
body = {"name": name}
if color:
body["color"] = color
d, status = post("/api/projects/create", body)
assert status == 200
pid = d["project"]["project_id"]
created_list.append(pid)
return pid, d["project"]
def cleanup_projects(project_ids):
for pid in project_ids:
try:
post("/api/projects/delete", {"project_id": pid})
except Exception:
pass
# ── Project CRUD ─────────────────────────────────────────────────────────
def test_create_project():
"""Creating a project returns a valid project dict."""
pids = []
try:
pid, proj = make_project(pids, "My Project", "#7cb9ff")
assert pid and len(pid) == 12
assert proj["name"] == "My Project"
assert proj["color"] == "#7cb9ff"
assert "created_at" in proj
finally:
cleanup_projects(pids)
def test_list_projects_empty():
"""Listing projects when none exist returns empty list."""
d, status = get("/api/projects")
assert status == 200
assert isinstance(d["projects"], list)
def test_list_projects():
"""Listing projects returns created projects."""
pids = []
try:
make_project(pids, "Alpha")
make_project(pids, "Beta")
d, status = get("/api/projects")
assert status == 200
names = [p["name"] for p in d["projects"]]
assert "Alpha" in names
assert "Beta" in names
finally:
cleanup_projects(pids)
def test_rename_project():
"""Renaming a project updates its name."""
pids = []
try:
pid, _ = make_project(pids, "Old Name")
d, status = post("/api/projects/rename", {"project_id": pid, "name": "New Name"})
assert status == 200
assert d["project"]["name"] == "New Name"
# Verify via list
dl, _ = get("/api/projects")
names = [p["name"] for p in dl["projects"]]
assert "New Name" in names
assert "Old Name" not in names
finally:
cleanup_projects(pids)
def test_delete_project():
"""Deleting a project removes it from the list."""
pids = []
try:
pid, _ = make_project(pids, "Doomed")
d, status = post("/api/projects/delete", {"project_id": pid})
assert status == 200
assert d["ok"] is True
dl, _ = get("/api/projects")
assert all(p["project_id"] != pid for p in dl["projects"])
pids.clear() # already deleted
finally:
cleanup_projects(pids)
def test_delete_project_unassigns_sessions():
"""Deleting a project unassigns all sessions that belonged to it."""
pids = []
sids = []
try:
pid, _ = make_project(pids, "Temp Project")
sid, _ = make_session(sids)
# Assign session to project
post("/api/session/move", {"session_id": sid, "project_id": pid})
# Verify assigned
sd, _ = get(f"/api/session?session_id={sid}")
assert sd["session"].get("project_id") == pid
# Delete project
post("/api/projects/delete", {"project_id": pid})
pids.clear()
# Verify session is unassigned
sd2, _ = get(f"/api/session?session_id={sid}")
assert sd2["session"].get("project_id") is None
finally:
cleanup_projects(pids)
for s in sids:
post("/api/session/delete", {"session_id": s})
def test_create_project_requires_name():
"""Creating a project without a name returns 400."""
d, status = post("/api/projects/create", {})
assert status == 400
def test_delete_nonexistent_project():
"""Deleting a project that doesn't exist returns 404."""
d, status = post("/api/projects/delete", {"project_id": "nonexistent99"})
assert status == 404
# ── Session move ─────────────────────────────────────────────────────────
def test_session_move_to_project():
"""Moving a session to a project sets its project_id."""
pids = []
sids = []
try:
pid, _ = make_project(pids, "Work")
sid, _ = make_session(sids)
d, status = post("/api/session/move", {"session_id": sid, "project_id": pid})
assert status == 200
assert d["session"]["project_id"] == pid
finally:
cleanup_projects(pids)
for s in sids:
post("/api/session/delete", {"session_id": s})
def test_session_move_to_unassigned():
"""Moving a session to null project unassigns it."""
pids = []
sids = []
try:
pid, _ = make_project(pids, "Temp")
sid, _ = make_session(sids)
# Assign then unassign
post("/api/session/move", {"session_id": sid, "project_id": pid})
d, status = post("/api/session/move", {"session_id": sid, "project_id": None})
assert status == 200
assert d["session"]["project_id"] is None
finally:
cleanup_projects(pids)
for s in sids:
post("/api/session/delete", {"session_id": s})
def test_session_project_in_list():
"""Session list includes project_id for assigned sessions."""
pids = []
sids = []
try:
pid, _ = make_project(pids, "Listed")
sid, _ = make_session(sids)
# Give it a title so it shows in list (non-empty Untitled sessions are hidden)
post("/api/session/rename", {"session_id": sid, "title": "Project Test Session"})
post("/api/session/move", {"session_id": sid, "project_id": pid})
dl, _ = get("/api/sessions")
match = [s for s in dl["sessions"] if s["session_id"] == sid]
assert len(match) == 1
assert match[0]["project_id"] == pid
finally:
cleanup_projects(pids)
for s in sids:
post("/api/session/delete", {"session_id": s})
# ── Backward compat ──────────────────────────────────────────────────────
def test_compact_includes_project_id():
"""New session compact dict includes project_id as null."""
sids = []
try:
sid, sess = make_session(sids)
# Give it a title so it appears in the list
post("/api/session/rename", {"session_id": sid, "title": "Compat Test"})
dl, _ = get("/api/sessions")
match = [s for s in dl["sessions"] if s["session_id"] == sid]
assert len(match) == 1
assert "project_id" in match[0]
assert match[0]["project_id"] is None
finally:
for s in sids:
post("/api/session/delete", {"session_id": s})
def test_session_move_requires_session_id():
"""Moving without session_id returns 400."""
d, status = post("/api/session/move", {"project_id": "abc"})
assert status == 400