248 lines
8.2 KiB
Python
248 lines
8.2 KiB
Python
"""
|
|
tests/test_analyzer.py
|
|
-----------------------
|
|
Unit tests for core/analyzer.py.
|
|
Uses three deterministic draws so every assertion is hand-verifiable.
|
|
|
|
Draw data (all Powerball, game_id from tmp_db):
|
|
Draw 1 (oldest) 2024-01-01: [ 1, 13, 36, 61, 69]
|
|
Draw 2 2024-01-03: [ 1, 2, 13, 45, 69]
|
|
Draw 3 (newest) 2024-01-05: [ 2, 13, 22, 36, 55]
|
|
"""
|
|
|
|
import pytest
|
|
from db.models import get_game_by_name, insert_draw
|
|
from core.analyzer import (
|
|
delta_analysis,
|
|
frequency_analysis,
|
|
gap_analysis,
|
|
odd_even_ratio,
|
|
pair_analysis,
|
|
positional_frequency,
|
|
sum_range_analysis,
|
|
)
|
|
|
|
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]),
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def pb_game(tmp_db):
|
|
"""Return Powerball game row and insert the three test draws."""
|
|
game = get_game_by_name("Powerball")
|
|
for date, nums in DRAWS:
|
|
insert_draw(game["id"], date, nums, bonus=7, source="test")
|
|
return game
|
|
|
|
|
|
# ── frequency_analysis ────────────────────────────────────────────────────────
|
|
|
|
def test_frequency_counts_all_draws(pb_game):
|
|
data = frequency_analysis(pb_game["id"])
|
|
assert data[13] == 3 # in all 3 draws
|
|
assert data[1] == 2
|
|
assert data[69] == 2
|
|
assert data[36] == 2
|
|
assert data[2] == 2
|
|
assert data[61] == 1
|
|
|
|
|
|
def test_frequency_sorted_high_to_low(pb_game):
|
|
data = frequency_analysis(pb_game["id"])
|
|
counts = list(data.values())
|
|
assert counts == sorted(counts, reverse=True)
|
|
|
|
|
|
def test_frequency_last_n_restricts_draws(pb_game):
|
|
# last_n=1 → only draw 3: [2, 13, 22, 36, 55]
|
|
data = frequency_analysis(pb_game["id"], last_n=1)
|
|
for n in (2, 13, 22, 36, 55):
|
|
assert data[n] == 1
|
|
assert 1 not in data
|
|
assert 69 not in data
|
|
|
|
|
|
def test_frequency_last_n_two_draws(pb_game):
|
|
# last_n=2 → draws 2 and 3: [1,2,13,45,69] + [2,13,22,36,55]
|
|
data = frequency_analysis(pb_game["id"], last_n=2)
|
|
assert data[2] == 2
|
|
assert data[13] == 2
|
|
assert data[1] == 1
|
|
assert 61 not in data
|
|
|
|
|
|
def test_frequency_empty_db_returns_empty(tmp_db):
|
|
game = get_game_by_name("Powerball")
|
|
assert frequency_analysis(game["id"]) == {}
|
|
|
|
|
|
# ── gap_analysis ──────────────────────────────────────────────────────────────
|
|
|
|
def test_gap_zero_for_numbers_in_last_draw(pb_game):
|
|
gaps = gap_analysis(pb_game["id"])
|
|
# Draw 3 = [2, 13, 22, 36, 55] — all have gap 0
|
|
for n in (2, 13, 22, 36, 55):
|
|
assert gaps[n] == 0, f"Expected gap 0 for {n}, got {gaps[n]}"
|
|
|
|
|
|
def test_gap_one_for_draw_before_last(pb_game):
|
|
gaps = gap_analysis(pb_game["id"])
|
|
# 1 and 69 were last in draw 2 (one before latest)
|
|
assert gaps[1] == 1
|
|
assert gaps[69] == 1
|
|
|
|
|
|
def test_gap_two_for_number_two_draws_back(pb_game):
|
|
gaps = gap_analysis(pb_game["id"])
|
|
# 61 only in draw 1 (two draws before latest)
|
|
assert gaps[61] == 2
|
|
|
|
|
|
def test_gap_total_for_never_appeared(pb_game):
|
|
gaps = gap_analysis(pb_game["id"])
|
|
# Number 3 never appeared → gap = total draws = 3
|
|
assert gaps[3] == 3
|
|
|
|
|
|
def test_gap_covers_full_pool(pb_game):
|
|
gaps = gap_analysis(pb_game["id"])
|
|
game = get_game_by_name("Powerball")
|
|
assert len(gaps) == game["main_max"]
|
|
|
|
|
|
def test_gap_empty_db_returns_empty(tmp_db):
|
|
game = get_game_by_name("Powerball")
|
|
assert gap_analysis(game["id"]) == {}
|
|
|
|
|
|
# ── positional_frequency ──────────────────────────────────────────────────────
|
|
|
|
def test_positional_has_correct_positions(pb_game):
|
|
pf = positional_frequency(pb_game["id"])
|
|
game = get_game_by_name("Powerball")
|
|
assert set(pf.keys()) == set(range(1, game["main_count"] + 1))
|
|
|
|
|
|
def test_positional_counts_correct_values(pb_game):
|
|
pf = positional_frequency(pb_game["id"])
|
|
# Position 1: draw1=1, draw2=1, draw3=2 → {1: 2, 2: 1}
|
|
assert pf[1][1] == 2
|
|
assert pf[1][2] == 1
|
|
# Position 2: draw1=13, draw2=2, draw3=13 → {13: 2, 2: 1}
|
|
assert pf[2][13] == 2
|
|
assert pf[2][2] == 1
|
|
# Position 5: draw1=69, draw2=69, draw3=55 → {69: 2, 55: 1}
|
|
assert pf[5][69] == 2
|
|
assert pf[5][55] == 1
|
|
|
|
|
|
def test_positional_empty_db_returns_empty_counters(tmp_db):
|
|
game = get_game_by_name("Powerball")
|
|
pf = positional_frequency(game["id"])
|
|
assert all(len(v) == 0 for v in pf.values())
|
|
|
|
|
|
# ── pair_analysis ─────────────────────────────────────────────────────────────
|
|
|
|
def test_pair_counts_known_pairs(pb_game):
|
|
pairs = pair_analysis(pb_game["id"])
|
|
# (1,13) appears in draw1 and draw2
|
|
assert pairs[(1, 13)] == 2
|
|
# (1,69) appears in draw1 and draw2
|
|
assert pairs[(1, 69)] == 2
|
|
# (13,69) appears in draw1 and draw2
|
|
assert pairs[(13, 69)] == 2
|
|
# (2,13) appears in draw2 and draw3
|
|
assert pairs[(2, 13)] == 2
|
|
# (13,36) appears in draw1 and draw3
|
|
assert pairs[(13, 36)] == 2
|
|
|
|
|
|
def test_pair_keys_are_sorted_tuples(pb_game):
|
|
pairs = pair_analysis(pb_game["id"])
|
|
for n1, n2 in pairs.keys():
|
|
assert n1 < n2
|
|
|
|
|
|
def test_pair_respects_top_n(pb_game):
|
|
pairs = pair_analysis(pb_game["id"], top_n=3)
|
|
assert len(pairs) <= 3
|
|
|
|
|
|
def test_pair_empty_db_returns_empty(tmp_db):
|
|
game = get_game_by_name("Powerball")
|
|
assert pair_analysis(game["id"]) == {}
|
|
|
|
|
|
# ── odd_even_ratio ────────────────────────────────────────────────────────────
|
|
|
|
def test_odd_even_counts_correct(pb_game):
|
|
ratios = odd_even_ratio(pb_game["id"])
|
|
assert len(ratios) == 3
|
|
# Draw 1 [1,13,36,61,69]: odd=4, even=1
|
|
r1 = next(r for r in ratios if r["date"] == "2024-01-01")
|
|
assert r1["odd"] == 4
|
|
assert r1["even"] == 1
|
|
# Draw 3 [2,13,22,36,55]: odd=2, even=3
|
|
r3 = next(r for r in ratios if r["date"] == "2024-01-05")
|
|
assert r3["odd"] == 2
|
|
assert r3["even"] == 3
|
|
|
|
|
|
def test_odd_even_sums_to_main_count(pb_game):
|
|
game = get_game_by_name("Powerball")
|
|
ratios = odd_even_ratio(pb_game["id"])
|
|
for r in ratios:
|
|
assert r["odd"] + r["even"] == game["main_count"]
|
|
|
|
|
|
def test_odd_even_empty_db(tmp_db):
|
|
game = get_game_by_name("Powerball")
|
|
assert odd_even_ratio(game["id"]) == []
|
|
|
|
|
|
# ── sum_range_analysis ────────────────────────────────────────────────────────
|
|
|
|
def test_sum_values_correct(pb_game):
|
|
sums = sum_range_analysis(pb_game["id"])
|
|
assert len(sums) == 3
|
|
s1 = next(s for s in sums if s["date"] == "2024-01-01")
|
|
assert s1["sum"] == 1 + 13 + 36 + 61 + 69 # 180
|
|
s2 = next(s for s in sums if s["date"] == "2024-01-03")
|
|
assert s2["sum"] == 1 + 2 + 13 + 45 + 69 # 130
|
|
s3 = next(s for s in sums if s["date"] == "2024-01-05")
|
|
assert s3["sum"] == 2 + 13 + 22 + 36 + 55 # 128
|
|
|
|
|
|
def test_sum_empty_db(tmp_db):
|
|
game = get_game_by_name("Powerball")
|
|
assert sum_range_analysis(game["id"]) == []
|
|
|
|
|
|
# ── delta_analysis ────────────────────────────────────────────────────────────
|
|
|
|
def test_delta_values_correct(pb_game):
|
|
deltas = delta_analysis(pb_game["id"])
|
|
assert len(deltas) == 3
|
|
d1 = next(d for d in deltas if d["date"] == "2024-01-01")
|
|
# sorted [1,13,36,61,69] → diffs [12, 23, 25, 8]
|
|
assert d1["deltas"] == [12, 23, 25, 8]
|
|
d3 = next(d for d in deltas if d["date"] == "2024-01-05")
|
|
# sorted [2,13,22,36,55] → diffs [11, 9, 14, 19]
|
|
assert d3["deltas"] == [11, 9, 14, 19]
|
|
|
|
|
|
def test_delta_length_matches_main_count_minus_one(pb_game):
|
|
game = get_game_by_name("Powerball")
|
|
deltas = delta_analysis(pb_game["id"])
|
|
for d in deltas:
|
|
assert len(d["deltas"]) == game["main_count"] - 1
|
|
|
|
|
|
def test_delta_empty_db(tmp_db):
|
|
game = get_game_by_name("Powerball")
|
|
assert delta_analysis(game["id"]) == []
|