"""Tests for issue #92: Browse activity dates from the keyboard. Covers: - AC92-1: [ and ] select previous/next day, g opens date entry, t returns to today/live - AC92-2: Date entry accepts valid dates, reports errors, returns on cancel - AC92-3: Selecting a date obtains correct local-day evidence - AC92-4: Historical date, measurement, and context remain stable across refresh - AC92-5: t resolves to today/live, themes through discoverable control - AC92-6: Drill-down shows available data with distinguishable states - AC92-7: Keyboard actions verified from normal launch with headless driver - AC92-8: Equivalent clickable actions at 80x24 and constrained widths """ from datetime import datetime, timedelta, timezone from pathlib import Path from unittest.mock import patch import pytest import sys sys.path.insert(0, str(Path(__file__).parent.parent / "src")) from fenris.store import init_store from fenris.monitoring_periods import ensure_period_open from fenris.local_day import ( LocalDaySummary, local_day_boundaries, persist_local_day, ) from fenris.tz_util import detect_system_tz from fenris.tui import ( FenrisTuiApp, _RANGE_DEFAULT, ) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _clock(year=2026, month=9, day=30, hour=12): return datetime(year, month, day, hour, 0, 0, tzinfo=timezone.utc) def _insert_segment(conn, opened_at="2026-09-01T00:00:00+00:00", identity_key="nqn.test"): conn.execute( "INSERT INTO controller_segments " "(opened_at, identity_key, identity_degraded, subnqn, sn, mn, fr, vid, ssvid, transport) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (opened_at, identity_key, False, "nqn.test", "SN123", "Samsung SSD 970 EVO Plus 1TB", "FW1", "0x144d", "0x144d", "pcie"), ) conn.commit() def _insert_day(conn, day, bw=1024*1024*100, coverage=0.95, samples=24): conn.execute( "INSERT INTO day_aggregates (day, active_seconds, idle_seconds, powered_off_seconds, " "unknown_seconds, bytes_written_delta, bytes_read_delta, sample_count, coverage) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", (day, 3600, 0, 0, 0, bw, 0, samples, coverage), ) conn.commit() def _open_period(conn, start="2026-09-01T00:00:00+00:00"): ensure_period_open(conn, datetime.fromisoformat(start)) def _insert_local_day(conn, local_date, tz_name="UTC", tz_offset="+00:00", bw=300, br=130, complete=True, utc_start=None, utc_end=None): if utc_start is None: utc_start = f"{local_date}T00:00:00+00:00" if utc_end is None: utc_end = f"{(datetime.fromisoformat(local_date) + timedelta(days=1)).strftime('%Y-%m-%d')}T00:00:00+00:00" summary = LocalDaySummary( local_date=local_date, tz_name=tz_name, tz_offset=tz_offset, utc_start=utc_start, utc_end=utc_end, bytes_written=bw, bytes_read=br, coverage=0.95, sample_count=24, complete=complete, ) persist_local_day(conn, summary) conn.commit() def _setup_store(tmp_path, n_days=30, today=None): """Create a store with n_days of data and return (conn, clock).""" if today is None: today = _clock() conn = init_store(tmp_path / "test.db") _insert_segment(conn) _open_period(conn) for i in range(n_days): d = (today - timedelta(days=n_days - 1 - i)).strftime("%Y-%m-%d") _insert_day(conn, d) return conn, today def _make_app(tmp_path, clock=None): """Create a FenrisTuiApp with the clock mocked to *clock*. Returns (app, patcher) - caller must keep patcher alive during the test. """ if clock is None: clock = _clock() import fenris.tui as tui_mod from datetime import datetime as _real_datetime class MockDatetime(_real_datetime): @classmethod def now(cls, tz=None): return clock mock_dt = MockDatetime patcher = patch.object(tui_mod, "datetime", mock_dt) patcher.start() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) return app, patcher def _visible_selected_date(app): return str(app.query_one("#local-day").render())[:10] # --------------------------------------------------------------------------- # AC92-1: Keyboard bindings exist # --------------------------------------------------------------------------- class TestBindingExistence: def test_open_square_bracket_binding(self, tmp_path): """The [ binding exists for previous day navigation.""" app = FenrisTuiApp(store_path=tmp_path / "test.db") binding_keys = {b.key for b in app.BINDINGS} assert "left_square_bracket" in binding_keys def test_close_square_bracket_binding(self, tmp_path): """The ] binding exists for next day navigation.""" app = FenrisTuiApp(store_path=tmp_path / "test.db") binding_keys = {b.key for b in app.BINDINGS} assert "right_square_bracket" in binding_keys def test_g_binding_exists(self, tmp_path): """The g binding exists for date entry.""" app = FenrisTuiApp(store_path=tmp_path / "test.db") binding_keys = {b.key for b in app.BINDINGS} assert "g" in binding_keys def test_t_binding_is_today(self, tmp_path): """The t binding maps to action_today, not a theme action.""" app = FenrisTuiApp(store_path=tmp_path / "test.db") t_binding = next(b for b in app.BINDINGS if b.key == "t") assert "today" in t_binding.action.lower() def test_pause_resume_quit_bindings_exist(self, tmp_path): """Existing bindings are preserved.""" app = FenrisTuiApp(store_path=tmp_path / "test.db") binding_keys = {b.key for b in app.BINDINGS} assert "p" in binding_keys # pause assert "r" in binding_keys # resume assert "q" in binding_keys # quit assert "d" in binding_keys # disclosures assert "m" in binding_keys # motion toggle # --------------------------------------------------------------------------- # AC92-1: Bracket navigation from normal launch # --------------------------------------------------------------------------- class TestBracketNavigation: @pytest.mark.asyncio async def test_right_bracket_advances_day(self, tmp_path): """The ] key moves to the next (newer) day when not at the end.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() # Move to an older day first so ] can advance for _ in range(3): await pilot.press("left_square_bracket") day_before = _visible_selected_date(app) await pilot.press("right_square_bracket") day_after = _visible_selected_date(app) # ] should move to a newer day (closer to today) assert day_after > day_before @pytest.mark.asyncio async def test_left_bracket_goes_to_previous_day(self, tmp_path): """The [ key moves to the previous (older) day.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() day_before = _visible_selected_date(app) await pilot.press("left_square_bracket") day_after = _visible_selected_date(app) # [ should move to an older day (further from today) assert day_after < day_before @pytest.mark.asyncio async def test_bracket_navigates_outside_visible_range(self, tmp_path): """[ beyond the current range shifts the visible window.""" app_clock = _clock() conn, clock = _setup_store(tmp_path, n_days=30, today=app_clock) conn.close() app, patcher = _make_app(tmp_path, clock=app_clock) try: async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() graph = app.query_one("#usage-history") assert graph.range_days == _RANGE_DEFAULT # Navigate to oldest day by pressing [ many times for _ in range(29): await pilot.press("left_square_bracket") # Visible readout identifies oldest selected date. selected_day = _visible_selected_date(app) oldest_day = (app_clock - timedelta(days=29)).strftime("%Y-%m-%d") assert selected_day == oldest_day finally: patcher.stop() @pytest.mark.asyncio async def test_bracket_at_boundary_does_not_move(self, tmp_path): """] at the last day does not move the selection.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() initial_date = _visible_selected_date(app) await pilot.press("right_square_bracket") assert _visible_selected_date(app) == initial_date # --------------------------------------------------------------------------- # AC92-1: Arrow keys inspect the selected view # --------------------------------------------------------------------------- class TestArrowInspection: @pytest.mark.asyncio async def test_arrow_keys_still_work_in_daily_view(self, tmp_path): """Arrow keys move the selection within the visible daily view.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() await pilot.press("v", "v") app.query_one("#usage-history").focus() await pilot.pause() day_before = _visible_selected_date(app) await pilot.press("left") day_after = _visible_selected_date(app) assert day_after != day_before @pytest.mark.asyncio async def test_arrow_keys_inspect_hours_in_drill(self, tmp_path): """Arrow keys navigate hours when in hourly drill-down.""" conn, clock = _setup_store(tmp_path, n_days=14) local_date = datetime.now().astimezone().date().isoformat() timezone_name = detect_system_tz() utc_start, utc_end, tz_offset = local_day_boundaries(local_date, timezone_name) _insert_local_day( conn, local_date, tz_name=timezone_name, tz_offset=tz_offset, utc_start=utc_start.isoformat(), utc_end=utc_end.isoformat(), ) hour = utc_start.replace(minute=0, second=0, microsecond=0) if hour < utc_start: hour += timedelta(hours=1) while hour + timedelta(hours=1) <= utc_end: conn.execute( "INSERT INTO hour_observations " "(hour, bytes_written_delta, bytes_read_delta, coverage, sample_count) " "VALUES (?, 1000000, 2000000, 1, 1)", (hour.isoformat(),), ) hour += timedelta(hours=1) conn.commit() conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() await pilot.press("v", "v") await pilot.click("#usage-history") # Enter drill-down await pilot.press("enter") assert app.query_one("#activity-tabs").active == "view-day" # Arrow keys navigate hours readout_before = str(app.query_one("#bar-readout").render()) await pilot.press("left") assert str(app.query_one("#bar-readout").render()) != readout_before # --------------------------------------------------------------------------- # AC92-1: t returns to today/live # --------------------------------------------------------------------------- class TestTodayBinding: @pytest.mark.asyncio async def test_t_returns_to_today_after_browsing(self, tmp_path): """The t key returns to today's live view after browsing.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() # Browse to an older day using [ for _ in range(5): await pilot.press("left_square_bracket") browsed_day = _visible_selected_date(app) today = datetime.now().astimezone().date().isoformat() assert browsed_day != today # Press t to return to today await pilot.press("t") assert app.query_one("#activity-tabs").active == "view-live" @pytest.mark.asyncio async def test_t_exits_hourly_drill(self, tmp_path): """The t key exits hourly drill-down back to daily view.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() await pilot.press("v", "v") await pilot.click("#usage-history") # Enter drill await pilot.press("enter") assert app.query_one("#activity-tabs").active == "view-day" # t returns to daily await pilot.press("t") assert app.query_one("#activity-tabs").active == "view-live" # --------------------------------------------------------------------------- # AC92-2: Date entry screen # --------------------------------------------------------------------------- class TestDateEntry: @pytest.mark.asyncio async def test_g_opens_date_entry(self, tmp_path): """The g key opens the date entry screen.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() await pilot.press("g") await pilot.pause() # Date picker should be pushed as a screen assert app.screen.query_one("#date-input") is not None @pytest.mark.asyncio async def test_valid_date_navigates(self, tmp_path): """Entering a valid date navigates to that day.""" app_clock = _clock() conn, clock = _setup_store(tmp_path, n_days=30, today=app_clock) conn.close() app, patcher = _make_app(tmp_path, clock=app_clock) try: async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() target_date = (app_clock - timedelta(days=5)).strftime("%Y-%m-%d") await pilot.press("g") await pilot.press(*target_date) await pilot.press("enter") await pilot.pause() assert _visible_selected_date(app) == target_date finally: patcher.stop() @pytest.mark.asyncio async def test_invalid_date_shows_error(self, tmp_path): """Entering an invalid date shows an error message.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() await pilot.press("g") await pilot.pause() await pilot.press("n", "o", "t", " ", "a", " ", "d", "a", "t", "e") await pilot.press("enter") await pilot.pause() # Should remain on date picker (error shown, not dismissed) error = str(app.screen.query_one("#date-error").render()) assert "Invalid date format" in error @pytest.mark.asyncio async def test_cancel_returns_to_prior(self, tmp_path): """Pressing escape in date entry returns to the prior view.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() prior_readout = str(app.query_one("#bar-readout").render()) await pilot.press("g") await pilot.pause() await pilot.press("escape") await pilot.pause() # Should be back to the graph with the same selection assert str(app.query_one("#bar-readout").render()) == prior_readout # --------------------------------------------------------------------------- # AC92-3: Selecting a date obtains correct local-day evidence # --------------------------------------------------------------------------- class TestLocalDayEvidence: @pytest.mark.asyncio async def test_bracket_shows_local_day_for_selected_date(self, tmp_path): """Navigating with brackets shows the local-day summary for that date.""" app_clock = _clock() conn, clock = _setup_store(tmp_path, n_days=14, today=app_clock) # Insert local-day data for a day within the 14-day range target = (app_clock - timedelta(days=4)).strftime("%Y-%m-%d") _insert_local_day(conn, target, bw=500, br=200) conn.close() app, patcher = _make_app(tmp_path, clock=app_clock) try: async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() # Navigate to the target day for _ in range(4): await pilot.press("left_square_bracket") await pilot.pause() local_day = app.query_one("#local-day") text = str(local_day.render()) assert target in text finally: patcher.stop() @pytest.mark.asyncio async def test_date_picker_shows_local_day(self, tmp_path): """Picking a date via the date picker shows its local-day summary.""" conn, clock = _setup_store(tmp_path, n_days=14) target = (clock - timedelta(days=10)).strftime("%Y-%m-%d") _insert_local_day(conn, target, bw=100, br=50) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() # Simulate the date picker callback directly app._go_to_date_callback(target) await pilot.pause() local_day = app.query_one("#local-day") text = str(local_day.render()) assert target in text # --------------------------------------------------------------------------- # AC92-4: Browsing stability across background refresh # --------------------------------------------------------------------------- class TestBrowsingStability: @pytest.mark.asyncio async def test_selection_stable_across_refresh(self, tmp_path): """Browsing selection is preserved when background refresh fires.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=0.1) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() # Browse to a specific day for _ in range(3): await pilot.press("left_square_bracket") browsed_day = _visible_selected_date(app) # Wait for at least one refresh tick import asyncio await asyncio.sleep(0.3) await pilot.pause() # Selection should be preserved assert _visible_selected_date(app) == browsed_day @pytest.mark.asyncio async def test_navigate_away_does_not_jump_on_refresh(self, tmp_path): """After navigating away from today, refresh does not jump back.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=0.1) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() # Browse to older day using [ for _ in range(5): await pilot.press("left_square_bracket") browsed_day = _visible_selected_date(app) today = datetime.now().astimezone().date().isoformat() assert browsed_day != today # Wait for refresh import asyncio await asyncio.sleep(0.3) await pilot.pause() # Should NOT jump back to today assert _visible_selected_date(app) == browsed_day assert _visible_selected_date(app) != today # --------------------------------------------------------------------------- # AC92-6: Drill-down shows available data # --------------------------------------------------------------------------- class TestDrillDownData: @pytest.mark.asyncio async def test_drill_down_shows_loading_then_data(self, tmp_path): """Drill-down transitions from loading to actual data.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() await pilot.press("v", "v") await pilot.click("#usage-history") # Enter drill-down await pilot.press("enter") assert app.query_one("#activity-tabs").active == "view-day" assert "hourly evidence unavailable" in str( app.query_one("#bar-readout").render() ) # --------------------------------------------------------------------------- # AC92-8: Constrained width behavior # --------------------------------------------------------------------------- class TestConstrainedWidth: @pytest.mark.asyncio async def test_bracket_navigation_works_at_80x24(self, tmp_path): """Date navigation works at minimum 80x24 terminal size.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(80, 24)) as pilot: await pilot.pause() day_before = _visible_selected_date(app) await pilot.press("left_square_bracket") day_after = _visible_selected_date(app) assert day_after < day_before @pytest.mark.asyncio async def test_g_opens_date_entry_at_80x24(self, tmp_path): """Date entry opens at minimum terminal size.""" conn, clock = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(80, 24)) as pilot: await pilot.pause() await pilot.press("g") await pilot.pause() assert app.screen.query_one("#date-input") is not None class TestClickableNavigation: @pytest.mark.asyncio @pytest.mark.parametrize("size", [(80, 24), (70, 20)]) async def test_click_and_keyboard_select_same_local_date(self, tmp_path, size): """Clickable and keyboard navigation share date-entry transitions.""" app_clock = _clock() conn, _ = _setup_store(tmp_path, n_days=14, today=app_clock) conn.close() app, patcher = _make_app(tmp_path, clock=app_clock) expected = (app_clock - timedelta(days=1)).strftime("%Y-%m-%d") direct = (app_clock - timedelta(days=5)).strftime("%Y-%m-%d") try: async with app.run_test(size=size) as pilot: await pilot.press("left_square_bracket") keyboard_date = _visible_selected_date(app) assert keyboard_date == expected await pilot.press("t") await pilot.click("#activity-tools", offset=(2, 0)) assert _visible_selected_date(app) == keyboard_date await pilot.press("g") await pilot.press(*direct) await pilot.press("enter") await pilot.pause() assert _visible_selected_date(app) == direct finally: patcher.stop() class TestHistoricalSelectionIdentity: @pytest.mark.asyncio async def test_unavailable_entered_date_stays_visible_after_refresh(self, tmp_path): """An unavailable requested date remains selected and explained.""" conn, clock = _setup_store(tmp_path, n_days=30) conn.close() app, patcher = _make_app(tmp_path, clock=clock) target = (clock - timedelta(days=120)).strftime("%Y-%m-%d") try: async with app.run_test(size=(100, 40)) as pilot: await pilot.press("g") await pilot.press(*target) await pilot.press("enter") await pilot.pause() local_day = str(app.query_one("#local-day").render()) assert target in local_day assert "unavailable" in local_day app.on_refresh_tick() await pilot.pause() local_day = str(app.query_one("#local-day").render()) assert target in local_day assert "unavailable" in local_day finally: patcher.stop() @pytest.mark.asyncio async def test_retained_summary_outside_recent_window_remains_available(self, tmp_path): """Direct date entry can inspect durable local summaries beyond 90 days.""" clock = _clock() conn, _ = _setup_store(tmp_path, n_days=30, today=clock) target = (clock - timedelta(days=120)).strftime("%Y-%m-%d") utc_start, utc_end, tz_offset = local_day_boundaries( target, "Asia/Kolkata", ) _insert_local_day( conn, target, tz_name="Asia/Kolkata", tz_offset=tz_offset, bw=2_000_000_000, br=1_000_000_000, utc_start=utc_start.isoformat(), utc_end=utc_end.isoformat(), ) conn.close() app, patcher = _make_app(tmp_path, clock=clock) try: async with app.run_test(size=(100, 40)) as pilot: await pilot.press("g") await pilot.press(*target) await pilot.press("enter") await pilot.pause() await pilot.press("v") await pilot.pause() readout = str(app.query_one("#bar-readout").render()) assert target in readout assert "Asia/Kolkata +05:30" in readout assert "W 2.000 GB known · R 1.000 GB known" in readout finally: patcher.stop() @pytest.mark.asyncio async def test_constrained_history_keeps_known_totals_and_evidence_state(self, tmp_path): """Small textual view retains selected day, totals, and shared evidence.""" clock = _clock() conn, _ = _setup_store(tmp_path, n_days=14, today=clock) target = (clock - timedelta(days=2)).strftime("%Y-%m-%d") utc_start, utc_end, tz_offset = local_day_boundaries( target, "Asia/Kolkata", ) _insert_local_day( conn, target, tz_name="Asia/Kolkata", tz_offset=tz_offset, bw=1_500_000_000, br=500_000_000, utc_start=utc_start.isoformat(), utc_end=utc_end.isoformat(), ) conn.execute( "INSERT INTO local_day_unallocated_evidence " "(start_sample_id, end_sample_id, start_local_date, end_local_date, " " start_tz_name, end_tz_name, started_at, ended_at, bytes_written, " " bytes_read, reason) VALUES (1, 2, ?, ?, ?, ?, ?, ?, ?, ?, 'local_midnight')", (target, "2026-09-29", "Asia/Kolkata", "Asia/Kolkata", utc_start.isoformat(), utc_start.isoformat(), 200_000_000, 300_000_000), ) conn.commit() conn.close() app, patcher = _make_app(tmp_path, clock=clock) try: async with app.run_test(size=(70, 20)) as pilot: await pilot.press("g") await pilot.press(*target) await pilot.press("enter") await pilot.press("v") await pilot.pause() readout = str(app.query_one("#bar-readout").render()) assert target in readout assert "Asia/Kolkata +05:30" in readout assert "incomplete" in readout assert "W 1.500 GB known · R 0.500 GB known" in readout assert "shared at midnight W 0.200 GB · R 0.300 GB" in readout finally: patcher.stop() @pytest.mark.asyncio async def test_date_picker_keystrokes_do_not_toggle_measurement(self, tmp_path): """Typing in date entry does not trigger dashboard shortcuts.""" conn, _ = _setup_store(tmp_path, n_days=14) conn.close() app = FenrisTuiApp(store_path=tmp_path / "test.db", refresh_interval_s=999) async with app.run_test(size=(100, 40)) as pilot: await pilot.pause() await pilot.press("g") await pilot.press("w") await pilot.press("escape") await pilot.pause() header = str(app.query_one("#live-header").render()) assert "Writes" in header @pytest.mark.asyncio async def test_historical_hour_uses_recorded_timezone_and_utc_identity(self, tmp_path): """Repeated local clock labels retain distinct UTC-hour identities.""" clock = _clock(2026, 11, 2, 12) conn, _ = _setup_store(tmp_path, n_days=30, today=clock) target = "2026-11-01" _insert_local_day( conn, target, tz_name="America/New_York", tz_offset="-04:00", utc_start="2026-11-01T04:00:00+00:00", utc_end="2026-11-02T05:00:00+00:00", ) conn.execute( "INSERT INTO local_day_unallocated_evidence " "(start_sample_id, end_sample_id, start_local_date, end_local_date, " " start_tz_name, end_tz_name, started_at, ended_at, bytes_written, " " bytes_read, reason) VALUES (1, 2, ?, ?, ?, ?, ?, ?, ?, ?, 'local_midnight')", (target, "2026-11-02", "America/New_York", "America/New_York", "2026-11-02T03:59:00+00:00", "2026-11-02T04:01:00+00:00", 2_000_000_000, 3_000_000_000), ) for index in range(25): hour = datetime(2026, 11, 1, 4, tzinfo=timezone.utc) + timedelta(hours=index) conn.execute( "INSERT INTO hour_observations " "(hour, bytes_written_delta, bytes_read_delta, coverage, sample_count) " "VALUES (?, ?, ?, 1, 1)", (hour.isoformat(), (index + 1) * 1_000_000_000, (index + 1) * 2_000_000_000), ) conn.execute( "UPDATE hour_observations SET coverage = 0.5, unknown_seconds = 1800 " "WHERE hour = ?", ("2026-11-01T04:00:00+00:00",), ) conn.commit() conn.close() app, patcher = _make_app(tmp_path, clock=clock) try: async with app.run_test(size=(100, 40)) as pilot: await pilot.click("#view-history") await pilot.press("g") await pilot.press(*target) await pilot.press("enter") await pilot.pause() local_day = str(app.query_one("#local-day").render()) assert "America/New_York -04:00" in local_day assert "incomplete" in local_day assert "shared at midnight W 2.000 GB · R 3.000 GB" in local_day # Move from final local hour to the second 01:00 occurrence. await pilot.press(*(["left"] * 22)) second_occurrence = str(app.query_one("#bar-readout").render()) assert "01:00 -0500" in second_occurrence assert "America/New_York -0500" in second_occurrence assert "06:00 UTC" in second_occurrence await pilot.press("left") first_occurrence = str(app.query_one("#bar-readout").render()) assert "01:00 -0400" in first_occurrence assert "America/New_York -0400" in first_occurrence assert "05:00 UTC" in first_occurrence await pilot.press("left") incomplete_hour = str(app.query_one("#bar-readout").render()) assert "00:00 -0400" in incomplete_hour assert "50% coverage · incomplete" in incomplete_hour finally: patcher.stop()