"""Références croisées ``external_ref`` — Fusion ACC-1.3 (Flowdeck). Vérifie le parseur d'URL profondes (contrat figé dracodev-commons), le cycle API sur une page et sur un projet (collection), les refus (422/404/401) et la route profonde ``/collections/{id}`` du contrat ACC-0.1 (redirection vers la vue native ``/db/{id}``). """ import pytest from app.db import get_conn from app.services.external_refs import parse_deep_link # ── Parseur d'URL profondes ───────────────────────────────────────────── @pytest.mark.parametrize("url,attendu", [ ("https://tiktak.dracodev.net/?task=42", ("tiktak", "task", "42")), ("https://tiktak.dracodev.net/?list=3", ("tiktak", "list", "3")), ("https://og.dracodev.net/#file=Perso:Notes/Idée.md", ("obsigate", "note", "Perso:Notes/Idée.md")), ("https://flowdeck.dracodev.net/pages/7", ("flowdeck", "page", "7")), ("https://flowdeck.dracodev.net/collections/9", ("flowdeck", "project", "9")), ("https://git.dracodev.net/Projets/ticktick-clone/issues/213", ("kanbante", "card", "Projets/ticktick-clone#213")), ]) def test_parse_formes_reconnues(url, attendu): parsed = parse_deep_link(url) assert parsed is not None assert parsed[:3] == attendu @pytest.mark.parametrize("url", [ "https://tiktak.dracodev.net/autre?x=1", "https://og.dracodev.net/#recherche", "https://flowdeck.dracodev.net/tableau", "https://example.com/pages/1", "pas une url", "", ]) def test_parse_formes_refusees(url): assert parse_deep_link(url) is None # ── Fixtures d'objets ─────────────────────────────────────────────────── def _creer_page() -> int: with get_conn() as conn: cur = conn.execute( "INSERT INTO pages (workspace, title) VALUES ('bruno/flowdeck', 'Page Fusion')" ) conn.commit() return cur.lastrowid def _creer_collection() -> int: with get_conn() as conn: cur = conn.execute("INSERT INTO collections (name) VALUES ('Projet Fusion')") conn.commit() return cur.lastrowid # ── API sur une page ──────────────────────────────────────────────────── def test_cycle_refs_page(client): pid = _creer_page() r = client.post(f"/api/pages/{pid}/refs", json={"url": "https://tiktak.dracodev.net/?task=42"}) assert r.status_code == 201, r.text ref = r.json() assert ref["target_app"] == "tiktak" and ref["target_id"] == "42" assert ref["label"] == "TikTak · tâche 42" assert ref["app_color"] == "#e8762c" r = client.post(f"/api/pages/{pid}/refs", json={"url": "https://og.dracodev.net/#file=Perso:A.md", "label": "Note liée"}) assert r.status_code == 201, r.text r = client.get(f"/api/pages/{pid}/refs") assert r.status_code == 200 assert [x["target_app"] for x in r.json()["refs"]] == ["tiktak", "obsigate"] assert r.json()["refs"][1]["label"] == "Note liée" r = client.delete(f"/api/refs/{ref['id']}") assert r.status_code == 200 r = client.get(f"/api/pages/{pid}/refs") assert [x["target_app"] for x in r.json()["refs"]] == ["obsigate"] def test_ref_url_inconnue_422(client): pid = _creer_page() r = client.post(f"/api/pages/{pid}/refs", json={"url": "https://example.com/x"}) assert r.status_code == 422 def test_delete_ref_inconnue_404(client): r = client.delete("/api/refs/999999") assert r.status_code == 404 def test_refs_anonyme_401(client): from tests.conftest import anon pid = _creer_page() r = anon(client).get(f"/api/pages/{pid}/refs") assert r.status_code == 401 # ── API sur un projet (collection) + route profonde ───────────────────── def test_cycle_refs_collection(client): cid = _creer_collection() r = client.post(f"/api/collections/{cid}/refs", json={"url": "https://git.dracodev.net/Projets/fusion/issues/5"}) assert r.status_code == 201, r.text assert r.json()["target_app"] == "kanbante" r = client.get(f"/api/collections/{cid}/refs") assert len(r.json()["refs"]) == 1 def test_route_profonde_collection(client): cid = _creer_collection() r = client.get(f"/collections/{cid}", follow_redirects=False) assert r.status_code == 303 assert r.headers["location"] == f"/db/{cid}" r = client.get("/collections/999999", follow_redirects=False) assert r.status_code == 404