feat(web): row-count selector on the Executions card

Adds a "Show" selector (10 / 25 / 50 / 100) to the card header and drops the
default row count from 15 to 10. The chosen value rides on the card's own
polling URL (`/executions?limit=N`), so it survives the 5s htmx refresh with no
client-side state; a config default outside those four is rendered as an extra
option instead of disappearing. The query is parsed permissively — a stale or
hand-edited polling URL clamps or falls back to the config default rather than
422-ing a card that would otherwise render.

Ordering is now explicit rather than inherited from whatever Gitea returns:
newest first, a not-yet-started (queued) run above everything, ties broken on
Gitea's run id so matrix runs sharing a timestamp keep a stable order.
This commit is contained in:
2026-07-25 15:36:21 +02:00
parent 30fd3120a6
commit 7d1f4e2784
9 changed files with 280 additions and 22 deletions
+55 -4
View File
@@ -36,7 +36,7 @@ def _make_config(
tmp_path: Path,
*,
gitea_url: str | None = "https://gitea.example",
limit: int = 15,
limit: int = 10,
) -> Config:
return Config(
paths=Paths(
@@ -190,7 +190,7 @@ def test_admin_payload_parsed_into_executions(
# Bearer auth against the admin endpoint, with the configured limit.
assert calls == [
(
"https://gitea.example/api/v1/admin/actions/runs?limit=15",
"https://gitea.example/api/v1/admin/actions/runs?limit=10",
"Bearer tok",
)
]
@@ -398,6 +398,57 @@ def test_non_list_runs_payload_is_an_error(
assert view.source == "none"
# ── ordering ────────────────────────────────────────────────────────────────
def test_runs_are_sorted_newest_first(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Gitea's order is not contracted -- the card sorts by recency itself."""
payload = [
_run(run_id=1, number=1, started="2026-07-25T09:00:00Z"),
_run(run_id=3, number=3, started="2026-07-25T11:00:00Z"),
_run(run_id=2, number=2, started="2026-07-25T10:00:00Z"),
]
_install_urlopen(monkeypatch, _admin_ok(payload))
view = list_executions(_make_config(tmp_path), [], token="tok", now=_NOW)
assert [r.number for r in view.runs] == [3, 2, 1]
def test_queued_run_sorts_above_started_runs(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""A run with no start time was just created: it is the newest of all."""
payload = [
_run(run_id=1, number=1, started="2026-07-25T11:00:00Z"),
_run(run_id=2, number=2, status="waiting", conclusion="", started=None, completed=None),
]
_install_urlopen(monkeypatch, _admin_ok(payload))
view = list_executions(_make_config(tmp_path), [], token="tok", now=_NOW)
assert [r.status for r in view.runs] == ["queued", "success"]
def test_same_timestamp_falls_back_to_run_id(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Matrix runs share a timestamp; ids keep the order stable, not arbitrary."""
stamp = "2026-07-25T11:00:00Z"
payload = [
_run(run_id=10, number=10, started=stamp),
_run(run_id=12, number=12, started=stamp),
_run(run_id=11, number=11, started=stamp),
]
_install_urlopen(monkeypatch, _admin_ok(payload))
view = list_executions(_make_config(tmp_path), [], token="tok", now=_NOW)
assert [r.number for r in view.runs] == [12, 11, 10]
# ── endpoint fallback ───────────────────────────────────────────────────────
@@ -418,8 +469,8 @@ def test_admin_denied_falls_back_to_user_endpoint(
assert view.error is None
assert len(view.runs) == 1
assert [url for url, _auth in calls] == [
"https://gitea.example/api/v1/admin/actions/runs?limit=15",
"https://gitea.example/api/v1/user/actions/runs?limit=15",
"https://gitea.example/api/v1/admin/actions/runs?limit=10",
"https://gitea.example/api/v1/user/actions/runs?limit=10",
]