diff --git a/tests/conftest.py b/tests/conftest.py index 9742982..00c649a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -85,6 +85,7 @@ class FakeGitea: self._label_seq = 100 self.cache_invalidations = 0 self.state_changes: list[tuple] = [] + self.assignee_changes: list[tuple] = [] self.created: list[tuple] = [] self.comments: dict[tuple, list[dict]] = { ("Projets/demo", 1): [ @@ -239,6 +240,14 @@ class FakeGitea: self.created.append((owner, repo, title)) return issue + async def set_issue_assignees(self, owner, repo, issue_number, logins): + self.assignee_changes.append((owner, repo, issue_number, list(logins))) + for issue in self.issues.get(f"{owner}/{repo}", []): + if issue["number"] == issue_number: + issue["assignees"] = [{"login": name} for name in logins] + return issue + return {} + async def set_issue_state(self, owner, repo, issue_number, state): if self.fail_state_change: raise httpx.HTTPError("simulated write-back failure") diff --git a/tests/test_palier7.py b/tests/test_palier7.py new file mode 100644 index 0000000..55628df --- /dev/null +++ b/tests/test_palier7.py @@ -0,0 +1,81 @@ +"""Palier 7 (0.10.0): mode Triage (issue #11).""" + +import json +import re + +from tests.conftest import csrf_from + + +def _queue_from(page_html: str) -> list: + m = re.search( + r'', + page_html, re.S, + ) + assert m, "file de triage absente de la page" + return json.loads(m.group(1)) + + +def test_triage_picker_lists_project_with_backlog(client, fake_gitea): + r = client.get("/triage") + assert r.status_code == 200 + assert "Projets/demo" in r.text + assert "au backlog" in r.text + + +def test_triage_session_queue_is_backlog_only(client, fake_gitea): + r = client.get("/triage/Projets/demo") + assert r.status_code == 200 + queue = _queue_from(r.text) + numbers = [c["number"] for c in queue] + # Open issues of the fake repo: #1 and #5, both unplaced -> backlog. + # #3 is closed (goes to done), #2 is a PR (excluded). + assert numbers == [1, 5] + card = queue[0] + assert card["title"] == "Tâche un" + assert "excerpt" in card and "html_url" in card + + +def test_triage_session_empty_when_nothing_to_triage(client, fake_gitea): + for issue in fake_gitea.issues["Projets/demo"]: + if issue["number"] in (1, 5): + issue["state"] = "closed" + r = client.get("/triage/Projets/demo") + assert r.status_code == 200 + assert _queue_from(r.text) == [] + + +def test_assign_self_adds_token_user(client, fake_gitea): + token = csrf_from(client) + # Issue #5 is unassigned in the fake; #1 already belongs to bruno. + r = client.post( + "/api/issues/Projets/demo/5/assign-self", + data={"csrf_token": token}, + ) + assert r.status_code == 200 + assert r.json()["ok"] is True + assert r.json()["assignees"] == ["bruno"] + assert fake_gitea.assignee_changes == [("Projets", "demo", 5, ["bruno"])] + # Idempotent: assigning #1 (already bruno's) writes nothing. + r = client.post( + "/api/issues/Projets/demo/1/assign-self", + data={"csrf_token": token}, + ) + assert r.status_code == 200 + assert r.json()["assignees"] == ["bruno"] + assert len(fake_gitea.assignee_changes) == 1 + + +def test_assign_self_requires_csrf(client, fake_gitea): + r = client.post("/api/issues/Projets/demo/1/assign-self", data={}) + assert r.status_code == 403 + assert fake_gitea.assignee_changes == [] + + +def test_excerpt_strips_markdown(): + from app.main import _excerpt + + text = _excerpt("# Titre\n\nDu **gras** et un [lien](http://x) ici.", 280) + assert "Titre" in text and "gras" in text and "lien" in text + assert "#" not in text and "**" not in text + assert _excerpt(None) == "" + assert len(_excerpt("mot " * 200, 50)) <= 51