Merge pull request 'fix(webui): suppress misleading empty queue copy on fail-closed fetch (#458)' (#459) from feat/issue-458-queue-fail-closed-ux into master
This commit was merged in pull request #459.
This commit is contained in:
@@ -12,12 +12,14 @@ from starlette.testclient import TestClient
|
|||||||
from webui.app import create_app
|
from webui.app import create_app
|
||||||
from webui.queue_loader import (
|
from webui.queue_loader import (
|
||||||
PaginationMeta,
|
PaginationMeta,
|
||||||
|
QueueSnapshot,
|
||||||
_classify_issue,
|
_classify_issue,
|
||||||
_classify_pr,
|
_classify_pr,
|
||||||
_extract_linked_issue,
|
_extract_linked_issue,
|
||||||
load_queue_snapshot,
|
load_queue_snapshot,
|
||||||
snapshot_to_dict,
|
snapshot_to_dict,
|
||||||
)
|
)
|
||||||
|
from webui.queue_views import render_queue_page
|
||||||
|
|
||||||
_RECENT = datetime.now(timezone.utc).isoformat()
|
_RECENT = datetime.now(timezone.utc).isoformat()
|
||||||
_STALE = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
|
_STALE = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
|
||||||
@@ -210,5 +212,77 @@ class TestQueueRoutes(unittest.TestCase):
|
|||||||
self.assertEqual(len(snapshot.prs), 0)
|
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:</strong> 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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -38,7 +38,13 @@ def _queue_table(
|
|||||||
title: str,
|
title: str,
|
||||||
items: tuple[QueueItem, ...],
|
items: tuple[QueueItem, ...],
|
||||||
columns: tuple[tuple[str, str], ...],
|
columns: tuple[tuple[str, str], ...],
|
||||||
|
fetch_failed: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
if fetch_failed:
|
||||||
|
return (
|
||||||
|
f"<h3>{html.escape(title)}</h3>"
|
||||||
|
"<p class='muted'>Not loaded — queue fetch did not complete.</p>"
|
||||||
|
)
|
||||||
if not items:
|
if not items:
|
||||||
return f"<h3>{html.escape(title)}</h3><p class='muted'>No open items.</p>"
|
return f"<h3>{html.escape(title)}</h3><p class='muted'>No open items.</p>"
|
||||||
|
|
||||||
@@ -73,9 +79,11 @@ def render_queue_page(snapshot: QueueSnapshot) -> str:
|
|||||||
f"{html.escape(snapshot.fetch_error)}</p></div>"
|
f"{html.escape(snapshot.fetch_error)}</p></div>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fetch_failed = bool(snapshot.fetch_error)
|
||||||
pr_section = _queue_table(
|
pr_section = _queue_table(
|
||||||
title="Open pull requests",
|
title="Open pull requests",
|
||||||
items=snapshot.prs,
|
items=snapshot.prs,
|
||||||
|
fetch_failed=fetch_failed,
|
||||||
columns=(
|
columns=(
|
||||||
("number", "#"),
|
("number", "#"),
|
||||||
("title", "Title"),
|
("title", "Title"),
|
||||||
@@ -88,6 +96,7 @@ def render_queue_page(snapshot: QueueSnapshot) -> str:
|
|||||||
issue_section = _queue_table(
|
issue_section = _queue_table(
|
||||||
title="Open issues",
|
title="Open issues",
|
||||||
items=snapshot.issues,
|
items=snapshot.issues,
|
||||||
|
fetch_failed=fetch_failed,
|
||||||
columns=(
|
columns=(
|
||||||
("number", "#"),
|
("number", "#"),
|
||||||
("title", "Title"),
|
("title", "Title"),
|
||||||
|
|||||||
Reference in New Issue
Block a user