test: A32 phase 2e — dashboard +9 routes, comptes A2/A3 (v7.16.0)
FlowDeck CI / lint (push) Canceled after 0s
FlowDeck CI / test (push) Canceled after 0s
FlowDeck CI / docker (push) Canceled after 0s

Cumul dashboard : 27 → 36 des 63 routes. 6 nouveaux tests (fichier à 38) :

- /accounts + /accounts/settings : 200 HTML et « password_hash » ABSENT du
  rendu (whitelist A2 vérifiée côté page)
- PUT /api/user/profile : persistance relue en base, restauration finally
- PUT /api/user/password : 403 « current password is incorrect » (A3 — la
  session seule ne change pas le mdp) + quirk assumé documenté : la longueur
  est validée AVANT l'auth et répond 200 + message
- POST /api/user/token : format fd_ + 64 hex ; ligne user_tokens nettoyée
- DELETE /api/user/forge/{provider} : {"status": "ok"}
- PUT /api/settings/account : full_name/email persistés + 400 sur mdp court,
  restauration finally
- POST /api/workspaces/1/select : Set-Cookie flowdeck_workspace vérifié ;
  GET /api/local-workspace/breadcrumb : shape liste

Reste A32 : dashboard 27 routes (fichiers/avatars/local-workspace/collections)
+ 6 routes Gitea d'api.py (stub transport httpx).

suite **1075/1075** · `ruff check app tests` OK · docs à jour
This commit is contained in:
2026-10-01 13:59:00 -04:00
parent 360c705fd4
commit 8b48dbdd4b
7 changed files with 133 additions and 6 deletions
+106
View File
@@ -500,3 +500,109 @@ def test_dashboard_workspace_members_list(client):
r = client.get("/api/workspace/1/members")
assert r.status_code == 200
assert isinstance(r.json()["members"], list)
def test_dashboard_account_pages_html(client):
"""Pages HTML /accounts et /accounts/settings (whitelist A2 vérifiée)."""
for path in ("/accounts", "/accounts/settings"):
r = client.get(path)
assert r.status_code == 200, path
assert "text/html" in r.headers["content-type"], path
# la page ne doit plus contenir de hash (colonnes whitelistées en A29)
assert "password_hash" not in client.get("/accounts").text
def test_dashboard_user_profile_update_persisted(client):
from app.db import get_conn
uid = client.get("/api/users/me").json()["id"]
with get_conn() as conn:
prev = conn.execute("SELECT full_name FROM users WHERE id=?", (uid,)).fetchone()[
"full_name"
]
try:
r = client.put("/api/user/profile", json={"full_name": "Smoke A32"})
assert r.status_code == 200 and r.json() == {"status": "ok"}
with get_conn() as conn:
now = conn.execute(
"SELECT full_name FROM users WHERE id=?", (uid,)
).fetchone()["full_name"]
assert now == "Smoke A32"
finally:
with get_conn() as conn:
conn.execute("UPDATE users SET full_name=? WHERE id=?", (prev, uid))
conn.commit()
def test_dashboard_password_change_gated_by_current(client):
"""A3 : sans mdp actuel correct, la session ne permet PAS de changer le mdp."""
court = client.put("/api/user/password", json={"password": "abc"})
# quirk assumé : la longueur est validée AVANT auth et répond 200 + erreur
assert "at least 6 characters" in court.json().get("error", "")
faux = client.put(
"/api/user/password",
json={"password": "nouveaumdp1", "current_password": "pas-le-bon"},
)
assert faux.status_code == 403
assert "incorrect" in faux.json()["detail"].lower()
def test_dashboard_user_token_and_forge_disconnect(client):
import re as _re
from app.db import get_conn
uid = client.get("/api/users/me").json()["id"]
try:
r = client.post("/api/user/token")
assert r.status_code == 200
assert _re.fullmatch(r"fd_[0-9a-f]{64}", r.json()["token"])
r2 = client.delete("/api/user/forge/gitea")
assert r2.status_code == 200 and r2.json() == {"status": "ok"}
finally:
with get_conn() as conn:
conn.execute("DELETE FROM user_tokens WHERE gitea_user_id=?", (uid,))
conn.commit()
def test_dashboard_settings_account_update(client):
from app.db import get_conn
uid = client.get("/api/users/me").json()["id"]
with get_conn() as conn:
prev = conn.execute(
"SELECT full_name, email FROM users WHERE id=?", (uid,)
).fetchone()
try:
r = client.put(
"/api/settings/account",
json={"full_name": "Compte A32", "email": "[email protected]"},
)
assert r.status_code == 200
with get_conn() as conn:
row = conn.execute(
"SELECT full_name, email FROM users WHERE id=?", (uid,)
).fetchone()
assert row["full_name"] == "Compte A32" and row["email"] == "[email protected]"
# mdp trop court → 400 (validateur, pas de changement)
court = client.put("/api/settings/account", json={"password": "abc"})
assert court.status_code == 400
finally:
with get_conn() as conn:
conn.execute(
"UPDATE users SET full_name=?, email=? WHERE id=?",
(prev["full_name"], prev["email"], uid),
)
conn.commit()
def test_dashboard_workspace_select_and_breadcrumb(client):
# select : cookie positionné + shape
r = client.post("/api/workspaces/1/select")
assert r.status_code == 200
assert r.json() == {"status": "ok", "workspace_id": 1}
assert "flowdeck_workspace" in r.headers.get("set-cookie", "")
# breadcrumb d'un dossier inexistant → liste (shape)
b = client.get("/api/local-workspace/breadcrumb", params={"folder": 999999})
assert b.status_code == 200
assert isinstance(b.json()["breadcrumb"], list)