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