"""Tests for birthday functionality.""" import tempfile from datetime import date from typing import Any import pytest import yaml from agenda.birthday import YEAR_NOT_KNOWN, get_birthdays, next_birthday from agenda.event import Event class TestNextBirthday: """Test the next_birthday function.""" def test_birthday_this_year_future(self) -> None: """Test birthday that hasn't occurred this year.""" from_date = date(2024, 3, 15) birth_date = date(1990, 6, 20) next_bday, age = next_birthday(from_date, birth_date) assert next_bday == date(2024, 6, 20) assert age == 34 def test_birthday_this_year_past(self) -> None: """Test birthday that already occurred this year.""" from_date = date(2024, 8, 15) birth_date = date(1990, 6, 20) next_bday, age = next_birthday(from_date, birth_date) assert next_bday == date(2025, 6, 20) assert age == 35 def test_birthday_today(self) -> None: """Test when today is the birthday.""" from_date = date(2024, 6, 20) birth_date = date(1990, 6, 20) next_bday, age = next_birthday(from_date, birth_date) assert next_bday == date(2024, 6, 20) assert age == 34 def test_birthday_unknown_year(self) -> None: """Test birthday with unknown year.""" from_date = date(2024, 3, 15) birth_date = date(YEAR_NOT_KNOWN, 6, 20) next_bday, age = next_birthday(from_date, birth_date) assert next_bday == date(2024, 6, 20) assert age is None def test_birthday_unknown_year_past(self) -> None: """Test birthday with unknown year that already passed.""" from_date = date(2024, 8, 15) birth_date = date(YEAR_NOT_KNOWN, 6, 20) next_bday, age = next_birthday(from_date, birth_date) assert next_bday == date(2025, 6, 20) assert age is None def test_leap_year_birthday(self) -> None: """Test birthday on leap day.""" from_date = date(2024, 1, 15) # 2024 is a leap year birth_date = date(2000, 2, 29) # Born on leap day next_bday, age = next_birthday(from_date, birth_date) assert next_bday == date(2024, 2, 29) assert age == 24 class TestGetBirthdays: """Test the get_birthdays function.""" def test_get_birthdays_with_year(self) -> None: """Test getting birthdays with known birth years.""" birthday_data = [ { "label": "John Doe", "birthday": {"year": 1990, "month": 6, "day": 20} }, { "label": "Jane Smith", "birthday": {"year": 1985, "month": 12, "day": 15} } ] with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: yaml.dump(birthday_data, f) filepath = f.name try: from_date = date(2024, 3, 15) events = get_birthdays(from_date, filepath) # Should have 4 events (2 people × 2 years each) assert len(events) == 4 # Check John's birthdays john_events = [e for e in events if "John Doe" in e.title] assert len(john_events) == 2 assert john_events[0].date == date(2024, 6, 20) assert "aged 34" in john_events[0].title assert john_events[1].date == date(2025, 6, 20) assert "aged 35" in john_events[1].title # Check Jane's birthdays jane_events = [e for e in events if "Jane Smith" in e.title] assert len(jane_events) == 2 assert jane_events[0].date == date(2024, 12, 15) assert "aged 39" in jane_events[0].title # All events should be birthday events for event in events: assert event.name == "birthday" assert isinstance(event, Event) finally: import os os.unlink(filepath) def test_get_birthdays_without_year(self) -> None: """Test getting birthdays with unknown birth years.""" birthday_data = [ { "label": "Anonymous Person", "birthday": {"month": 6, "day": 20} } ] with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: yaml.dump(birthday_data, f) filepath = f.name try: from_date = date(2024, 3, 15) events = get_birthdays(from_date, filepath) # Should have 2 events (1 person × 2 years) assert len(events) == 2 # Check that age is unknown for event in events: assert "age unknown" in event.title assert "Anonymous Person" in event.title assert event.name == "birthday" finally: import os os.unlink(filepath) def test_get_birthdays_mixed_data(self) -> None: """Test getting birthdays with mixed known/unknown years.""" birthday_data = [ { "label": "Known Person", "birthday": {"year": 1990, "month": 6, "day": 20} }, { "label": "Unknown Person", "birthday": {"month": 8, "day": 15} } ] with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: yaml.dump(birthday_data, f) filepath = f.name try: from_date = date(2024, 3, 15) events = get_birthdays(from_date, filepath) # Should have 4 events total assert len(events) == 4 # Check known person events known_events = [e for e in events if "Known Person" in e.title] assert len(known_events) == 2 assert all("aged" in e.title and "age unknown" not in e.title for e in known_events) # Check unknown person events unknown_events = [e for e in events if "Unknown Person" in e.title] assert len(unknown_events) == 2 assert all("age unknown" in e.title for e in unknown_events) finally: import os os.unlink(filepath) def test_get_birthdays_empty_file(self) -> None: """Test getting birthdays from empty file.""" birthday_data: list[dict[str, Any]] = [] with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: yaml.dump(birthday_data, f) filepath = f.name try: from_date = date(2024, 3, 15) events = get_birthdays(from_date, filepath) assert events == [] finally: import os os.unlink(filepath) def test_get_birthdays_file_not_found(self) -> None: """Test error handling when file doesn't exist.""" from_date = date(2024, 3, 15) with pytest.raises(FileNotFoundError): get_birthdays(from_date, "/nonexistent/file.yaml") class TestConstants: """Test module constants.""" def test_year_not_known_constant(self) -> None: """Test that YEAR_NOT_KNOWN has expected value.""" assert YEAR_NOT_KNOWN == 1900