288 lines
9.8 KiB
Python
288 lines
9.8 KiB
Python
"""
|
|
tests/test_exporter.py
|
|
-----------------------
|
|
Tests for core/exporter.py — draw history, predictions, frequency analysis exports.
|
|
All tests write to tmp paths; no file-dialog interaction required.
|
|
"""
|
|
|
|
import csv
|
|
import os
|
|
|
|
import openpyxl
|
|
import pytest
|
|
|
|
from db.models import get_game_by_name, insert_draw, insert_prediction
|
|
from core.exporter import (
|
|
export_draws_excel,
|
|
export_draws_csv,
|
|
export_predictions_excel,
|
|
export_predictions_csv,
|
|
export_frequency_excel,
|
|
create_icon_png,
|
|
ensure_exports_dir,
|
|
default_path,
|
|
EXPORTS_DIR,
|
|
)
|
|
|
|
|
|
# ── Fixtures ──────────────────────────────────────────────────────────────────
|
|
|
|
@pytest.fixture()
|
|
def pb(tmp_db):
|
|
return get_game_by_name("Powerball")
|
|
|
|
|
|
@pytest.fixture()
|
|
def mm(tmp_db):
|
|
return get_game_by_name("Mega Millions")
|
|
|
|
|
|
@pytest.fixture()
|
|
def populated_db(pb, mm):
|
|
insert_draw(pb["id"], "2024-01-01", [1, 13, 36, 61, 69], bonus=7, multiplier="2x")
|
|
insert_draw(pb["id"], "2024-01-03", [5, 10, 20, 30, 40], bonus=15, multiplier="3x")
|
|
insert_draw(mm["id"], "2024-01-02", [2, 14, 37, 62, 70], bonus=5)
|
|
insert_prediction(pb["id"], "Hot Numbers", [1, 13, 36, 61, 69], bonus=7)
|
|
insert_prediction(mm["id"], "Due Numbers", [2, 14, 37, 62, 70], bonus=5)
|
|
return pb, mm
|
|
|
|
|
|
# ── Draw Excel ────────────────────────────────────────────────────────────────
|
|
|
|
def test_draws_excel_creates_file(tmp_path, populated_db):
|
|
fp = str(tmp_path / "draws.xlsx")
|
|
result = export_draws_excel(fp)
|
|
assert result == fp
|
|
assert os.path.exists(fp)
|
|
|
|
|
|
def test_draws_excel_header(tmp_path, populated_db):
|
|
fp = str(tmp_path / "draws.xlsx")
|
|
export_draws_excel(fp)
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
headers = [ws.cell(1, c).value for c in range(1, 7)]
|
|
assert headers == ["Game", "Date", "Numbers", "Bonus", "Multiplier", "Source"]
|
|
|
|
|
|
def test_draws_excel_row_count(tmp_path, populated_db):
|
|
pb, mm = populated_db
|
|
fp = str(tmp_path / "draws.xlsx")
|
|
export_draws_excel(fp)
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
# 3 draws inserted (header is row 1)
|
|
assert ws.max_row == 4
|
|
|
|
|
|
def test_draws_excel_game_filter(tmp_path, populated_db):
|
|
pb, mm = populated_db
|
|
fp = str(tmp_path / "pb_only.xlsx")
|
|
export_draws_excel(fp, game_id=pb["id"])
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
games_in_file = {ws.cell(r, 1).value for r in range(2, ws.max_row + 1)}
|
|
assert games_in_file == {"Powerball"}
|
|
|
|
|
|
def test_draws_excel_date_filter(tmp_path, populated_db):
|
|
fp = str(tmp_path / "filtered.xlsx")
|
|
export_draws_excel(fp, date_from="2024-01-03", date_to="2024-01-03")
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
# Only the 2024-01-03 draw
|
|
assert ws.max_row == 2
|
|
|
|
|
|
def test_draws_excel_empty_db(tmp_path, tmp_db):
|
|
fp = str(tmp_path / "empty.xlsx")
|
|
export_draws_excel(fp)
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
assert ws.max_row == 1 # header only
|
|
|
|
|
|
# ── Draw CSV ──────────────────────────────────────────────────────────────────
|
|
|
|
def test_draws_csv_creates_file(tmp_path, populated_db):
|
|
fp = str(tmp_path / "draws.csv")
|
|
result = export_draws_csv(fp)
|
|
assert result == fp
|
|
assert os.path.exists(fp)
|
|
|
|
|
|
def test_draws_csv_header(tmp_path, populated_db):
|
|
fp = str(tmp_path / "draws.csv")
|
|
export_draws_csv(fp)
|
|
with open(fp, newline="", encoding="utf-8") as f:
|
|
reader = csv.reader(f)
|
|
header = next(reader)
|
|
assert header == ["Game", "Date", "Numbers", "Bonus", "Multiplier", "Source"]
|
|
|
|
|
|
def test_draws_csv_row_count(tmp_path, populated_db):
|
|
fp = str(tmp_path / "draws.csv")
|
|
export_draws_csv(fp)
|
|
with open(fp, newline="", encoding="utf-8") as f:
|
|
rows = list(csv.reader(f))
|
|
assert len(rows) == 4 # 1 header + 3 draws
|
|
|
|
|
|
def test_draws_csv_game_filter(tmp_path, populated_db):
|
|
pb, _ = populated_db
|
|
fp = str(tmp_path / "pb.csv")
|
|
export_draws_csv(fp, game_id=pb["id"])
|
|
with open(fp, newline="", encoding="utf-8") as f:
|
|
rows = list(csv.reader(f))
|
|
data_rows = rows[1:]
|
|
assert all(r[0] == "Powerball" for r in data_rows)
|
|
|
|
|
|
# ── Predictions Excel ─────────────────────────────────────────────────────────
|
|
|
|
def test_predictions_excel_creates_file(tmp_path, populated_db):
|
|
fp = str(tmp_path / "preds.xlsx")
|
|
result = export_predictions_excel(fp)
|
|
assert result == fp
|
|
assert os.path.exists(fp)
|
|
|
|
|
|
def test_predictions_excel_header(tmp_path, populated_db):
|
|
fp = str(tmp_path / "preds.xlsx")
|
|
export_predictions_excel(fp)
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
headers = [ws.cell(1, c).value for c in range(1, 7)]
|
|
assert headers == ["ID", "Game", "Strategy", "Numbers", "Bonus", "Created"]
|
|
|
|
|
|
def test_predictions_excel_row_count(tmp_path, populated_db):
|
|
fp = str(tmp_path / "preds.xlsx")
|
|
export_predictions_excel(fp)
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
assert ws.max_row == 3 # 1 header + 2 predictions
|
|
|
|
|
|
def test_predictions_excel_game_filter(tmp_path, populated_db):
|
|
pb, _ = populated_db
|
|
fp = str(tmp_path / "pb_preds.xlsx")
|
|
export_predictions_excel(fp, game_id=pb["id"])
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
games = {ws.cell(r, 2).value for r in range(2, ws.max_row + 1)}
|
|
assert games == {"Powerball"}
|
|
|
|
|
|
# ── Predictions CSV ───────────────────────────────────────────────────────────
|
|
|
|
def test_predictions_csv_creates_file(tmp_path, populated_db):
|
|
fp = str(tmp_path / "preds.csv")
|
|
result = export_predictions_csv(fp)
|
|
assert result == fp
|
|
assert os.path.exists(fp)
|
|
|
|
|
|
def test_predictions_csv_header(tmp_path, populated_db):
|
|
fp = str(tmp_path / "preds.csv")
|
|
export_predictions_csv(fp)
|
|
with open(fp, newline="", encoding="utf-8") as f:
|
|
header = next(csv.reader(f))
|
|
assert header == ["ID", "Game", "Strategy", "Numbers", "Bonus", "Created"]
|
|
|
|
|
|
def test_predictions_csv_row_count(tmp_path, populated_db):
|
|
fp = str(tmp_path / "preds.csv")
|
|
export_predictions_csv(fp)
|
|
with open(fp, newline="", encoding="utf-8") as f:
|
|
rows = list(csv.reader(f))
|
|
assert len(rows) == 3 # 1 header + 2 predictions
|
|
|
|
|
|
# ── Frequency Excel ───────────────────────────────────────────────────────────
|
|
|
|
def test_frequency_excel_creates_file(tmp_path, populated_db):
|
|
pb, _ = populated_db
|
|
fp = str(tmp_path / "freq.xlsx")
|
|
result = export_frequency_excel(fp, pb["id"])
|
|
assert result == fp
|
|
assert os.path.exists(fp)
|
|
|
|
|
|
def test_frequency_excel_header(tmp_path, populated_db):
|
|
pb, _ = populated_db
|
|
fp = str(tmp_path / "freq.xlsx")
|
|
export_frequency_excel(fp, pb["id"])
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
headers = [ws.cell(1, c).value for c in range(1, 4)]
|
|
assert headers == ["Number", "Frequency", "Gap (draws since last seen)"]
|
|
|
|
|
|
def test_frequency_excel_covers_full_pool(tmp_path, populated_db):
|
|
pb, _ = populated_db
|
|
fp = str(tmp_path / "freq.xlsx")
|
|
export_frequency_excel(fp, pb["id"])
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
# Powerball main_max=69: 69 data rows + 1 header = 70
|
|
assert ws.max_row == 70
|
|
|
|
|
|
def test_frequency_excel_number_column(tmp_path, populated_db):
|
|
pb, _ = populated_db
|
|
fp = str(tmp_path / "freq.xlsx")
|
|
export_frequency_excel(fp, pb["id"])
|
|
wb = openpyxl.load_workbook(fp)
|
|
ws = wb.active
|
|
numbers = [ws.cell(r, 1).value for r in range(2, ws.max_row + 1)]
|
|
assert numbers == list(range(1, 70))
|
|
|
|
|
|
# ── Icon PNG ──────────────────────────────────────────────────────────────────
|
|
|
|
def test_create_icon_png_creates_file(tmp_path):
|
|
path = str(tmp_path / "icon.png")
|
|
create_icon_png(path)
|
|
assert os.path.exists(path)
|
|
|
|
|
|
def test_create_icon_png_is_valid_png(tmp_path):
|
|
path = str(tmp_path / "icon.png")
|
|
create_icon_png(path)
|
|
with open(path, "rb") as f:
|
|
sig = f.read(8)
|
|
assert sig == b"\x89PNG\r\n\x1a\n"
|
|
|
|
|
|
def test_create_icon_png_default_size(tmp_path):
|
|
path = str(tmp_path / "icon.png")
|
|
create_icon_png(path)
|
|
size = os.path.getsize(path)
|
|
assert size > 100 # a valid non-empty PNG
|
|
|
|
|
|
def test_create_icon_png_custom_size(tmp_path):
|
|
path = str(tmp_path / "icon32.png")
|
|
create_icon_png(path, size=32)
|
|
assert os.path.exists(path)
|
|
|
|
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
def test_ensure_exports_dir_creates_directory(tmp_path, monkeypatch):
|
|
target = str(tmp_path / "new_exports")
|
|
monkeypatch.setattr("core.exporter.EXPORTS_DIR", target)
|
|
import core.exporter as exp
|
|
exp.EXPORTS_DIR = target
|
|
exp.ensure_exports_dir()
|
|
assert os.path.isdir(target)
|
|
|
|
|
|
def test_default_path_returns_string_with_stem(tmp_path, monkeypatch):
|
|
import core.exporter as exp
|
|
monkeypatch.setattr(exp, "EXPORTS_DIR", str(tmp_path))
|
|
path = exp.default_path("draws", "xlsx")
|
|
assert "draws" in os.path.basename(path)
|
|
assert path.endswith(".xlsx")
|