Files

155 lines
5.1 KiB
Python

"""Capture éclair (feuille de route #12) : API à jeton, page web, ntfy."""
import pytest
from app import models
from app.capture import parse_capture_text, parse_ntfy_message
from tests.conftest import csrf_from
TOKEN = "jeton-de-capture-test"
@pytest.fixture
def capture_env(monkeypatch):
monkeypatch.setenv("KANBANTE_CAPTURE_TOKEN", TOKEN)
monkeypatch.setenv("KANBANTE_CAPTURE_REPO", "Projets/demo")
# ── Parsing ───────────────────────────────────────────────────────
def test_parse_capture_text_first_line_is_title():
title, body = parse_capture_text("Idée géniale\n\nDétails\nsur deux lignes")
assert title == "Idée géniale"
assert body == "Détails\nsur deux lignes"
def test_parse_capture_text_empty():
assert parse_capture_text(" \n ") == ("", "")
def test_parse_ntfy_message_variants():
assert parse_ntfy_message(
{"event": "message", "title": "Titre", "message": "Corps"}
) == ("Titre", "Corps")
assert parse_ntfy_message(
{"event": "message", "message": "Ligne un\n\nLigne deux"}
) == ("Ligne un", "Ligne deux")
assert parse_ntfy_message({"event": "open"}) is None
assert parse_ntfy_message(
{"event": "message", "message": "auto", "tags": ["kanbante"]}
) is None # never ingest our own posts
# ── API ───────────────────────────────────────────────────────────
def test_capture_api_disabled_without_token(client, fake_gitea):
r = client.post("/api/capture", json={"text": "Idée"})
assert r.status_code == 503
def test_capture_api_rejects_wrong_token(client, fake_gitea, capture_env):
r = client.post(
"/api/capture",
json={"text": "Idée"},
headers={"X-Capture-Token": "mauvais"},
)
assert r.status_code == 401
assert fake_gitea.created == []
def test_capture_api_creates_issue_in_default_repo(client, fake_gitea, capture_env):
r = client.post(
"/api/capture",
json={"text": "Idée éclair\n\nLe détail qui compte"},
headers={"X-Capture-Token": TOKEN},
)
assert r.status_code == 201
data = r.json()
assert data["ok"] is True
number = data["issue"]["number"]
assert fake_gitea.created == [("Projets", "demo", "Idée éclair")]
state = models.get_board_state("Projets/demo")
assert number in [c["issue_number"] for c in state["backlog"]]
events = [
e for e in models.get_events("Projets/demo")
if e["issue_number"] == number
]
assert any(
e["event_type"] == "created" and e["source"] == "capture" for e in events
)
def test_capture_api_text_plain_and_bearer(client, fake_gitea, capture_env):
r = client.post(
"/api/capture",
content="Titre en texte brut\nCorps",
headers={
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "text/plain",
},
)
assert r.status_code == 201
assert fake_gitea.created == [("Projets", "demo", "Titre en texte brut")]
def test_capture_api_explicit_repo_and_column(client, fake_gitea, capture_env):
r = client.post(
"/api/capture",
json={"title": "Direct en todo", "repo": "Projets/demo", "column": "todo"},
headers={"X-Capture-Token": TOKEN},
)
assert r.status_code == 201
number = r.json()["issue"]["number"]
state = models.get_board_state("Projets/demo")
assert number in [c["issue_number"] for c in state["todo"]]
def test_capture_api_validates_input(client, fake_gitea, capture_env):
r = client.post(
"/api/capture", json={"text": " "},
headers={"X-Capture-Token": TOKEN},
)
assert r.status_code == 400
r = client.post(
"/api/capture",
json={"title": "X", "repo": "pas-de-slash"},
headers={"X-Capture-Token": TOKEN},
)
assert r.status_code == 400
def test_capture_api_no_repo_anywhere(client, fake_gitea, monkeypatch):
monkeypatch.setenv("KANBANTE_CAPTURE_TOKEN", TOKEN)
monkeypatch.delenv("KANBANTE_CAPTURE_REPO", raising=False)
r = client.post(
"/api/capture", json={"title": "Orpheline"},
headers={"X-Capture-Token": TOKEN},
)
assert r.status_code == 400
# ── Page web ──────────────────────────────────────────────────────
def test_capture_page_renders(client, capture_env):
r = client.get("/capture")
assert r.status_code == 200
assert "Capture éclair" in r.text
assert "Projets/demo" in r.text
def test_capture_web_form_creates_issue(client, fake_gitea, capture_env):
token = csrf_from(client)
r = client.post(
"/capture",
data={
"repo_full_name": "Projets/demo", "title": "Via le formulaire",
"body": "Détails", "column": "backlog", "csrf_token": token,
},
)
assert r.status_code == 200
assert "Capturé" in r.text
assert fake_gitea.created == [("Projets", "demo", "Via le formulaire")]