Résolution uniforme des quatre sœurs (ObsiGate, Flowdeck, TikTak, Kanbante) dans app/services/draco_urls.py : explicite prioritaire (contrat inchangé), sinon dérivation depuis l'hôte de l'agent connu via AGENT_EVENEMENTS_URL (agent<SUFFIXE>.dracodev.net → <slug><SUFFIXE>.dracodev.net), sinon défaut officiel. Hors motif (autre domaine, LAN, IP) : aucune dérivation. Gitea inchangé. Tests +4 dans test_draco_urls_devprod.py.
191 lines
8.6 KiB
Python
191 lines
8.6 KiB
Python
"""URLs publiques dracodev par configuration — chantier dev/prod (v7.82.0).
|
|
|
|
Règle vérifiée : aucun nom public codé en dur dans le code vivant ;
|
|
les défauts sont les noms OFFICIELS (jamais de ``-dev`` en défaut) et
|
|
chaque environnement déclare ses noms en ``-dev`` par l'environnement
|
|
(``DRACO_SSO_URL``, ``DRACO_AGENT_URL``, ``TIKTAK_URL``,
|
|
``KANBANTE_URL``, ``FLOWDECK_URL``/``OAUTH_REDIRECT_URI``…).
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from app.services import draco_urls
|
|
|
|
VARS = ("DRACO_SSO_URL", "DRACO_AGENT_URL", "TIKTAK_URL", "KANBANTE_URL",
|
|
"FLOWDECK_URL", "OAUTH_REDIRECT_URI", "AGENT_EVENEMENTS_URL")
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _env_propre(monkeypatch):
|
|
for v in VARS:
|
|
monkeypatch.delenv(v, raising=False)
|
|
|
|
|
|
def test_defauts_officiels_sans_dev():
|
|
urls = draco_urls.toutes()
|
|
assert urls["obsigate"] == "https://og.dracodev.net"
|
|
assert urls["agent"] == "https://agent.dracodev.net"
|
|
assert urls["tiktak"] == "https://tiktak.dracodev.net"
|
|
assert urls["kanbante"] == "https://kanbante.dracodev.net"
|
|
assert urls["flowdeck"] == "https://flowdeck.dracodev.net"
|
|
assert all("-dev" not in u for u in urls.values())
|
|
|
|
|
|
def test_surcharge_dev_par_env(monkeypatch):
|
|
monkeypatch.setenv("DRACO_SSO_URL", "https://og-dev.dracodev.net")
|
|
monkeypatch.setenv("DRACO_AGENT_URL", "https://agent-dev.dracodev.net")
|
|
monkeypatch.setenv("TIKTAK_URL", "https://tiktak-dev.dracodev.net")
|
|
monkeypatch.setenv("KANBANTE_URL", "https://kanbante-dev.dracodev.net")
|
|
monkeypatch.setenv("FLOWDECK_URL", "https://flowdeck-dev.dracodev.net")
|
|
urls = draco_urls.toutes()
|
|
assert urls["obsigate"] == "https://og-dev.dracodev.net"
|
|
assert urls["agent"] == "https://agent-dev.dracodev.net"
|
|
assert urls["tiktak"] == "https://tiktak-dev.dracodev.net"
|
|
assert urls["kanbante"] == "https://kanbante-dev.dracodev.net"
|
|
assert urls["flowdeck"] == "https://flowdeck-dev.dracodev.net"
|
|
|
|
|
|
def test_flowdeck_url_derivée_oauth_redirect(monkeypatch):
|
|
monkeypatch.setenv(
|
|
"OAUTH_REDIRECT_URI", "https://flowdeck-dev.dracodev.net/auth/callback")
|
|
assert draco_urls.flowdeck_url() == "https://flowdeck-dev.dracodev.net"
|
|
|
|
|
|
def test_evenements_url_derivée_agent(monkeypatch):
|
|
from app.services import agent_evenements
|
|
|
|
monkeypatch.setenv("DRACO_AGENT_URL", "https://agent-dev.dracodev.net")
|
|
assert agent_evenements._url() == \
|
|
"https://agent-dev.dracodev.net/api/v1/evenements"
|
|
monkeypatch.setenv("AGENT_EVENEMENTS_URL", "http://127.0.0.1:9/api/v1/evenements")
|
|
assert agent_evenements._url() == "http://127.0.0.1:9/api/v1/evenements"
|
|
|
|
|
|
def test_csp_agent_depuis_config(client, monkeypatch):
|
|
monkeypatch.setenv("DRACO_AGENT_URL", "https://agent-dev.dracodev.net")
|
|
r = client.get("/")
|
|
csp = r.headers.get("content-security-policy", "")
|
|
assert "https://agent-dev.dracodev.net" in csp
|
|
assert "connect-src 'self' https://agent-dev.dracodev.net" in csp
|
|
assert "frame-src 'self' https://agent-dev.dracodev.net" in csp
|
|
assert "https://agent.dracodev.net " not in csp.replace(
|
|
"https://agent-dev.dracodev.net", "")
|
|
|
|
|
|
def test_parse_deep_link_dev_et_officiel(monkeypatch):
|
|
from app.services.external_refs import parse_deep_link
|
|
|
|
monkeypatch.setenv("TIKTAK_URL", "https://tiktak-dev.dracodev.net")
|
|
monkeypatch.setenv("DRACO_SSO_URL", "https://og-dev.dracodev.net")
|
|
# Hôte configuré (dev)…
|
|
assert parse_deep_link("https://tiktak-dev.dracodev.net/?task=7")[:3] == \
|
|
("tiktak", "task", "7")
|
|
assert parse_deep_link("https://og-dev.dracodev.net/#file=P:A.md")[:3] == \
|
|
("obsigate", "note", "P:A.md")
|
|
# …et hôte officiel toujours reconnu (références déjà stockées).
|
|
assert parse_deep_link("https://tiktak.dracodev.net/?task=7")[:3] == \
|
|
("tiktak", "task", "7")
|
|
assert parse_deep_link("https://og.dracodev.net/#file=P:A.md")[:3] == \
|
|
("obsigate", "note", "P:A.md")
|
|
|
|
|
|
def test_url_affichage_reecrit_hote_sans_toucher_stockage(monkeypatch):
|
|
from app.services.external_refs import url_affichage
|
|
|
|
monkeypatch.setenv("TIKTAK_URL", "https://tiktak-dev.dracodev.net")
|
|
stockee = "https://tiktak.dracodev.net/?task=42"
|
|
assert url_affichage(stockee) == "https://tiktak-dev.dracodev.net/?task=42"
|
|
# La fonction est pure : la valeur d'entrée (celle en base) ne bouge pas.
|
|
assert stockee == "https://tiktak.dracodev.net/?task=42"
|
|
assert url_affichage("https://example.com/x") == "https://example.com/x"
|
|
|
|
|
|
def test_preset_tiktak_depuis_config(monkeypatch):
|
|
from app.services import connectors
|
|
|
|
assert connectors.PRESETS["tiktak"]["url"] == \
|
|
"https://tiktak.dracodev.net/mcp"
|
|
monkeypatch.setenv("TIKTAK_URL", "https://tiktak-dev.dracodev.net")
|
|
assert connectors.PRESETS["tiktak"]["url"] == \
|
|
"https://tiktak-dev.dracodev.net/mcp"
|
|
|
|
|
|
def test_derivation_dev_depuis_agent_evenements_seul(monkeypatch):
|
|
# Cas réel de la dev : seul AGENT_EVENEMENTS_URL est posé —
|
|
# les quatre URLs sœurs servies suivent en ``-dev``, sans
|
|
# déclarer quatre variables de plus. Gitea ne change pas.
|
|
monkeypatch.setenv(
|
|
"AGENT_EVENEMENTS_URL",
|
|
"https://agent-dev.dracodev.net/api/v1/evenements")
|
|
urls = draco_urls.toutes()
|
|
assert urls["obsigate"] == "https://og-dev.dracodev.net"
|
|
assert urls["flowdeck"] == "https://flowdeck-dev.dracodev.net"
|
|
assert urls["tiktak"] == "https://tiktak-dev.dracodev.net"
|
|
assert urls["kanbante"] == "https://kanbante-dev.dracodev.net"
|
|
assert urls["gitea"] == "https://git.dracodev.net"
|
|
# Les accesseurs individuels partagent la même résolution.
|
|
assert draco_urls.obsigate_url() == "https://og-dev.dracodev.net"
|
|
assert draco_urls.flowdeck_url() == "https://flowdeck-dev.dracodev.net"
|
|
assert draco_urls.tiktak_url() == "https://tiktak-dev.dracodev.net"
|
|
assert draco_urls.kanbante_url() == "https://kanbante-dev.dracodev.net"
|
|
|
|
|
|
def test_derivation_explicite_gagne_sur_derivation(monkeypatch):
|
|
monkeypatch.setenv(
|
|
"AGENT_EVENEMENTS_URL",
|
|
"https://agent-dev.dracodev.net/api/v1/evenements")
|
|
# Explicites (contrat inchangé) : elles priment sur la dérivation,
|
|
# y compris quand elles pointent le nom officiel.
|
|
monkeypatch.setenv("DRACO_SSO_URL", "https://og.dracodev.net")
|
|
monkeypatch.setenv("TIKTAK_URL", "https://tiktak.example.net")
|
|
urls = draco_urls.toutes()
|
|
assert urls["obsigate"] == "https://og.dracodev.net"
|
|
assert urls["tiktak"] == "https://tiktak.example.net"
|
|
# Non déclarées : dérivées.
|
|
assert urls["flowdeck"] == "https://flowdeck-dev.dracodev.net"
|
|
assert urls["kanbante"] == "https://kanbante-dev.dracodev.net"
|
|
|
|
|
|
def test_derivation_hote_agent_hors_motif_defauts_officiels(monkeypatch):
|
|
# Autre domaine, LAN, IP : aucune dérivation, jamais un lien deviné.
|
|
for url_agent in (
|
|
"https://agent.example.com/api/v1/evenements",
|
|
"https://agent-dev.example.net/api/v1/evenements",
|
|
"https://monagent-dev.dracodev.net/api/v1/evenements",
|
|
"https://agent-dev.dracodev.net.evil.com/api/v1/evenements",
|
|
"http://192.168.30.101:8790/api/v1/evenements",
|
|
"http://127.0.0.1:8790/api/v1/evenements",
|
|
"http://agent-dev.local:8790/api/v1/evenements",
|
|
):
|
|
monkeypatch.setenv("AGENT_EVENEMENTS_URL", url_agent)
|
|
urls = draco_urls.toutes()
|
|
assert urls["obsigate"] == "https://og.dracodev.net", url_agent
|
|
assert urls["flowdeck"] == "https://flowdeck.dracodev.net", url_agent
|
|
assert urls["tiktak"] == "https://tiktak.dracodev.net", url_agent
|
|
assert urls["kanbante"] == "https://kanbante.dracodev.net", url_agent
|
|
|
|
|
|
def test_derivation_agent_officiel_suffixe_vide(monkeypatch):
|
|
# Hôte agent officiel : suffixe vide, les sœurs restent officielles.
|
|
monkeypatch.setenv(
|
|
"AGENT_EVENEMENTS_URL", "https://agent.dracodev.net/api/v1/evenements")
|
|
urls = draco_urls.toutes()
|
|
assert urls["obsigate"] == "https://og.dracodev.net"
|
|
assert urls["flowdeck"] == "https://flowdeck.dracodev.net"
|
|
assert urls["tiktak"] == "https://tiktak.dracodev.net"
|
|
assert urls["kanbante"] == "https://kanbante.dracodev.net"
|
|
|
|
|
|
def test_bouton_sso_login_depuis_config(monkeypatch):
|
|
from app.routers.auth import _sso_login_url
|
|
|
|
assert _sso_login_url() == (
|
|
"https://og.dracodev.net/sso/authorize?return_to="
|
|
"https%3A%2F%2Fflowdeck.dracodev.net%2Fauth%2Fdraco")
|
|
monkeypatch.setenv("DRACO_SSO_URL", "https://og-dev.dracodev.net")
|
|
monkeypatch.setenv(
|
|
"OAUTH_REDIRECT_URI", "https://flowdeck-dev.dracodev.net/auth/callback")
|
|
assert _sso_login_url() == (
|
|
"https://og-dev.dracodev.net/sso/authorize?return_to="
|
|
"https%3A%2F%2Fflowdeck-dev.dracodev.net%2Fauth%2Fdraco")
|