diff --git a/src/specify_cli/_console.py b/src/specify_cli/_console.py index 8d1216387f..0e448780ad 100644 --- a/src/specify_cli/_console.py +++ b/src/specify_cli/_console.py @@ -7,6 +7,7 @@ """ from __future__ import annotations +import logging import sys from collections.abc import Callable @@ -21,6 +22,8 @@ from rich.tree import Tree from typer.core import TyperGroup +logger = logging.getLogger(__name__) + BANNER = """ ███████╗██████╗ ███████╗ ██████╗██╗███████╗██╗ ██╗ ██╔════╝██╔══██╗██╔════╝██╔════╝██║██╔════╝╚██╗ ██╔╝ @@ -85,7 +88,7 @@ def _maybe_refresh(self): try: self._refresh_cb() except Exception: - pass + logger.debug("Progress tracker refresh failed", exc_info=True) def render(self): tree = Tree(f"[cyan]{self.title}[/cyan]", guide_style="grey50") diff --git a/tests/test_console_imports.py b/tests/test_console_imports.py index 2ae328732e..9ecb49cf3b 100644 --- a/tests/test_console_imports.py +++ b/tests/test_console_imports.py @@ -1,9 +1,12 @@ """Regression guard: console symbols must remain importable from specify_cli.""" +import logging + from specify_cli import ( console, StepTracker, select_with_arrows, ) +from specify_cli._console import logger as console_logger def test_console_symbols_importable(): @@ -39,3 +42,21 @@ def test_select_with_arrows_raises_on_empty_options(): import pytest with pytest.raises(ValueError, match="at least one option"): select_with_arrows({}) + + +def test_step_tracker_refresh_error_is_logged(caplog): + """Regression: _maybe_refresh must log exceptions instead of silently swallowing.""" + tracker = StepTracker("test") + + def failing_refresh(): + raise RuntimeError("simulated refresh failure") + + tracker.attach_refresh(failing_refresh) + tracker.add("step1", "Step One") + + with caplog.at_level(logging.DEBUG, logger=console_logger.name): + tracker.complete("step1", "done") + + assert "Progress tracker refresh failed" in caplog.text + assert "RuntimeError: simulated refresh failure" in caplog.text + assert tracker.steps[0]["status"] == "done"