Files
flowdeck/scripts/audit_functional.py
T
bruno 7794a03934
FlowDeck CI / lint (push) Successful in 55s
FlowDeck CI / test (push) Successful in 6m7s
FlowDeck CI / docker (push) Successful in 44s
fix(tests): rendre la suite hermétique — pin oauth_redirect_uri + FLOWDECK_DATA_DIR temporel
- test_get_redirect_uri_from_host_header: épingle oauth_redirect_uri à vide
  pour tester la dérivation Host de façon isolée (le .env du projet définit
  OAUTH_REDIRECT_URI, qui passe prioritaire par design)
- conftest: pose FLOWDECK_DATA_DIR vers un répertoire temporel inscriptible
  pour les tests emoji/docx/covers qui dépendaient de /data (conteneur)
- ajoute scripts/audit_functional.py: audit de bout-en-bout des processus
  (notes, DB, tâches, partage, publication, export, recherche, agents)

Suite locale: 538 passed
2026-09-14 07:45:33 -04:00

102 lines
5.0 KiB
Python

"""
FlowDeck — Audit fonctionnel de bout-en-bout.
Base SQLite isolée (init schéma) + TestClient FastAPI réel.
Vérifie status code + payload de chaque processus métier.
"""
import os, tempfile, json, sys
from pathlib import Path
_tmp = tempfile.mkdtemp(prefix="fd_audit_")
os.environ["DATABASE_URL"] = f"sqlite:///{_tmp}/audit.db"
os.environ["APP_SECRET_KEY"] = "audit-secret-0000"
os.environ["RATE_LIMIT_ENABLED"] = "false"
os.environ["BACKUP_ENABLED"] = "false"
os.environ["FLOWDECK_DATA_DIR"] = _tmp
os.environ["PROJECT_SYNC_ENABLED"] = "false"
os.environ["PROJECT_SYNC"] = "false"
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from fastapi.testclient import TestClient
from app.main import app
from app.db import init_db
init_db()
client = TestClient(app)
results = []
def csrf():
return client.get("/api/csrf-token").json().get("token", "")
def h(c):
return {"X-CSRF-Token": c, "Content-Type": "application/json"}
def register_login(email="[email protected]", pwd="secret123"):
c = csrf()
client.post("/auth/register", json={"name": "Audit", "email": email, "password": pwd}, headers=h(c))
c2 = csrf()
r = client.post("/auth/local-login", json={"email": email, "password": pwd}, headers=h(c2))
return c2
def rec(name, ok, detail=""):
results.append((name, ok, detail))
print(f"{'PASS' if ok else 'FAIL'} {name}" + (f" — {detail}" if detail else ""))
print("="*72)
print("FLOWDECK — AUDIT FONCTIONNEL (base isolée)")
print("="*72)
try:
# ── 1. AUTH & WORKSPACE ───────────────────────────────
c = register_login()
me = client.get("/api/users/me").json()
rec("register + login user local", me.get("login") == "[email protected]" or me.get("email") == "[email protected]", f"login={me.get('login')} admin={me.get('is_admin')}")
r = client.post("/api/workspaces", json={"name": "Audit WS"}, headers=h(c))
rec("POST /api/workspaces", r.status_code in (200, 201, 302), f"HTTP {r.status_code} {str(r.text)[:70]}")
# ── 2. CRÉATION NOTE ──────────────────────────────────
r = client.post("/api/pages", json={"workspace": "Audit WS", "title": "Note de test", "content": "## MD init", "content_format": "markdown"}, headers=h(c))
pid = r.json().get("id") if r.status_code in (200, 201) else None
rec("POST /api/pages (création note)", pid is not None, f"HTTP {r.status_code} body={str(r.text)[:60]}")
# ── 3. CRÉATION BASE DE DONNÉES (collection) ──────────
r = client.post("/db/api", json={"name": "Tâches", "schema": [{"name":"Statut","prop_type":"select","options":["À faire","En cours","Fait"]}]}, headers=h(c))
coll = r.json()
cid = coll.get("id")
rec("POST /db/api (création DB)", cid is not None, f"HTTP {r.status_code} id={cid}")
# ── 4. TÂCHES (rows) ──────────────────────────────────
if cid:
r = client.post(f"/db/{cid}/pages/api", json={"title": "Terminer audit", "property_values_json": json.dumps({"Statut":"En cours"})}, headers=h(c))
rec("POST /{cid}/pages (création row)", r.status_code in (200, 201, 200), f"HTTP {r.status_code} {str(r.text)[:70]}")
# ── 5. (Kanban board-config testé via instance Gitea réelle — cf. audit live) ──
# ── 6. SHARE / PUBLISH ────────────────────────────────
if pid:
r = client.post(f"/api/pages/{pid}/share", json={"email":"[email protected]","permission":"edit"}, headers=h(c))
rec("POST /pages/{id}/share", r.status_code in (200, 201), f"HTTP {r.status_code} {str(r.text)[:70]}")
r = client.post(f"/api/pages/{pid}/publish", json={}, headers=h(c))
rec("POST /pages/{id}/publish", r.status_code in (200, 201), f"HTTP {r.status_code} {str(r.text)[:70]}")
# ── 7. EXPORT ─────────────────────────────────────────
if pid:
r = client.get(f"/api/export/markdown/{pid}")
rec("GET /api/export/markdown", r.status_code == 200, f"HTTP {r.status_code} len={len(r.content)}")
# ── 8. RECHERCHE ──────────────────────────────────────
r = client.get("/api/search", params={"q": "audit"})
rec("GET /api/search?q=", r.status_code == 200, f"HTTP {r.status_code}")
# ── 9. AGENTS ─────────────────────────────────────────
r = client.get("/api/agent")
rec("GET /api/agent (agents)", r.status_code == 200, f"HTTP {r.status_code}")
except Exception as e:
import traceback
traceback.print_exc()
rec("EXÉCUTION SCRIPT", False, str(e)[:100])
print("="*72)
ok = sum(1 for _, o, _ in results if o)
print(f"RÉSULTAT: {ok}/{len(results)} processus OK")