-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Root Cause Analysis
test_cli.py has good coverage of the happy paths, but several distinct code branches in cli.py remain unexercised. Each gap is small on its own, but together they leave real execution paths unverified — including the auto-refresh feature which is central to the interactive mode.
1. _interactive_loop auto-refresh branches (lines 169–185)
When change_event.is_set() is True, three distinct re-render paths run depending on the current view:
if change_event.is_set():
change_event.clear()
sessions = get_all_sessions(path)
if view == "home": # ← untested
_draw_home(...)
elif view == "cost": # ← untested
render_cost_view(...)
elif view == "detail" and detail_idx is not None: # ← untested
_show_session_by_index(...)None of these branches are exercised. The _FileChangeHandler and _start_observer are unit-tested in isolation, but the wiring inside _interactive_loop that consumes the event is never triggered.
Tests to add — call _interactive_loop (or the main group) with a monkeypatched _read_line_nonblocking that:
- Pre-sets
change_eventto assert home-view auto-refresh - Navigates to cost view, then pre-sets
change_eventto assert cost-view auto-refresh - Navigates to detail view (e.g. input
"1"), then pre-setschange_eventto assert detail-view auto-refresh (including thedetail_idx is not Noneguard)
2. Uppercase interactive commands 'Q', 'C', 'R' (lines 207–220)
The interactive loop handles both cases:
if line in ("q", "Q"): break
if line in ("c", "C"): ...cost view...
if line in ("r", "R"): ...refresh...Tests only ever send lowercase q, c, r. The uppercase variants are dead code from the test perspective.
Tests to add:
test_interactive_quit_uppercase— input"Q\n", assert exit code 0test_interactive_cost_view_uppercase— input"C\nq\n", assert "Cost" in outputtest_interactive_refresh_uppercase— input"R\nq\n", assert exit code 0
3. _show_session_by_index with events_path=None (lines 77–80)
if s.events_path is None:
console.print("[red]No events path for this session.[/red]")
returntest_show_session_by_index_missing_file covers the OSError/FileNotFoundError path (non-None path to a deleted file), but the events_path=None branch is never hit.
Test to add: Create a SessionSummary with events_path=None, call _show_session_by_index(console, [s], 1), and assert "No events path" appears in the captured output.
4. Group-level --path propagation to subcommands (cli.py ctx.obj)
Each subcommand falls back to the group-level path:
path = path or ctx.obj.get("path")All existing tests pass --path directly to the subcommand (e.g. ["summary", "--path", str(tmp_path)]). No test passes --path at the group level and omits it from the subcommand (e.g. ["--path", str(tmp_path), "summary"]).
Tests to add (one per subcommand — summary, session, cost, live):
- Invoke with
["--path", str(tmp_path), "(subcommand)"](no--pathon subcommand) - Assert
exit_code == 0and expected output matches the subcommand-level--pathtests
5. _ensure_aware — no unit tests
def _ensure_aware(dt: datetime | None) -> datetime | None:
if dt is not None and dt.tzinfo is None:
return dt.replace(tzinfo=UTC)
return dtThis is exercised by --since/--until CLI option parsing end-to-end, but never verified directly. Three distinct cases exist:
dt=None→ returnsNonedtalready timezone-aware → returns unchangeddtnaive → attaches UTC
Tests to add: Three parametrized unit tests covering all three cases.
Acceptance Criteria
- Auto-refresh: tests trigger
change_eventwhile in each view (home,cost,detail) and assert the correct render function is called / output appears - Uppercase
Q/C/Rproduce the same observable outcomes as their lowercase equivalents -
_show_session_by_index(console, [session_with_none_path], 1)outputs the "No events path" error - Group-level
--pathpropagates correctly to all four subcommands without error -
_ensure_aware(None)→None,_ensure_aware(aware_dt)→ sameaware_dt,_ensure_aware(naive_dt)→ UTC-attached datetime - All new tests pass with no regressions
Generated by Test Suite Analysis · ◷
Warning
⚠️ Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
pypi.org
To allow these domains, add them to the network.allowed list in your workflow frontmatter:
network:
allowed:
- defaults
- "pypi.org"See Network Configuration for more information.