"""Tests for calendar date helper functions.""" from __future__ import annotations from datetime import date from unittest.mock import patch, MagicMock from shared.utils.calendar_helpers import add_months, build_calendar_weeks # --------------------------------------------------------------------------- # add_months # --------------------------------------------------------------------------- class TestAddMonths: def test_same_year(self): assert add_months(2025, 3, 2) == (2025, 5) def test_next_year(self): assert add_months(2025, 11, 2) == (2026, 1) def test_subtract(self): assert add_months(2025, 3, -2) == (2025, 1) def test_subtract_prev_year(self): assert add_months(2025, 1, -1) == (2024, 12) def test_add_twelve(self): assert add_months(2025, 6, 12) == (2026, 6) def test_subtract_twelve(self): assert add_months(2025, 6, -12) == (2024, 6) def test_large_delta(self): assert add_months(2025, 1, 25) == (2027, 2) def test_zero_delta(self): assert add_months(2025, 7, 0) == (2025, 7) def test_december_to_january(self): assert add_months(2025, 12, 1) == (2026, 1) def test_january_to_december(self): assert add_months(2025, 1, -1) == (2024, 12) def test_subtract_large(self): assert add_months(2025, 3, -15) == (2023, 12) def test_month_boundaries(self): # Every month +1 for m in range(1, 12): y, nm = add_months(2025, m, 1) assert nm == m + 1 assert y == 2025 y, nm = add_months(2025, 12, 1) assert nm == 1 assert y == 2026 # --------------------------------------------------------------------------- # build_calendar_weeks # --------------------------------------------------------------------------- class TestBuildCalendarWeeks: def test_returns_list_of_weeks(self): weeks = build_calendar_weeks(2025, 6) assert isinstance(weeks, list) assert len(weeks) >= 4 # at least 4 weeks in any month assert len(weeks) <= 6 # at most 6 weeks def test_each_week_has_7_days(self): weeks = build_calendar_weeks(2025, 6) for week in weeks: assert len(week) == 7 def test_day_dict_structure(self): weeks = build_calendar_weeks(2025, 6) day = weeks[0][0] assert "date" in day assert "in_month" in day assert "is_today" in day assert isinstance(day["date"], date) assert isinstance(day["in_month"], bool) assert isinstance(day["is_today"], bool) def test_in_month_flag(self): weeks = build_calendar_weeks(2025, 6) june_days = [d for w in weeks for d in w if d["in_month"]] assert len(june_days) == 30 # June has 30 days def test_february_leap_year(self): weeks = build_calendar_weeks(2024, 2) feb_days = [d for w in weeks for d in w if d["in_month"]] assert len(feb_days) == 29 def test_february_non_leap_year(self): weeks = build_calendar_weeks(2025, 2) feb_days = [d for w in weeks for d in w if d["in_month"]] assert len(feb_days) == 28 def test_starts_on_monday(self): """Calendar should start on Monday (firstweekday=0).""" weeks = build_calendar_weeks(2025, 6) first_day = weeks[0][0]["date"] assert first_day.weekday() == 0 # Monday def test_is_today_flag(self): """The today flag should be True for exactly one day (or zero if not in month range).""" # Use a fixed known date - mock datetime.now from datetime import datetime, timezone fixed_now = datetime(2025, 6, 15, tzinfo=timezone.utc) with patch("shared.utils.calendar_helpers.datetime") as mock_dt: mock_dt.now.return_value = fixed_now mock_dt.side_effect = lambda *a, **kw: datetime(*a, **kw) weeks = build_calendar_weeks(2025, 6) today_days = [d for w in weeks for d in w if d["is_today"]] assert len(today_days) == 1 assert today_days[0]["date"] == date(2025, 6, 15)