+85
-33
@@ -771,12 +771,29 @@ class TestQueryDailyGraphData:
|
||||
assert result[0]["is_gap"] is True
|
||||
conn.close()
|
||||
|
||||
def test_window_is_consecutive_utc_days_with_explicit_gaps(self, tmp_path):
|
||||
conn = init_store(tmp_path / "test.db")
|
||||
_insert_day(conn, "2026-09-14", bw=500)
|
||||
result = _query_daily_graph_data(
|
||||
conn, datetime(2026, 9, 16, 12, tzinfo=timezone.utc),
|
||||
)
|
||||
assert len(result) == 90
|
||||
assert result[-3]["day"] == "2026-09-14"
|
||||
assert result[-2]["day"] == "2026-09-15"
|
||||
assert result[-2]["is_gap"] is True
|
||||
assert result[-1]["day"] == "2026-09-16"
|
||||
assert result[-1]["is_partial"] is True
|
||||
conn.close()
|
||||
|
||||
|
||||
class TestQueryHourlyGraphData:
|
||||
def test_empty_day(self, tmp_path):
|
||||
conn = init_store(tmp_path / "test.db")
|
||||
result = _query_hourly_graph_data(conn, "2026-09-20")
|
||||
assert result == []
|
||||
result = _query_hourly_graph_data(
|
||||
conn, "2026-09-20", datetime(2026, 9, 21, tzinfo=timezone.utc),
|
||||
)
|
||||
assert len(result) == 24
|
||||
assert all(item["is_gap"] for item in result)
|
||||
conn.close()
|
||||
|
||||
def test_with_hours(self, tmp_path):
|
||||
@@ -795,9 +812,25 @@ class TestQueryHourlyGraphData:
|
||||
def test_hour_labels(self, tmp_path):
|
||||
conn = init_store(tmp_path / "test.db")
|
||||
_insert_hour(conn, "2026-09-20T12:00:00+00:00", bw=1000)
|
||||
result = _query_hourly_graph_data(conn, "2026-09-20")
|
||||
assert len(result) == 1
|
||||
assert result[0]["local_label"] == "12"
|
||||
result = _query_hourly_graph_data(
|
||||
conn, "2026-09-20", datetime(2026, 9, 21, tzinfo=timezone.utc),
|
||||
)
|
||||
assert len(result) == 24
|
||||
assert result[12]["local_label"] == "12"
|
||||
assert result[12]["bytes_written"] == 1000
|
||||
conn.close()
|
||||
|
||||
def test_current_hour_is_partial_and_future_hours_are_not_gaps(self, tmp_path):
|
||||
conn = init_store(tmp_path / "test.db")
|
||||
result = _query_hourly_graph_data(
|
||||
conn, "2026-09-20",
|
||||
datetime(2026, 9, 20, 14, 30, tzinfo=timezone.utc),
|
||||
)
|
||||
assert result[13]["is_gap"] is True
|
||||
assert result[14]["is_partial"] is True
|
||||
assert result[14]["is_gap"] is True
|
||||
assert result[15]["is_future"] is True
|
||||
assert result[15]["is_gap"] is False
|
||||
conn.close()
|
||||
|
||||
|
||||
@@ -848,6 +881,17 @@ class TestDailyBarGraph:
|
||||
graph._trim_to_range()
|
||||
assert len(graph._day_data) == 7
|
||||
|
||||
def test_scale_includes_allocated_and_unallocated_writes(self):
|
||||
graph = DailyBarGraph()
|
||||
graph._all_day_data = [{
|
||||
"day": "2026-09-16",
|
||||
"total_bytes": 800,
|
||||
"allocated_bytes": 500,
|
||||
"unallocated_bytes": 300,
|
||||
}]
|
||||
graph._trim_to_range()
|
||||
assert graph._max_bytes == 800
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bar graph headless TUI tests (issue #75)
|
||||
@@ -877,6 +921,13 @@ class TestBarGraphTUI:
|
||||
legend = str(app.query_one("#bar-legend").render())
|
||||
assert "Alloc" in legend
|
||||
assert "Zero" in legend
|
||||
range_label = str(app.query_one("#bar-range").render())
|
||||
assert "14 days" in range_label
|
||||
assert "UTC" in range_label
|
||||
plotted = str(app.query_one("#bar-render").render())
|
||||
assert "Writes (" in plotted
|
||||
assert "Day UTC" in plotted
|
||||
assert "→" in plotted
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_arrow_selection(self, tmp_path):
|
||||
@@ -897,20 +948,16 @@ class TestBarGraphTUI:
|
||||
await pilot.pause()
|
||||
await pilot.pause()
|
||||
|
||||
# Right arrow selects first bar
|
||||
await pilot.press("right")
|
||||
await pilot.pause()
|
||||
assert graph.selected_index == 0
|
||||
|
||||
# Right again moves to second
|
||||
await pilot.press("right")
|
||||
await pilot.pause()
|
||||
assert graph.selected_index == 1
|
||||
|
||||
# Left moves back
|
||||
# The newest day is selected by default; left moves backward.
|
||||
initial = graph.selected_index
|
||||
await pilot.press("left")
|
||||
await pilot.pause()
|
||||
assert graph.selected_index == 0
|
||||
assert graph.selected_index == initial - 1
|
||||
|
||||
# Right returns to the newest day.
|
||||
await pilot.press("right")
|
||||
await pilot.pause()
|
||||
assert graph.selected_index == initial
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_range_switching(self, tmp_path):
|
||||
@@ -941,17 +988,17 @@ class TestBarGraphTUI:
|
||||
assert graph.range_days == 7
|
||||
assert len(graph._day_data) == 7
|
||||
|
||||
# Press 3 for 28-day range
|
||||
# Press 3 for 30-day range
|
||||
await pilot.press("3")
|
||||
await pilot.pause()
|
||||
assert graph.range_days == 28
|
||||
assert len(graph._day_data) == 28
|
||||
assert graph.range_days == 30
|
||||
assert len(graph._day_data) == 30
|
||||
|
||||
# Press 4 for 90-day range (only 30 days available)
|
||||
# Exact windows retain gap slots instead of compressing time.
|
||||
await pilot.press("4")
|
||||
await pilot.pause()
|
||||
assert graph.range_days == 90
|
||||
assert len(graph._day_data) == 30
|
||||
assert len(graph._day_data) == 90
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_readout_updates_on_selection(self, tmp_path):
|
||||
@@ -972,12 +1019,13 @@ class TestBarGraphTUI:
|
||||
await pilot.pause()
|
||||
await pilot.pause()
|
||||
|
||||
# No selection initially
|
||||
# Newest day is selected initially.
|
||||
readout = str(app.query_one("#bar-readout").render())
|
||||
assert "select" in readout.lower()
|
||||
assert "UTC" in readout
|
||||
assert "GB" in readout
|
||||
|
||||
# Select first bar
|
||||
await pilot.press("right")
|
||||
# Moving left updates the selected-day readout.
|
||||
await pilot.press("left")
|
||||
await pilot.pause()
|
||||
readout = str(app.query_one("#bar-readout").render())
|
||||
assert "2026-09" in readout
|
||||
@@ -1007,10 +1055,8 @@ class TestBarGraphTUI:
|
||||
await pilot.pause()
|
||||
await pilot.pause()
|
||||
|
||||
# Select a day
|
||||
await pilot.press("right")
|
||||
await pilot.pause()
|
||||
assert graph.selected_index == 0
|
||||
# The newest day is selected automatically.
|
||||
assert graph.selected_index == len(graph._day_data) - 1
|
||||
assert graph.view_mode == "daily"
|
||||
|
||||
# Enter drill-down
|
||||
@@ -1020,6 +1066,14 @@ class TestBarGraphTUI:
|
||||
assert graph.drill_day is not None
|
||||
assert len(graph._hour_data) == 24
|
||||
|
||||
# Five-minute data refreshes retain the selected hour.
|
||||
graph._hourly_selected = 12
|
||||
selected_hour = graph._hour_data[12]["hour"]
|
||||
app._refresh()
|
||||
await pilot.pause()
|
||||
assert graph.view_mode == "hourly"
|
||||
assert graph._hour_data[graph._hourly_selected]["hour"] == selected_hour
|
||||
|
||||
# Esc returns to daily
|
||||
await pilot.press("escape")
|
||||
await pilot.pause()
|
||||
@@ -1208,9 +1262,7 @@ class TestConstrainedLayout:
|
||||
# Focus and select a day
|
||||
app.set_focus(graph)
|
||||
await pilot.pause()
|
||||
await pilot.press("right")
|
||||
await pilot.pause()
|
||||
assert graph.selected_index == 0
|
||||
assert graph.selected_index == len(graph._day_data) - 1
|
||||
|
||||
# Resize to constrained — pilot.resize_terminal changes terminal size
|
||||
await pilot.resize_terminal(79, 24)
|
||||
|
||||
Reference in New Issue
Block a user