05/23 Phase 10
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user