"""FlowDeck — v5.11.0 Wiki-links & mentions + v5.12.0 Templates & page lock. Covers: migrations 13, the wiki token helpers, the picker / titles endpoints, rename propagation, backlinks via tokens, the page lock (423 guard, unlock permissions), page layout options, global page templates (list/create/use) and the public renderer wiki chips. """ from __future__ import annotations import json import os import secrets import tempfile import pytest from conftest import login_test_client from fastapi.testclient import TestClient @pytest.fixture def client(): db_file = tempfile.NamedTemporaryFile(suffix=".db", delete=False) db_path = db_file.name db_file.close() os.environ["DATABASE_URL"] = f"sqlite:///{db_path}" os.environ["APP_SECRET_KEY"] = "test-secret-for-tests" os.environ["RATE_LIMIT_ENABLED"] = "false" os.environ["PUBLIC_API_INSECURE_OK"] = "true" from app.config import settings settings.database_url = f"sqlite:///{db_path}" settings.rate_limit_enabled = False settings.public_api_insecure_ok = True from app.db import init_db from app.main import app init_db() yield login_test_client(TestClient(app)) try: os.unlink(db_path) except PermissionError: pass def _login(client): from app.auth.session import SessionManager from app.db import get_conn with get_conn() as conn: login = f"v511u_{secrets.token_hex(4)}" conn.execute( "INSERT INTO users (login, full_name, email, password_hash, is_admin) " "VALUES (?, 'V511 User', ?, ?, 0)", (login, f"{login}@test.com", ""), ) uid = conn.execute("SELECT id FROM users WHERE login=?", (login,)).fetchone()["id"] conn.commit() session = SessionManager.create_session({"id": uid, "login": login, "is_admin": 0}) # Réutilise le CSRF du jar : /api/csrf-token fait tourner le token à chaque # appel, un token rendu par un login précédent deviendrait invalide (403). csrf = client.cookies.get("csrf_token") if not csrf: csrf = client.get("/api/csrf-token", cookies={"flowdeck_session": session}).json()["csrf_token"] return session, csrf, uid def _login_admin(client): from app.auth.session import SessionManager from app.db import get_conn with get_conn() as conn: login = f"v511a_{secrets.token_hex(4)}" conn.execute( "INSERT INTO users (login, full_name, email, password_hash, is_admin) " "VALUES (?, 'V511 Admin', ?, ?, 1)", (login, f"{login}@test.com", ""), ) uid = conn.execute("SELECT id FROM users WHERE login=?", (login,)).fetchone()["id"] conn.commit() session = SessionManager.create_session({"id": uid, "login": login, "is_admin": 1}) # Réutilise le CSRF du jar : /api/csrf-token fait tourner le token à chaque # appel, un token rendu par un login précédent deviendrait invalide (403). csrf = client.cookies.get("csrf_token") if not csrf: csrf = client.get("/api/csrf-token", cookies={"flowdeck_session": session}).json()["csrf_token"] return session, csrf, uid def _mk_page(client, session, title, blocks=None): r = client.post("/board/api/pages", params={"title": title, "section": "Private"}, cookies={"flowdeck_session": session}) assert r.status_code == 200 pid = r.json()["id"] if blocks is not None: r = client.post(f"/board/api/pages/{pid}/blocks", json={"title": title, "blocks": blocks}, cookies={"flowdeck_session": session}) assert r.status_code == 200, r.text return pid def _blocks_b(text): return [{"id": "b1", "type": "paragraph", "content": text}] # ── Migration ── def test_migration_v513_schema(client): from app.db import get_conn with get_conn() as conn: pcols = {r[1] for r in conn.execute("PRAGMA table_info(pages)").fetchall()} tables = {r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'")} version = conn.execute("SELECT MAX(version) FROM schema_version").fetchone()[0] for col in ("is_locked", "locked_by", "full_width", "font_small"): assert col in pcols assert "page_global_templates" in tables assert version >= 13 # ── wiki_links service ── def test_wiki_token_helpers(): from app.services.wiki_links import extract_page_ids, resolve_tokens_html assert extract_page_ids("see [[fdpage:12]] and [[fdpage:7]] plus [[fdpage:12]]") == [7, 12] html = resolve_tokens_html( "Hi [[fdpage:7]] on [[fddate:2026-10-01]]", {"[[fdpage:7]]": "Project X", "[[fddate:2026-10-01]]": "Thu 1 Oct 2026"}, ) assert 'x"}) assert "