from datetime import date, timedelta from app import db as d from app.config import get_settings from app.services import compute_renewal_due def _conn(): settings = get_settings() settings.data_dir.mkdir(parents=True, exist_ok=True) d.init_schema(settings.resolved_db_path) return d.connect(settings.resolved_db_path) def _bike(conn, name="B"): return d.get_bike(conn, d.create_bike(conn, name, None)) def _log(conn, bike_id, type, day): d.create_event(conn, bike_id, type, day, None, None, None) def test_no_data_when_no_renewal_event(): conn = _conn() b = _bike(conn) _log(conn, b.id, "mot", date(2024, 6, 1)) r = compute_renewal_due(conn, b, "insurance") assert r.status == "no-data" assert r.due_date is None assert r.days_until is None conn.close() def test_due_is_twelve_months_after_renewal(): conn = _conn() b = _bike(conn) _log(conn, b.id, "mot", date(2025, 6, 1)) r = compute_renewal_due(conn, b, "mot", today=date(2025, 6, 1)) assert r.renewal_date == date(2025, 6, 1) assert r.due_date == date(2026, 6, 1) assert r.status == "ok" assert r.days_until > get_settings().renewal_due_soon_days conn.close() def test_due_soon_within_lead_time(): # Due 30 days after today; renewal was 12 months before that. conn = _conn() b = _bike(conn) today = date(2026, 1, 15) _log(conn, b.id, "insurance", date(2025, 2, 14)) # due 2026-02-14 r = compute_renewal_due(conn, b, "insurance", today=today) assert r.status == "due-soon" assert 0 <= r.days_until <= get_settings().renewal_due_soon_days conn.close() def test_exactly_at_lead_time_is_due_soon(): conn = _conn() b = _bike(conn) lead = get_settings().renewal_due_soon_days due = date(2026, 4, 1) _log(conn, b.id, "mot", date(2025, 4, 1)) # due 2026-04-01 r = compute_renewal_due(conn, b, "mot", today=due - timedelta(days=lead)) assert r.days_until == lead assert r.status == "due-soon" conn.close() def test_just_outside_lead_time_is_ok(): conn = _conn() b = _bike(conn) lead = get_settings().renewal_due_soon_days due = date(2026, 4, 1) _log(conn, b.id, "mot", date(2025, 4, 1)) # due 2026-04-01 r = compute_renewal_due(conn, b, "mot", today=due - timedelta(days=lead + 1)) assert r.days_until == lead + 1 assert r.status == "ok" conn.close() def test_overdue_when_renewal_is_thirteen_months_old(): conn = _conn() b = _bike(conn) _log(conn, b.id, "mot", date.today() - timedelta(days=365 + 31)) r = compute_renewal_due(conn, b, "mot") assert r.status == "overdue" assert r.days_until < 0 conn.close() def test_renewal_badge_shown_on_bike_page(client): client.login() bike_id = client.create_bike("Roadie") result = client.create_event(bike_id, type="mot", event_date=date.today().isoformat()) assert result["status"] == 200 page = client.get(f"/bikes/{bike_id}").text assert "MOT" in page assert "Insurance" in page