test: A32 phase 2b — api.py 3 → 16/22 routes couvertes (v7.13.0)
+6 smokes dans test_smoke_uncovered.py (22 tests au total dans le fichier) : - board-config GET/POST : défauts 5 colonnes sans board, création puis relecture du roundtrip (seed via l'endpoint lui-même, pas de SQL brut) - col-mapping POST/DELETE : 404 sans board, upsert label vérifié, suppression - card POST : 404 sans board, ok avec - collaborators GET : gitea.get_collaborators STUBBÉ (zéro accès réseau réel) - frontend-error(s) : capture, JSON invalide → ignored, DÉDUP d'une erreur répétée (count=2), lecture qui purge (cleared=true puis 0) - checklist mutations : PATCH item (checked/content relus EN BASE), DELETE item, DELETE checklist (COUNT=0) — seed + cleanup en finally Reste api.py : 6 routes Gitea (issues ×4 + créations checklists owner/repo) → stub de transport httpx (phase suivante). Reste global : dashboard 17/63, api_v2 50/115. suite **1059/1059** (236 s) · `ruff check app tests` OK · docs à jour
This commit is contained in:
@@ -184,3 +184,131 @@ def test_library_lists_still_available_after_removal(client):
|
||||
"""Les listes restantes (celles qu'utilise le front) répondent toujours."""
|
||||
r = client.get("/api/library/recents")
|
||||
assert r.status_code == 200 and isinstance(r.json()["items"], list)
|
||||
|
||||
|
||||
# ── api.py (3/23 → 11/23) ────────────────────────────────────────────────────
|
||||
|
||||
def _seed_board(client) -> None:
|
||||
"""Board via l'endpoint lui-même (pas de SQL brut)."""
|
||||
r = client.post(
|
||||
f"/api/board-config/{OWNER}/{REPO}",
|
||||
params={"columns": '["Todo", "Doing"]', "wip_limits": "{}"},
|
||||
)
|
||||
assert r.status_code == 200 and r.json() == {"status": "ok"}
|
||||
|
||||
|
||||
def test_api_board_config_default_then_roundtrip(client):
|
||||
# pas de board → défauts
|
||||
r = client.get(f"/api/board-config/{OWNER}/other-repo")
|
||||
assert r.status_code == 200
|
||||
assert r.json()["columns"] == ["Backlog", "À faire", "En cours", "Révision", "Terminé"]
|
||||
# création puis relecture
|
||||
_seed_board(client)
|
||||
back = client.get(f"/api/board-config/{OWNER}/{REPO}")
|
||||
assert back.json()["columns"] == ["Todo", "Doing"]
|
||||
|
||||
|
||||
def test_api_col_mapping_requires_board_and_works(client):
|
||||
# board inexistant → 404
|
||||
r = client.post(
|
||||
"/api/col-mapping",
|
||||
params={"owner": OWNER, "repo": "absent", "column": "Doing", "gitea_label": "x"},
|
||||
)
|
||||
assert r.status_code == 404
|
||||
# avec board → ok (upsert) puis suppression
|
||||
_seed_board(client)
|
||||
r2 = client.post(
|
||||
"/api/col-mapping",
|
||||
params={"owner": OWNER, "repo": REPO, "column": "Doing", "gitea_label": "in-progress"},
|
||||
)
|
||||
assert r2.status_code == 200
|
||||
assert r2.json() == {"status": "ok", "column": "Doing", "label": "in-progress"}
|
||||
r3 = client.delete(
|
||||
"/api/col-mapping", params={"owner": OWNER, "repo": REPO, "column": "Doing"}
|
||||
)
|
||||
assert r3.status_code == 200 and r3.json() == {"status": "ok", "column": "Doing"}
|
||||
|
||||
|
||||
def test_api_card_metadata(client):
|
||||
r = client.post(f"/api/card/{OWNER}/absent/1", params={"priority": "high"})
|
||||
assert r.status_code == 404 # board requis
|
||||
_seed_board(client)
|
||||
r2 = client.post(
|
||||
f"/api/card/{OWNER}/{REPO}/1", params={"priority": "high", "due_date": "2026-10-01"}
|
||||
)
|
||||
assert r2.status_code == 200 and r2.json() == {"status": "ok"}
|
||||
|
||||
|
||||
def test_api_collaborators_stubbed(client, monkeypatch):
|
||||
from app.services import gitea_client
|
||||
|
||||
async def _none(*_a, **_k):
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(gitea_client.gitea, "get_collaborators", _none)
|
||||
r = client.get(f"/api/collaborators/{OWNER}/{REPO}")
|
||||
assert r.status_code == 200
|
||||
assert r.json() == {"collaborators": []}
|
||||
|
||||
|
||||
def test_api_checklist_item_toggle_and_deletes(client):
|
||||
"""PATCH item / DELETE item / DELETE checklist : mutation vérifiée en base."""
|
||||
from app.db import get_conn
|
||||
|
||||
_seed_board(client)
|
||||
with get_conn() as conn:
|
||||
board_id = conn.execute(
|
||||
"SELECT id FROM boards WHERE project_owner=? AND project_name=?",
|
||||
(OWNER, REPO),
|
||||
).fetchone()["id"]
|
||||
cur = conn.execute(
|
||||
"INSERT INTO checklists (board_id, gitea_issue_id, title) VALUES (?, 1, 'A32')",
|
||||
(board_id,),
|
||||
)
|
||||
cl_id = cur.lastrowid
|
||||
cur = conn.execute(
|
||||
"INSERT INTO checklist_items (checklist_id, content) VALUES (?, 'fait ?')",
|
||||
(cl_id,),
|
||||
)
|
||||
item_id = cur.lastrowid
|
||||
conn.commit()
|
||||
try:
|
||||
r = client.patch(
|
||||
f"/api/checklist-items/{item_id}", params={"checked": True, "content": "fait !"}
|
||||
)
|
||||
assert r.status_code == 200 and r.json() == {"status": "ok"}
|
||||
with get_conn() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT checked, content FROM checklist_items WHERE id=?", (item_id,)
|
||||
).fetchone()
|
||||
assert row["checked"] == 1 and row["content"] == "fait !"
|
||||
|
||||
assert client.delete(f"/api/checklist-items/{item_id}").json() == {"status": "ok"}
|
||||
assert client.delete(f"/api/checklists/{cl_id}").json() == {"status": "ok"}
|
||||
with get_conn() as conn:
|
||||
left = conn.execute(
|
||||
"SELECT COUNT(*) AS n FROM checklists WHERE id=?", (cl_id,)
|
||||
).fetchone()["n"]
|
||||
assert left == 0
|
||||
finally:
|
||||
with get_conn() as conn:
|
||||
conn.execute("DELETE FROM checklist_items WHERE checklist_id=?", (cl_id,))
|
||||
conn.execute("DELETE FROM checklists WHERE id=?", (cl_id,))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def test_api_frontend_error_capture_and_read(client):
|
||||
# purge d'un éventuel résidu d'un autre test
|
||||
client.get("/api/frontend-errors")
|
||||
ok = client.post("/api/frontend-error", json={"message": "boom a32", "type": "error"})
|
||||
assert ok.status_code == 200 and ok.json() == {"status": "ok"}
|
||||
invalid = client.post(
|
||||
"/api/frontend-error", content="pas du json",
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
assert invalid.status_code == 200 and invalid.json()["status"] == "ignored"
|
||||
# doublon consécutif dédupliqué (count=2), lecture puis purge
|
||||
client.post("/api/frontend-error", json={"message": "boom a32", "type": "error"})
|
||||
out = client.get("/api/frontend-errors").json()
|
||||
assert out["count"] == 1 and out["errors"][0]["count"] == 2 and out["cleared"] is True
|
||||
assert client.get("/api/frontend-errors").json()["count"] == 0
|
||||
|
||||
Reference in New Issue
Block a user