Files
F-r-ldraledigplannerare/backend/tests/unit/test_dates.py
T
2026-06-01 16:48:21 +02:00

206 lines
7.5 KiB
Python

"""Tests for date utility functions."""
import datetime
import pytest
from app.utils.dates import add_months, age_in_months, fourth_birthday
class TestAddMonths:
"""Test add_months function."""
def test_add_positive_months(self) -> None:
"""Adding positive months increments the month correctly."""
date = datetime.date(2026, 1, 15)
result = add_months(date, 1)
assert result == datetime.date(2026, 2, 15)
def test_add_multiple_months(self) -> None:
"""Adding multiple months increments year when needed."""
date = datetime.date(2026, 11, 15)
result = add_months(date, 2)
assert result == datetime.date(2027, 1, 15)
def test_add_12_months(self) -> None:
"""Adding exactly 12 months increments year only."""
date = datetime.date(2026, 3, 15)
result = add_months(date, 12)
assert result == datetime.date(2027, 3, 15)
def test_add_many_months(self) -> None:
"""Adding many months works correctly."""
date = datetime.date(2026, 1, 15)
result = add_months(date, 48) # 4 years
assert result == datetime.date(2030, 1, 15)
def test_subtract_months(self) -> None:
"""Subtracting months (negative n) works correctly."""
date = datetime.date(2026, 3, 15)
result = add_months(date, -1)
assert result == datetime.date(2026, 2, 15)
def test_subtract_year(self) -> None:
"""Subtracting 12 months decrements year."""
date = datetime.date(2026, 3, 15)
result = add_months(date, -12)
assert result == datetime.date(2025, 3, 15)
def test_subtract_across_year_boundary(self) -> None:
"""Subtracting months crossing year boundary works."""
date = datetime.date(2026, 1, 15)
result = add_months(date, -2)
assert result == datetime.date(2025, 11, 15)
def test_month_end_handling_jan_to_feb_leap_year(self) -> None:
"""Jan 31 + 1 month = Feb 29 in leap year."""
date = datetime.date(2024, 1, 31) # 2024 is a leap year
result = add_months(date, 1)
assert result == datetime.date(2024, 2, 29)
def test_month_end_handling_jan_to_feb_non_leap_year(self) -> None:
"""Jan 31 + 1 month = Feb 28 in non-leap year."""
date = datetime.date(2025, 1, 31) # 2025 is not a leap year
result = add_months(date, 1)
assert result == datetime.date(2025, 2, 28)
def test_month_end_handling_mar_to_feb(self) -> None:
"""Mar 31 - 1 month = Feb 28/29."""
date = datetime.date(2025, 3, 31)
result = add_months(date, -1)
assert result == datetime.date(2025, 2, 28)
def test_month_end_handling_may_to_apr(self) -> None:
"""May 31 - 1 month = Apr 30."""
date = datetime.date(2026, 5, 31)
result = add_months(date, -1)
assert result == datetime.date(2026, 4, 30)
def test_add_zero_months(self) -> None:
"""Adding 0 months returns the same date."""
date = datetime.date(2026, 6, 15)
result = add_months(date, 0)
assert result == date
def test_leap_year_edge_case(self) -> None:
"""Feb 29 in leap year + 1 year = Feb 28 in non-leap year."""
date = datetime.date(2024, 2, 29)
result = add_months(date, 12)
assert result == datetime.date(2025, 2, 28)
def test_leap_year_century_2000_is_leap(self) -> None:
"""Year 2000 is a leap year (divisible by 400)."""
date = datetime.date(2000, 1, 31)
result = add_months(date, 1)
assert result == datetime.date(2000, 2, 29)
def test_leap_year_century_1900_not_leap(self) -> None:
"""Year 1900 is not a leap year (divisible by 100 but not 400)."""
date = datetime.date(1900, 1, 31)
result = add_months(date, 1)
assert result == datetime.date(1900, 2, 28)
class TestAgeInMonths:
"""Test age_in_months function."""
def test_same_day(self) -> None:
"""Same birth and reference date = 0 months."""
birth = datetime.date(2026, 1, 15)
on = datetime.date(2026, 1, 15)
assert age_in_months(birth, on) == 0
def test_one_month_later(self) -> None:
"""One month after birth date."""
birth = datetime.date(2026, 1, 15)
on = datetime.date(2026, 2, 15)
assert age_in_months(birth, on) == 1
def test_one_month_not_complete(self) -> None:
"""Day before one-month anniversary."""
birth = datetime.date(2026, 1, 15)
on = datetime.date(2026, 2, 14)
assert age_in_months(birth, on) == 0
def test_twelve_months(self) -> None:
"""Exactly one year."""
birth = datetime.date(2026, 1, 15)
on = datetime.date(2027, 1, 15)
assert age_in_months(birth, on) == 12
def test_48_months(self) -> None:
"""Exactly 4 years (48 months)."""
birth = datetime.date(2026, 1, 15)
on = datetime.date(2030, 1, 15)
assert age_in_months(birth, on) == 48
def test_month_boundary_not_reached(self) -> None:
"""Before the day of month anniversary."""
birth = datetime.date(2026, 1, 31)
on = datetime.date(2026, 2, 28)
assert age_in_months(birth, on) == 0
def test_month_boundary_reached(self) -> None:
"""On or after the day of month anniversary."""
birth = datetime.date(2026, 1, 31)
on = datetime.date(2026, 2, 28)
assert age_in_months(birth, on) == 0
def test_month_boundary_on_31st_to_mar(self) -> None:
"""Jan 31 to Mar 31."""
birth = datetime.date(2026, 1, 31)
on = datetime.date(2026, 3, 31)
assert age_in_months(birth, on) == 2
def test_negative_age(self) -> None:
"""Reference date before birth date (not yet born)."""
birth = datetime.date(2026, 6, 15)
on = datetime.date(2026, 1, 15)
assert age_in_months(birth, on) == -5
def test_leap_year_boundary(self) -> None:
"""Born on Feb 29 (leap year)."""
birth = datetime.date(2024, 2, 29)
on = datetime.date(2025, 2, 28)
assert age_in_months(birth, on) == 11
def test_leap_year_boundary_anniversary(self) -> None:
"""Born on Feb 29; checking on Mar 1 (anniversary passed)."""
birth = datetime.date(2024, 2, 29)
on = datetime.date(2025, 3, 1)
assert age_in_months(birth, on) == 12
class TestFourthBirthday:
"""Test fourth_birthday function."""
def test_regular_date(self) -> None:
"""Fourth birthday calculation."""
birth = datetime.date(2026, 6, 15)
result = fourth_birthday(birth)
assert result == datetime.date(2030, 6, 15)
def test_leap_year_feb_29(self) -> None:
"""Born on Feb 29; fourth birthday in non-leap year."""
birth = datetime.date(2024, 2, 29)
result = fourth_birthday(birth)
assert result == datetime.date(2028, 2, 29)
def test_jan_31(self) -> None:
"""Born on Jan 31; fourth birthday."""
birth = datetime.date(2022, 1, 31)
result = fourth_birthday(birth)
assert result == datetime.date(2026, 1, 31)
def test_dec_31(self) -> None:
"""Born on Dec 31; fourth birthday crosses year."""
birth = datetime.date(2022, 12, 31)
result = fourth_birthday(birth)
assert result == datetime.date(2026, 12, 31)
def test_fourth_birthday_is_48_months_later(self) -> None:
"""Verify fourth birthday is exactly 48 months after birth."""
birth = datetime.date(2020, 5, 10)
result = fourth_birthday(birth)
assert age_in_months(birth, result) == 48