Files
flowdeck/tests/test_v753_canvases.py
T
bruno 18c72d77fe
FlowDeck CI / lint (push) Successful in 2m11s
FlowDeck CI / test (push) Failing after 15m16s
FlowDeck CI / docker (push) Skipped
feat: menu + — Connecteurs (socle) — phase 5/8 (v7.55.0)
- Table agent_connectors (migration 32) : colonnes plates name, url,
  secret_encrypted, enabled, status, detail — config_json remis (YAGNI, les
  scopes OAuth des phases 6-7 ajouteront le leur).
- app/services/connectors.py : 1 fichier au lieu du package connectors/ —
  3 natifs (gitea, github, web) servis à la volée avec statut sans réseau,
  CRUD des personnels (URL validée par _validate_url = garde SSRF, clé chiffrée
  Fernet et jamais renvoyée — seul has_secret), probe() qui persiste
  status/detail, connector_fetch() borné à 20 000 car.
- API /api/agent/connectors : GET, POST (400 URL privée), PATCH, DELETE,
  POST /connectors/probe — 401 sans session, CSRF global. OpenAPI 519 chemins.
- Outil LLM connector_fetch (26e outil) : un seul outil qui dispatche vers
  Gitea/GitHub/web/personnalisé (id ou nom ou kind + path + query) au lieu d'un
  outil par connecteur ; désactivé/inconnu = erreur outil, jamais de run cassé.
- Menu + : section « Connecteurs » (catalogue avec badge ✓/✗/⚠/?), fiche par
  connecteur (Tester, Activer/Désactiver, Supprimer — masqués pour les natifs),
  formulaire « Ajouter un connecteur personnalisé » (clé en type=password).
- Tests : tests/test_v755_connectors.py (13) — 3 natifs, roundtrip Fernet de la
  clé, 5 URLs refusées (localhost, 127.0.0.1, metadata cloud, ftp:, file:),
  toggle persisté, 401, probe OK/erreur, outil complet, câblage menu ;
  test_v751/v753/v754 adaptés (1 seule section « bientôt » = plugins).
  Suite complète 1306 verts (-n auto), ruff 0, eslint 0 problème.
2026-10-06 23:26:19 -04:00

74 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""v7.53.0 — Menu + : Design System – Canevas (créer / insérer / enregistrer)."""
import json
from pathlib import Path
from app.db import get_conn
ROOT = Path(__file__).resolve().parent.parent
JS_PATH = ROOT / "static" / "js" / "agent_panel_2.js"
def test_canvases_list_includes_design_system(client):
tpl = {t["key"]: t for t in client.get("/board/api/page-templates").json()["templates"] if t.get("builtin")}
assert "design_system" in tpl
assert tpl["design_system"]["name"] == "Design System"
assert tpl["design_system"]["icon"] == "🎨"
def test_blocks_route_builtin(client):
r = client.get("/board/api/page-templates/0/blocks", params={"key": "design_system"})
assert r.status_code == 200, r.text
blocks = r.json()["blocks"]
assert isinstance(blocks, list) and blocks
assert blocks[0]["type"] == "heading_1" and blocks[0]["content"] == "Design System"
# les builtins sont livrés sans id : le front appelle ensureBlockIds()
assert "id" not in blocks[0]
assert client.get("/board/api/page-templates/0/blocks", params={"key": "nope"}).status_code == 404
empty = client.get("/board/api/page-templates/0/blocks", params={"key": "empty"})
assert empty.status_code == 200 and empty.json()["blocks"]
def test_blocks_route_user_template(client):
blocks = [{"type": "heading_1", "content": "Mon canevas"},
{"type": "paragraph", "content": "Contenu."}]
created = client.post("/board/api/page-templates", json={"name": "Mon canevas", "blocks": blocks})
assert created.status_code == 200, created.text
tid = created.json()["id"]
got = client.get(f"/board/api/page-templates/{tid}/blocks")
assert got.status_code == 200
assert [b["content"] for b in got.json()["blocks"]] == ["Mon canevas", "Contenu."]
assert client.get("/board/api/page-templates/999999/blocks").status_code == 404
def test_use_design_system_creates_page(client):
r = client.post("/board/api/page-templates/0/use", json={"key": "design_system"})
assert r.status_code == 200, r.text
page_id = r.json()["id"]
with get_conn() as conn:
row = conn.execute(
"SELECT content, content_format FROM pages WHERE id=?", (page_id,)
).fetchone()
assert row["content_format"] == "blocks"
blocks = json.loads(row["content"])
texts = [b.get("content", "") for b in blocks]
assert "Design System" in texts
# les ids sont assignés côté serveur (_ensure_block_ids)
assert all(b.get("id") for b in blocks)
def test_canvases_menu_wired(client):
src = JS_PATH.read_text(encoding="utf-8")
root = src.split("var FD_PLUS_MENU = [", 1)[1].split("];", 1)[0]
assert "action:'canvases'" in root
# phases 1-5 livrées → 1 section « bientôt » restante (plugins)
assert root.count("disabled:true") == 1
for action in ("'canvas'", "'canvas-create'", "'canvas-insert'", "'canvas-save'"):
assert action in src, action
# piège /use : la clé builtin part DANS LE BODY, pas en query string
assert "body: JSON.stringify(c.builtin ? {key: c.key} : {})" in src
assert "ensureBlockIds" in src