agenda/tests/test_sun.py
Edward Betts e6327780aa Add comprehensive test coverage for 8 modules
Increases overall test coverage from 53% to 56% by adding tests for:
- accommodation.py (60% → 100%)
- birthday.py (24% → 100%)
- calendar.py (19% → 100%)
- carnival.py (33% → 100%)
- domains.py (75% → 100%)
- events_yaml.py (50% → 96%)
- fx.py (14% → 21% for tested functions)
- sun.py (55% → 100%)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-20 11:39:49 +02:00

183 lines
6.1 KiB
Python

"""Tests for sun functionality."""
from datetime import datetime
from unittest.mock import Mock, patch
import pytest
from agenda.sun import bristol, sunrise, sunset
class TestBristol:
"""Test the bristol function."""
def test_bristol_returns_observer(self) -> None:
"""Test that bristol returns an ephem.Observer with correct coordinates."""
observer = bristol()
# Check that it's an observer object with the right attributes
assert hasattr(observer, 'lat')
assert hasattr(observer, 'lon')
# Check coordinates are set to Bristol
assert str(observer.lat) == "51:27:16.2" # 51.4545 degrees
assert str(observer.lon) == "-2:35:16.4" # -2.5879 degrees
def test_bristol_observer_type(self) -> None:
"""Test that bristol returns the correct type."""
observer = bristol()
# Should have methods needed for sun calculations
assert hasattr(observer, 'next_rising')
assert hasattr(observer, 'next_setting')
class TestSunrise:
"""Test the sunrise function."""
@patch('agenda.sun.ephem')
def test_sunrise_returns_datetime(self, mock_ephem: Mock) -> None:
"""Test that sunrise returns a datetime object."""
# Mock the observer and sun
mock_observer = Mock()
mock_sun = Mock()
mock_ephem.Sun.return_value = mock_sun
# Mock the rising calculation
mock_rising = Mock()
mock_rising.datetime.return_value = datetime(2024, 6, 15, 5, 30, 0)
mock_observer.next_rising.return_value = mock_rising
result = sunrise(mock_observer)
assert isinstance(result, datetime)
assert result == datetime(2024, 6, 15, 5, 30, 0)
# Verify ephem calls
mock_ephem.Sun.assert_called_once_with(mock_observer)
mock_observer.next_rising.assert_called_once_with(mock_sun)
def test_sunrise_with_real_observer(self) -> None:
"""Test sunrise with a real observer (integration test)."""
observer = bristol()
# Set a specific date for the observer
observer.date = "2024/6/21" # Summer solstice
result = sunrise(observer)
# Should return a datetime object
assert isinstance(result, datetime)
# On summer solstice in Bristol, sunrise should be early morning
assert 3 <= result.hour <= 6 # Roughly between 3-6 AM
# Should be in 2024
assert result.year == 2024
def test_sunrise_different_dates(self) -> None:
"""Test sunrise for different dates."""
observer = bristol()
# Summer solstice (longest day)
observer.date = "2024/6/21"
summer_sunrise = sunrise(observer)
# Winter solstice (shortest day)
observer.date = "2024/12/21"
winter_sunrise = sunrise(observer)
# Summer sunrise should be earlier than winter sunrise
assert summer_sunrise.hour < winter_sunrise.hour
class TestSunset:
"""Test the sunset function."""
@patch('agenda.sun.ephem')
def test_sunset_returns_datetime(self, mock_ephem: Mock) -> None:
"""Test that sunset returns a datetime object."""
# Mock the observer and sun
mock_observer = Mock()
mock_sun = Mock()
mock_ephem.Sun.return_value = mock_sun
# Mock the setting calculation
mock_setting = Mock()
mock_setting.datetime.return_value = datetime(2024, 6, 15, 20, 30, 0)
mock_observer.next_setting.return_value = mock_setting
result = sunset(mock_observer)
assert isinstance(result, datetime)
assert result == datetime(2024, 6, 15, 20, 30, 0)
# Verify ephem calls
mock_ephem.Sun.assert_called_once_with(mock_observer)
mock_observer.next_setting.assert_called_once_with(mock_sun)
def test_sunset_with_real_observer(self) -> None:
"""Test sunset with a real observer (integration test)."""
observer = bristol()
# Set a specific date for the observer
observer.date = "2024/6/21" # Summer solstice
result = sunset(observer)
# Should return a datetime object
assert isinstance(result, datetime)
# On summer solstice in Bristol, sunset should be in evening
assert 19 <= result.hour <= 22 # Roughly between 7-10 PM
# Should be in 2024
assert result.year == 2024
def test_sunset_different_dates(self) -> None:
"""Test sunset for different dates."""
observer = bristol()
# Summer solstice (longest day)
observer.date = "2024/6/21"
summer_sunset = sunset(observer)
# Winter solstice (shortest day)
observer.date = "2024/12/21"
winter_sunset = sunset(observer)
# Summer sunset should be later than winter sunset
assert summer_sunset.hour > winter_sunset.hour
class TestSunIntegration:
"""Integration tests for sun calculations."""
def test_day_length_summer_vs_winter(self) -> None:
"""Test that summer days are longer than winter days."""
observer = bristol()
# Summer solstice
observer.date = "2024/6/21"
summer_sunrise = sunrise(observer)
summer_sunset = sunset(observer)
summer_day_length = summer_sunset - summer_sunrise
# Winter solstice
observer.date = "2024/12/21"
winter_sunrise = sunrise(observer)
winter_sunset = sunset(observer)
winter_day_length = winter_sunset - winter_sunrise
# Summer day should be longer than winter day
assert summer_day_length > winter_day_length
def test_sunrise_before_sunset(self) -> None:
"""Test that sunrise is always before sunset."""
observer = bristol()
observer.date = "2024/6/15" # Arbitrary date
sunrise_time = sunrise(observer)
sunset_time = sunset(observer)
assert sunrise_time < sunset_time