from claude_matrix_bot.reset_watcher.diff import Alert, WindowSnapshot from claude_matrix_bot.reset_watcher.format import ( _format_delta, _progress_bar, format_alert, format_armed, ) def test_delta_under_minute_shows_seconds() -> None: assert _format_delta(45) == "+45s" assert _format_delta(-30) == "-30s" assert _format_delta(0) == "+0s" def test_delta_under_hour_shows_minutes() -> None: assert _format_delta(60) == "+1 min" assert _format_delta(720) == "+12 min" assert _format_delta(-300) == "-5 min" def test_delta_at_or_above_hour_shows_hours_and_minutes() -> None: assert _format_delta(3_600) == "+1h" assert _format_delta(3_660) == "+1h 1m" assert _format_delta(12_000) == "+3h 20m" assert _format_delta(-7_200) == "-2h" def test_reset_shift_alert_renders_with_seconds_when_small() -> None: alert = Alert( kind="reset_shift", window="five_hour", detail={ "old_resets_at": 1_700_000_000, "new_resets_at": 1_700_000_045, "delta_seconds": 45, }, ) plain, html = format_alert(alert) assert "+45s" in plain assert "+45s" in html # Should never display "0 min" for a sub-minute delta. assert "0 min" not in plain # Aligned visual frame with armed message assert "
" in html assert "" in html assert "5-hour session" in html def test_threshold_alert_renders_emoji_and_percent() -> None: alert = Alert( kind="threshold_crossed", window="seven_day", detail={"threshold": 80, "utilization_pct": 82.5, "resets_at": 1_700_000_000}, ) plain, html = format_alert(alert) assert "🔥" in plain assert "82.5%" in plain assert "weekly (all)" in html # Aligned visual frame with armed message assert "
" in html assert "
" in html assert "" in html assert "weekly (all)" in html assert "#c0392b" in html # 82.5% -> red severity def test_threshold_50_uses_warn_emoji_not_fire() -> None: """50% threshold gets the warning emoji; only 80%+ uses the fire emoji.""" alert = Alert( kind="threshold_crossed", window="seven_day_sonnet", detail={"threshold": 50, "utilization_pct": 51.0, "resets_at": 1_700_000_000}, ) plain, html = format_alert(alert) assert "⚠️" in plain assert "🔥" not in plain def test_progress_bar_proportional() -> None: assert _progress_bar(0, width=10) == "░░░░░░░░░░" assert _progress_bar(50, width=10) == "█████░░░░░" assert _progress_bar(100, width=10) == "██████████" # Out-of-range stays clamped (defensive, not expected in practice) assert _progress_bar(120, width=10) == "██████████" assert _progress_bar(-5, width=10) == "░░░░░░░░░░" def test_reset_shift_uses_blockquote_and_table() -> None: alert = Alert( kind="reset_shift", window="five_hour", detail={ "old_resets_at": 1_700_000_000, "new_resets_at": 1_700_000_300, "delta_seconds": 300, }, ) plain, html = format_alert(alert) assert "
" in html assert "
" in html assert "5-hour session" in html # delta in plain still uses the arrow form for narrow display assert "→" in plain assert "+5 min" in plain # delta colored amber for <30min shifts assert "#d68910" in html def test_armed_message_renders_blockquote_table() -> None: fresh = { "five_hour": WindowSnapshot(utilization=0.06, resets_at=1_700_000_000), "seven_day": WindowSnapshot(utilization=0.55, resets_at=1_700_500_000), "seven_day_opus": WindowSnapshot(utilization=0.10, resets_at=1_700_500_000), "seven_day_sonnet": WindowSnapshot(utilization=0.62, resets_at=1_700_500_000), } plain, html = format_armed(fresh) assert "🤖" in plain assert "reset-watcher armed" in plain # Each window appears in both plain and html for label in ("5-hour session", "weekly (all)", "weekly Opus", "weekly Sonnet"): assert label in plain assert label in html # New visual frame: blockquote, italic title, code subtitle, table layout assert "
" in html assert "armed" in html assert "baseline captured" in html assert "
" in html assert "resets" in html # No progress bars anymore assert "█" not in plain assert "█" not in html # Severity colors applied per row (matches user's mockup palette) assert "#27ae60" in html # 6% -> green assert "#d68910" in html # 62% -> amber (50-80% band)