"""FlowDeck v7.70.0 — Ouverture d'un projet Gitea : présentation + miroir local. Régression couverte : - ``GET /gitea-workspace?owner=&repo=`` crée le **workspace miroir local** AVANT de calculer la sidebar : le premier affichage du projet ne montre plus « No workspace open » (ou un ``localWsId: 0``) — équivalent local ouvert en parallèle du dépôt distant ; - le contexte projet est porté par l'URL : la sidebar résout le miroir même sans cookie ``flowdeck_workspace`` (navigation directe / favori) ; - ``GET /api/sidebar/workspace-tree`` rend l'arbre du **miroir** (et non plus le placeholder « Remote workspace ») quand le cookie est ``gitea:owner:repo`` ; - ``loadGiteaTree`` (base.html) cible ``#sidebar-gitea-items`` et n'écrase plus l'arbre du workspace local de ``#sidebar-workspace-items``. """ from __future__ import annotations import re from app.db import get_conn def _mirror_id(ws_key: str) -> int | None: with get_conn() as conn: row = conn.execute( "SELECT id FROM workspaces WHERE name=? AND settings_json LIKE ?", (ws_key, "%gitea_repo%"), ).fetchone() return row["id"] if row else None def _mk_mirror_page(ws_id: int, title: str) -> int: with get_conn() as conn: cur = conn.execute( "INSERT INTO pages (workspace, workspace_id, title, content, content_format, parent_section) " "VALUES ('', ?, ?, '', 'blocks', 'Private')", (ws_id, title), ) conn.commit() return cur.lastrowid def test_gitea_workspace_creates_and_shows_local_mirror(client): """Le premier GET crée le miroir et le sidebar pointe dessus (localWsId>0).""" assert _mirror_id("acme/widget") is None r = client.get("/gitea-workspace?owner=acme&repo=widget") assert r.status_code == 200, r.text ws_id = _mirror_id("acme/widget") assert ws_id is not None, "le workspace miroir doit être créé" # La sidebar expose l'ID du miroir (et non 0) dès le premier rendu. m = re.search(r"localWsId:\s*(\d+)", r.text) assert m, "localWsId introuvable dans la page" assert int(m.group(1)) == ws_id, "la sidebar doit ouvrir l'équivalent local en parallèle" assert "Open a workspace to see your files" not in r.text assert "acme/widget" in r.text def test_gitea_workspace_context_from_url_without_cookie(client): """Navigation directe (sans cookie) : le projet reste identifié par l'URL.""" r = client.get("/gitea-workspace?owner=acme&repo=gadget") assert r.status_code == 200, r.text ws_id = _mirror_id("acme/gadget") assert ws_id is not None m = re.search(r"localWsId:\s*(\d+)", r.text) assert m and int(m.group(1)) == ws_id # Pas de bandeau « aucune workspace ouverte » alors que le miroir existe. assert "Open a workspace to see your files" not in r.text def test_sidebar_workspace_tree_renders_gitea_mirror(client): """Le refresh sidebar ne remplace plus le miroir par « Remote workspace ».""" client.get("/gitea-workspace?owner=acme&repo=repo1") ws_id = _mirror_id("acme/repo1") assert ws_id is not None _mk_mirror_page(ws_id, "Note miroir") r = client.get( "/api/sidebar/workspace-tree", cookies={"flowdeck_workspace": "gitea:acme:repo1"}, ) assert r.status_code == 200, r.text assert "Note miroir" in r.text assert "Remote workspace" not in r.text def test_sidebar_workspace_tree_gitea_without_mirror_is_empty_not_error(client): r = client.get( "/api/sidebar/workspace-tree", cookies={"flowdeck_workspace": "gitea:ghost/none"}, ) assert r.status_code == 200 assert "Remote workspace" not in r.text assert "No pages yet" in r.text def test_loadgitea_tree_targets_repository_section(): """Scan statique : loadGiteaTree ne doit pas cibler #sidebar-workspace-items.""" from pathlib import Path html = (Path(__file__).resolve().parent.parent / "app" / "templates" / "base.html").read_text( encoding="utf-8" ) fn = html.split("loadGiteaTree() {", 1)[1].split("newPageInWorkspace()", 1)[0] assert "getElementById('sidebar-gitea-items')" in fn assert "getElementById('sidebar-workspace-items')" not in fn