mirror of
https://github.com/mblanke/Lottery-Tracker.git
synced 2026-03-01 14:10:22 -05:00
Version 1.1
This commit is contained in:
21
tests/conftest.py
Normal file
21
tests/conftest.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""Shared pytest fixtures for the Lottery Tracker test suite."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from app import create_app
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def app():
|
||||
"""Create a test Flask app."""
|
||||
application = create_app()
|
||||
application.config["TESTING"] = True
|
||||
return application
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(app):
|
||||
"""Flask test client."""
|
||||
return app.test_client()
|
||||
215
tests/test_api.py
Normal file
215
tests/test_api.py
Normal file
@@ -0,0 +1,215 @@
|
||||
"""Integration tests for Flask API endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/health
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_health(client):
|
||||
resp = client.get("/api/health")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["status"] == "ok"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/jackpots
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@patch("app.get_all_jackpots")
|
||||
def test_jackpots(mock_get, client):
|
||||
mock_get.return_value = {
|
||||
"us": {"powerball": 500_000_000, "megaMillions": 300_000_000},
|
||||
"canadian": {"lottoMax": 70_000_000, "lotto649": 20_000_000},
|
||||
}
|
||||
resp = client.get("/api/jackpots")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["us"]["powerball"] == 500_000_000
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/calculate
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_calculate_us(client):
|
||||
resp = client.post(
|
||||
"/api/calculate",
|
||||
json={"jackpot": 100_000_000, "type": "us"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["country"] == "US"
|
||||
assert data["originalJackpot"] == 100_000_000
|
||||
|
||||
|
||||
def test_calculate_canadian(client):
|
||||
resp = client.post(
|
||||
"/api/calculate",
|
||||
json={"jackpot": 50_000_000, "type": "canadian"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["country"] == "Canada"
|
||||
|
||||
|
||||
def test_calculate_with_state(client):
|
||||
resp = client.post(
|
||||
"/api/calculate",
|
||||
json={"jackpot": 100_000_000, "type": "us", "state": "CA"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["stateTaxRate"] == 0.133 # California
|
||||
|
||||
|
||||
def test_calculate_missing_jackpot(client):
|
||||
resp = client.post("/api/calculate", json={"type": "us"})
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_calculate_bad_type(client):
|
||||
resp = client.post("/api/calculate", json={"jackpot": 100, "type": "mars"})
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_calculate_no_body(client):
|
||||
resp = client.post("/api/calculate")
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/states
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_states_list(client):
|
||||
resp = client.get("/api/states")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert len(data) == 51 # 50 states + DC
|
||||
codes = [s["code"] for s in data]
|
||||
assert "CA" in codes
|
||||
assert "TX" in codes
|
||||
|
||||
|
||||
def test_state_by_code(client):
|
||||
resp = client.get("/api/states/NY")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["name"] == "New York"
|
||||
assert data["rate"] == 0.109
|
||||
|
||||
|
||||
def test_state_not_found(client):
|
||||
resp = client.get("/api/states/ZZ")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/compare
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@patch("app.get_all_jackpots")
|
||||
def test_compare(mock_get, client):
|
||||
mock_get.return_value = {
|
||||
"us": {"powerball": 500_000_000, "megaMillions": 300_000_000},
|
||||
"canadian": {"lottoMax": 70_000_000, "lotto649": 20_000_000},
|
||||
}
|
||||
resp = client.get("/api/compare")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert len(data) == 4
|
||||
names = [d["name"] for d in data]
|
||||
assert "Powerball" in names
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/calculate/breakeven
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_breakeven(client):
|
||||
resp = client.post(
|
||||
"/api/calculate/breakeven",
|
||||
json={"lottery": "powerball"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["breakEvenJackpot"] > 0
|
||||
assert data["lottery"] == "Powerball"
|
||||
|
||||
|
||||
def test_breakeven_unknown_lottery(client):
|
||||
resp = client.post(
|
||||
"/api/calculate/breakeven",
|
||||
json={"lottery": "nosuchlottery"},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/calculate/annuity
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_annuity(client):
|
||||
resp = client.post(
|
||||
"/api/calculate/annuity",
|
||||
json={"jackpot": 500_000_000, "type": "us", "years": 30},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert len(data["schedule"]) == 30
|
||||
|
||||
|
||||
def test_annuity_canadian(client):
|
||||
resp = client.post(
|
||||
"/api/calculate/annuity",
|
||||
json={"jackpot": 100_000_000, "type": "canadian"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["country"] == "canadian"
|
||||
assert data["schedule"][0]["tax"] == 0.0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/calculate/group
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_group_play(client):
|
||||
resp = client.post(
|
||||
"/api/calculate/group",
|
||||
json={"jackpot": 100_000_000, "members": 4, "type": "us"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["members"] == 4
|
||||
assert len(data["memberResults"]) == 4
|
||||
|
||||
|
||||
def test_group_custom_shares(client):
|
||||
resp = client.post(
|
||||
"/api/calculate/group",
|
||||
json={"jackpot": 100_000_000, "members": 2, "shares": [0.7, 0.3], "type": "canadian"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data["memberResults"][0]["share"] == pytest.approx(0.7)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /api/odds
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_odds(client):
|
||||
resp = client.get("/api/odds")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert len(data) == 4
|
||||
names = [d["name"] for d in data]
|
||||
assert "Powerball" in names
|
||||
assert "Mega Millions" in names
|
||||
47
tests/test_config.py
Normal file
47
tests/test_config.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Tests for config.py."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
from config import LOTTERY_ODDS, STATE_TAX_RATES, load_config
|
||||
|
||||
|
||||
def test_all_states_present():
|
||||
assert len(STATE_TAX_RATES) == 51 # 50 states + DC
|
||||
|
||||
|
||||
def test_all_lottery_odds_present():
|
||||
assert "powerball" in LOTTERY_ODDS
|
||||
assert "megaMillions" in LOTTERY_ODDS
|
||||
assert "lottoMax" in LOTTERY_ODDS
|
||||
assert "lotto649" in LOTTERY_ODDS
|
||||
|
||||
|
||||
def test_load_config_defaults():
|
||||
cfg = load_config()
|
||||
assert cfg.debug is False
|
||||
assert cfg.port == 5000
|
||||
assert cfg.tax.lump_sum_rate == 0.52
|
||||
assert cfg.investment.cycles == 8
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"FLASK_DEBUG": "true", "FLASK_PORT": "8080"})
|
||||
def test_load_config_from_env():
|
||||
cfg = load_config()
|
||||
assert cfg.debug is True
|
||||
assert cfg.port == 8080
|
||||
|
||||
|
||||
@patch.dict(os.environ, {"LUMP_SUM_RATE": "0.60"})
|
||||
def test_tax_config_from_env():
|
||||
cfg = load_config()
|
||||
assert cfg.tax.lump_sum_rate == 0.60
|
||||
|
||||
|
||||
def test_no_tax_states():
|
||||
"""Florida, Texas, Nevada, etc. should have 0% tax."""
|
||||
no_tax = ["AK", "FL", "NV", "NH", "SD", "TN", "TX", "WA", "WY"]
|
||||
for code in no_tax:
|
||||
assert STATE_TAX_RATES[code]["rate"] == 0.0, f"{code} should have 0% tax"
|
||||
207
tests/test_lottery_calculator.py
Normal file
207
tests/test_lottery_calculator.py
Normal file
@@ -0,0 +1,207 @@
|
||||
"""Unit tests for lottery_calculator.py — pure calculation logic."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from lottery_calculator import (
|
||||
calculate_annuity,
|
||||
calculate_break_even,
|
||||
calculate_canadian_lottery,
|
||||
calculate_group_split,
|
||||
calculate_us_lottery,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# calculate_us_lottery
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCalculateUSLottery:
|
||||
"""Tests for the US lottery calculation."""
|
||||
|
||||
def test_basic_calculation(self):
|
||||
result = calculate_us_lottery(100_000_000)
|
||||
assert result["country"] == "US"
|
||||
assert result["originalJackpot"] == 100_000_000
|
||||
|
||||
def test_cash_sum_is_lump_sum_rate(self):
|
||||
result = calculate_us_lottery(100_000_000, lump_sum_rate=0.52)
|
||||
assert result["cashSum"] == pytest.approx(52_000_000)
|
||||
|
||||
def test_federal_tax_applied(self):
|
||||
result = calculate_us_lottery(100_000_000, lump_sum_rate=0.52, federal_tax_rate=0.37)
|
||||
assert result["federalTax"] == pytest.approx(52_000_000 * 0.37)
|
||||
|
||||
def test_state_tax_applied(self):
|
||||
result = calculate_us_lottery(100_000_000, state_tax_rate=0.10)
|
||||
assert result["stateTaxRate"] == 0.10
|
||||
|
||||
def test_net_amount_usd(self):
|
||||
result = calculate_us_lottery(
|
||||
100_000_000, lump_sum_rate=0.50, federal_tax_rate=0.30, state_tax_rate=0.05
|
||||
)
|
||||
expected = 50_000_000 * (1 - 0.30 - 0.05)
|
||||
assert result["netAmountUsd"] == pytest.approx(expected)
|
||||
|
||||
def test_cad_conversion(self):
|
||||
result = calculate_us_lottery(100_000_000, usd_cad_rate=1.40)
|
||||
assert result["netAmountCad"] == pytest.approx(result["netAmountUsd"] * 1.40)
|
||||
|
||||
def test_investment_split(self):
|
||||
result = calculate_us_lottery(100_000_000, invest_percentage=0.80)
|
||||
total = result["investmentPrincipal"] + result["funMoney"]
|
||||
assert total == pytest.approx(result["netAmountCad"])
|
||||
assert result["funMoney"] == pytest.approx(result["netAmountCad"] * 0.20)
|
||||
|
||||
def test_cycles_count(self):
|
||||
result = calculate_us_lottery(100_000_000, cycles=5)
|
||||
assert len(result["cycles"]) == 5
|
||||
|
||||
def test_principal_grows(self):
|
||||
result = calculate_us_lottery(100_000_000, cycles=4)
|
||||
for i in range(1, len(result["cycles"])):
|
||||
assert result["cycles"][i]["principalStart"] >= result["cycles"][i - 1]["principalStart"]
|
||||
|
||||
def test_final_principal_positive(self):
|
||||
result = calculate_us_lottery(500_000_000)
|
||||
assert result["finalPrincipal"] > 0
|
||||
|
||||
def test_daily_income_positive(self):
|
||||
result = calculate_us_lottery(500_000_000)
|
||||
assert result["netDailyIncome"] > 0
|
||||
|
||||
def test_zero_state_tax(self):
|
||||
"""Florida / Texas have 0% state tax."""
|
||||
result = calculate_us_lottery(100_000_000, state_tax_rate=0.0)
|
||||
assert result["stateTax"] == 0.0
|
||||
assert result["netAmountUsd"] > calculate_us_lottery(100_000_000, state_tax_rate=0.10)["netAmountUsd"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# calculate_canadian_lottery
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCalculateCanadianLottery:
|
||||
"""Tests for Canadian lottery calculation (tax-free winnings)."""
|
||||
|
||||
def test_basic_calculation(self):
|
||||
result = calculate_canadian_lottery(50_000_000)
|
||||
assert result["country"] == "Canada"
|
||||
assert result["originalJackpot"] == 50_000_000
|
||||
|
||||
def test_tax_free(self):
|
||||
result = calculate_canadian_lottery(50_000_000)
|
||||
assert result["netAmountCad"] == 50_000_000
|
||||
|
||||
def test_investment_split(self):
|
||||
result = calculate_canadian_lottery(50_000_000, invest_percentage=0.90)
|
||||
assert result["investmentPrincipal"] == pytest.approx(45_000_000)
|
||||
assert result["funMoney"] == pytest.approx(5_000_000)
|
||||
|
||||
def test_cycles_count(self):
|
||||
result = calculate_canadian_lottery(50_000_000, cycles=3)
|
||||
assert len(result["cycles"]) == 3
|
||||
|
||||
def test_daily_income_positive(self):
|
||||
result = calculate_canadian_lottery(50_000_000)
|
||||
assert result["netDailyIncome"] > 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# calculate_break_even
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCalculateBreakEven:
|
||||
"""Tests for the break-even expected-value calculator."""
|
||||
|
||||
def test_us_break_even(self):
|
||||
result = calculate_break_even(odds=292_201_338, ticket_cost=2.0, country="us")
|
||||
assert result["breakEvenJackpot"] > 0
|
||||
assert result["breakEvenJackpot"] > 292_201_338 # must be >> odds because of tax
|
||||
|
||||
def test_canadian_break_even(self):
|
||||
result = calculate_break_even(odds=13_983_816, ticket_cost=3.0, country="canadian")
|
||||
# Canadian take-home fraction = 1.0, so break-even = 3 * 13_983_816
|
||||
expected = 3.0 * 13_983_816
|
||||
assert result["breakEvenJackpot"] == pytest.approx(expected)
|
||||
|
||||
def test_ev_equals_ticket_cost(self):
|
||||
result = calculate_break_even(odds=100, ticket_cost=5.0, country="canadian")
|
||||
# EV at break even = probability * jackpot * 1.0 = 5.0
|
||||
assert result["expectedValueAtBreakEven"] == pytest.approx(5.0, rel=1e-6)
|
||||
|
||||
def test_higher_tax_needs_bigger_jackpot(self):
|
||||
r1 = calculate_break_even(odds=100, ticket_cost=2.0, country="us", state_tax_rate=0.0)
|
||||
r2 = calculate_break_even(odds=100, ticket_cost=2.0, country="us", state_tax_rate=0.10)
|
||||
assert r2["breakEvenJackpot"] > r1["breakEvenJackpot"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# calculate_annuity
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCalculateAnnuity:
|
||||
"""Tests for the annuity payout schedule."""
|
||||
|
||||
def test_us_annuity_schedule_length(self):
|
||||
result = calculate_annuity(500_000_000, country="us", years=30)
|
||||
assert len(result["schedule"]) == 30
|
||||
|
||||
def test_canadian_no_tax(self):
|
||||
result = calculate_annuity(100_000_000, country="canadian", years=10, annual_increase=0.0)
|
||||
for entry in result["schedule"]:
|
||||
assert entry["tax"] == 0.0
|
||||
assert entry["afterTax"] == entry["preTax"]
|
||||
|
||||
def test_total_pretax_approximates_jackpot(self):
|
||||
result = calculate_annuity(100_000_000, country="canadian", years=30, annual_increase=0.05)
|
||||
assert result["totalPreTax"] == pytest.approx(100_000_000, rel=1e-6)
|
||||
|
||||
def test_payments_increase_annually(self):
|
||||
result = calculate_annuity(100_000_000, years=5, annual_increase=0.05)
|
||||
for i in range(1, len(result["schedule"])):
|
||||
assert result["schedule"][i]["preTax"] > result["schedule"][i - 1]["preTax"]
|
||||
|
||||
def test_zero_increase(self):
|
||||
result = calculate_annuity(100_000_000, years=5, annual_increase=0.0)
|
||||
payments = [e["preTax"] for e in result["schedule"]]
|
||||
assert all(p == pytest.approx(payments[0]) for p in payments)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# calculate_group_split
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestCalculateGroupSplit:
|
||||
"""Tests for the group play calculator."""
|
||||
|
||||
def test_equal_split(self):
|
||||
result = calculate_group_split(100_000_000, members=4, country="canadian")
|
||||
assert len(result["memberResults"]) == 4
|
||||
for m in result["memberResults"]:
|
||||
assert m["jackpotShare"] == pytest.approx(25_000_000)
|
||||
|
||||
def test_custom_shares(self):
|
||||
shares = [0.5, 0.3, 0.2]
|
||||
result = calculate_group_split(100_000_000, members=3, shares=shares, country="canadian")
|
||||
assert result["memberResults"][0]["jackpotShare"] == pytest.approx(50_000_000)
|
||||
assert result["memberResults"][1]["jackpotShare"] == pytest.approx(30_000_000)
|
||||
assert result["memberResults"][2]["jackpotShare"] == pytest.approx(20_000_000)
|
||||
|
||||
def test_shares_normalize(self):
|
||||
"""Shares that don't sum to 1.0 should be normalised."""
|
||||
shares = [2, 2, 1] # sums to 5
|
||||
result = calculate_group_split(100_000_000, members=3, shares=shares, country="canadian")
|
||||
assert result["memberResults"][0]["share"] == pytest.approx(0.4)
|
||||
assert result["memberResults"][2]["share"] == pytest.approx(0.2)
|
||||
|
||||
def test_mismatched_shares_falls_back_to_equal(self):
|
||||
result = calculate_group_split(100_000_000, members=3, shares=[0.5, 0.5], country="canadian")
|
||||
for m in result["memberResults"]:
|
||||
assert m["share"] == pytest.approx(1 / 3)
|
||||
|
||||
def test_each_member_gets_calculation(self):
|
||||
result = calculate_group_split(100_000_000, members=2, country="us")
|
||||
for m in result["memberResults"]:
|
||||
assert "calculation" in m
|
||||
assert m["calculation"]["country"] == "US"
|
||||
111
tests/test_scrapers.py
Normal file
111
tests/test_scrapers.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""Tests for scrapers.py — uses mocks to avoid real HTTP calls."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from scrapers import (
|
||||
_parse_jackpot_from_lotto_net,
|
||||
clear_cache,
|
||||
get_all_jackpots,
|
||||
scrape_mega_millions,
|
||||
scrape_powerball,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# HTML parser unit tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
POWERBALL_HTML = """
|
||||
<html><body>
|
||||
<h2>Powerball</h2>
|
||||
<div>
|
||||
Next Jackpot
|
||||
$350 Million
|
||||
</div>
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
BILLION_HTML = """
|
||||
<html><body>
|
||||
<div>
|
||||
Next Jackpot
|
||||
$1.5 Billion
|
||||
</div>
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
|
||||
def test_parse_jackpot_millions():
|
||||
result = _parse_jackpot_from_lotto_net(POWERBALL_HTML)
|
||||
assert result == 350_000_000
|
||||
|
||||
|
||||
def test_parse_jackpot_billions():
|
||||
result = _parse_jackpot_from_lotto_net(BILLION_HTML)
|
||||
assert result == 1_500_000_000
|
||||
|
||||
|
||||
def test_parse_jackpot_no_match():
|
||||
result = _parse_jackpot_from_lotto_net("<html><body>No jackpot here</body></html>")
|
||||
assert result is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scraper integration tests (mocked HTTP)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@patch("scrapers.requests.get")
|
||||
def test_scrape_powerball_success(mock_get):
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.text = POWERBALL_HTML
|
||||
mock_resp.raise_for_status = MagicMock()
|
||||
mock_get.return_value = mock_resp
|
||||
|
||||
result = scrape_powerball()
|
||||
assert result == 350_000_000
|
||||
|
||||
|
||||
@patch("scrapers.requests.get")
|
||||
def test_scrape_powerball_failure(mock_get):
|
||||
mock_get.side_effect = Exception("Network error")
|
||||
result = scrape_powerball()
|
||||
assert result is None
|
||||
|
||||
|
||||
@patch("scrapers.requests.get")
|
||||
def test_scrape_mega_millions_success(mock_get):
|
||||
html = POWERBALL_HTML.replace("Powerball", "Mega Millions")
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.text = html
|
||||
mock_resp.raise_for_status = MagicMock()
|
||||
mock_get.return_value = mock_resp
|
||||
|
||||
result = scrape_mega_millions()
|
||||
assert result == 350_000_000
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cache tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@patch("scrapers.scrape_powerball", return_value=100_000_000)
|
||||
@patch("scrapers.scrape_mega_millions", return_value=200_000_000)
|
||||
@patch("scrapers.scrape_canadian_lotteries", return_value={"lottoMax": 50_000_000, "lotto649": 10_000_000})
|
||||
def test_get_all_jackpots_caches(mock_ca, mock_mm, mock_pb):
|
||||
clear_cache()
|
||||
r1 = get_all_jackpots()
|
||||
r2 = get_all_jackpots()
|
||||
# Second call should use cache — scrapers called only once
|
||||
assert mock_pb.call_count == 1
|
||||
assert r1 == r2
|
||||
|
||||
|
||||
@patch("scrapers.scrape_powerball", return_value=100_000_000)
|
||||
@patch("scrapers.scrape_mega_millions", return_value=200_000_000)
|
||||
@patch("scrapers.scrape_canadian_lotteries", return_value={"lottoMax": 50_000_000, "lotto649": 10_000_000})
|
||||
def test_get_all_jackpots_force_refresh(mock_ca, mock_mm, mock_pb):
|
||||
clear_cache()
|
||||
get_all_jackpots()
|
||||
get_all_jackpots(force_refresh=True)
|
||||
assert mock_pb.call_count == 2
|
||||
Reference in New Issue
Block a user