""" tests/test_analysis_charts.py ------------------------------ Tests for the pure chart-drawing functions in ui/analysis.py. Uses matplotlib's Agg (non-interactive) backend so no display is required. Each test verifies that the function populates the axes correctly and doesn't raise on empty or populated data. """ import matplotlib matplotlib.use("Agg") from matplotlib.figure import Figure import pytest from db.models import get_game_by_name, insert_draw from ui.analysis import ( _empty, _draw_frequency, _draw_heatmap, _draw_gap, _draw_pairs, _draw_odd_even, _draw_sum_range, _draw_deltas, ) DRAWS = [ ("2024-01-01", [1, 13, 36, 61, 69]), ("2024-01-03", [1, 2, 13, 45, 69]), ("2024-01-05", [2, 13, 22, 36, 55]), ] def _ax(): """Return a fresh Axes on a headless figure.""" fig = Figure() return fig.add_subplot(111) @pytest.fixture def pb(tmp_db): game = get_game_by_name("Powerball") for date, nums in DRAWS: insert_draw(game["id"], date, nums, bonus=7, source="test") return game # ── _empty ──────────────────────────────────────────────────────────────────── def test_empty_disables_axis(tmp_db): ax = _ax() _empty(ax, "test message") assert not ax.get_visible() or not ax.axison # ── _draw_frequency ─────────────────────────────────────────────────────────── def test_draw_frequency_creates_bars(pb): ax = _ax() _draw_frequency(ax, pb["id"]) assert len(ax.patches) > 0 def test_draw_frequency_empty_db_no_error(tmp_db): pb = get_game_by_name("Powerball") ax = _ax() _draw_frequency(ax, pb["id"]) # should show _empty placeholder, not raise def test_draw_frequency_last_n(pb): ax = _ax() _draw_frequency(ax, pb["id"], last_n=1) assert len(ax.patches) > 0 def test_draw_frequency_title_includes_last_n(pb): ax = _ax() _draw_frequency(ax, pb["id"], last_n=50) assert "50" in ax.get_title() # ── _draw_heatmap ───────────────────────────────────────────────────────────── def test_draw_heatmap_creates_image(pb): ax = _ax() _draw_heatmap(ax, pb["id"]) assert len(ax.images) > 0 def test_draw_heatmap_empty_db_no_error(tmp_db): pb = get_game_by_name("Powerball") ax = _ax() _draw_heatmap(ax, pb["id"]) # ── _draw_gap ───────────────────────────────────────────────────────────────── def test_draw_gap_creates_bars(pb): ax = _ax() _draw_gap(ax, pb["id"]) assert len(ax.patches) > 0 def test_draw_gap_empty_db_no_error(tmp_db): pb = get_game_by_name("Powerball") ax = _ax() _draw_gap(ax, pb["id"]) # ── _draw_pairs ─────────────────────────────────────────────────────────────── def test_draw_pairs_creates_bars(pb): ax = _ax() _draw_pairs(ax, pb["id"]) assert len(ax.patches) > 0 def test_draw_pairs_respects_top_n(pb): ax = _ax() _draw_pairs(ax, pb["id"], top_n=5) assert len(ax.patches) <= 5 def test_draw_pairs_empty_db_no_error(tmp_db): pb = get_game_by_name("Powerball") ax = _ax() _draw_pairs(ax, pb["id"]) def test_draw_pairs_title_set(pb): ax = _ax() _draw_pairs(ax, pb["id"], top_n=10) assert "Pair" in ax.get_title() or "pair" in ax.get_title().lower() # ── _draw_odd_even ──────────────────────────────────────────────────────────── def test_draw_odd_even_creates_bars(pb): ax = _ax() _draw_odd_even(ax, pb["id"]) assert len(ax.patches) > 0 def test_draw_odd_even_empty_db_no_error(tmp_db): pb = get_game_by_name("Powerball") ax = _ax() _draw_odd_even(ax, pb["id"]) def test_draw_odd_even_title_set(pb): ax = _ax() _draw_odd_even(ax, pb["id"]) assert "Odd" in ax.get_title() or "Even" in ax.get_title() # ── _draw_sum_range ─────────────────────────────────────────────────────────── def test_draw_sum_range_creates_patches(pb): ax = _ax() _draw_sum_range(ax, pb["id"]) assert len(ax.patches) > 0 def test_draw_sum_range_empty_db_no_error(tmp_db): pb = get_game_by_name("Powerball") ax = _ax() _draw_sum_range(ax, pb["id"]) def test_draw_sum_range_title_set(pb): ax = _ax() _draw_sum_range(ax, pb["id"]) assert "Sum" in ax.get_title() # ── _draw_deltas ────────────────────────────────────────────────────────────── def test_draw_deltas_creates_bars(pb): ax = _ax() _draw_deltas(ax, pb["id"]) assert len(ax.patches) > 0 def test_draw_deltas_empty_db_no_error(tmp_db): pb = get_game_by_name("Powerball") ax = _ax() _draw_deltas(ax, pb["id"]) def test_draw_deltas_title_set(pb): ax = _ax() _draw_deltas(ax, pb["id"]) assert "Delta" in ax.get_title() def test_draw_deltas_x_axis_are_positive(pb): ax = _ax() _draw_deltas(ax, pb["id"]) # All delta values between consecutive sorted numbers are positive x_vals = [p.get_x() for p in ax.patches] assert all(x >= 0 for x in x_vals)