diff --git a/tests/test_webui_queue_dashboard.py b/tests/test_webui_queue_dashboard.py index cc9ea97..cbdcc16 100644 --- a/tests/test_webui_queue_dashboard.py +++ b/tests/test_webui_queue_dashboard.py @@ -12,12 +12,14 @@ from starlette.testclient import TestClient from webui.app import create_app from webui.queue_loader import ( PaginationMeta, + QueueSnapshot, _classify_issue, _classify_pr, _extract_linked_issue, load_queue_snapshot, snapshot_to_dict, ) +from webui.queue_views import render_queue_page _RECENT = datetime.now(timezone.utc).isoformat() _STALE = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat() @@ -210,5 +212,77 @@ class TestQueueRoutes(unittest.TestCase): self.assertEqual(len(snapshot.prs), 0) +def _empty_pagination() -> PaginationMeta: + return PaginationMeta( + page=1, + per_page=50, + returned_count=0, + has_more=False, + is_final_page=True, + inventory_complete=True, + pages_fetched=1, + ) + + +def _empty_fetch(*_args, **_kwargs): + return [], _empty_pagination() + + +class TestQueueFailClosedUx(unittest.TestCase): + """Regression tests for #458 fail-closed empty-state copy.""" + + def test_fail_closed_view_suppresses_empty_queue_copy(self): + snapshot = QueueSnapshot( + project_id="gitea-tools", + repo_label="Scaled-Tech-Consulting/Gitea-Tools", + prs=(), + issues=(), + pr_pagination=None, + issue_pagination=None, + fetch_error="Gitea credentials unavailable for gitea.prgs.cc", + ) + html = render_queue_page(snapshot) + self.assertIn("Queue unavailable", html) + self.assertIn("Not loaded", html) + self.assertNotIn("No open items.", html) + self.assertIn("pagination: unavailable", html) + + def test_fail_closed_route_does_not_show_empty_queue_copy(self): + client = TestClient(create_app()) + snapshot = load_queue_snapshot( + fetch_prs=_empty_fetch, + fetch_issues=_empty_fetch, + ) + snapshot = QueueSnapshot( + project_id=snapshot.project_id, + repo_label=snapshot.repo_label, + prs=(), + issues=(), + pr_pagination=None, + issue_pagination=None, + fetch_error="Gitea credentials unavailable for gitea.prgs.cc", + ) + with mock.patch("webui.app.load_queue_snapshot", return_value=snapshot): + response = client.get("/queue") + self.assertEqual(response.status_code, 200) + self.assertIn("Queue unavailable", response.text) + self.assertNotIn("No open items.", response.text) + + def test_successful_empty_inventory_shows_empty_copy(self): + snapshot = load_queue_snapshot( + fetch_prs=_empty_fetch, + fetch_issues=_empty_fetch, + ) + self.assertIsNone(snapshot.fetch_error) + self.assertEqual(len(snapshot.prs), 0) + self.assertEqual(len(snapshot.issues), 0) + self.assertTrue(snapshot.pr_pagination.inventory_complete) + + html = render_queue_page(snapshot) + self.assertNotIn("Queue unavailable", html) + self.assertEqual(html.count("No open items."), 2) + self.assertIn("pagination (complete)", html) + + if __name__ == "__main__": unittest.main() \ No newline at end of file diff --git a/webui/queue_views.py b/webui/queue_views.py index 8294b8e..e5e6dd7 100644 --- a/webui/queue_views.py +++ b/webui/queue_views.py @@ -38,7 +38,13 @@ def _queue_table( title: str, items: tuple[QueueItem, ...], columns: tuple[tuple[str, str], ...], + fetch_failed: bool = False, ) -> str: + if fetch_failed: + return ( + f"
Not loaded — queue fetch did not complete.
" + ) if not items: return f"No open items.
" @@ -73,9 +79,11 @@ def render_queue_page(snapshot: QueueSnapshot) -> str: f"{html.escape(snapshot.fetch_error)}" ) + fetch_failed = bool(snapshot.fetch_error) pr_section = _queue_table( title="Open pull requests", items=snapshot.prs, + fetch_failed=fetch_failed, columns=( ("number", "#"), ("title", "Title"), @@ -88,6 +96,7 @@ def render_queue_page(snapshot: QueueSnapshot) -> str: issue_section = _queue_table( title="Open issues", items=snapshot.issues, + fetch_failed=fetch_failed, columns=( ("number", "#"), ("title", "Title"),