from datetime import date, datetime import agenda.busy import agenda.travel as travel import agenda.trip import pytest from agenda.busy import _parse_datetime_field from agenda.event import Event from web_view import app @pytest.fixture(scope="session") def app_context(): """Set up Flask app context for tests.""" app.config["SERVER_NAME"] = "test" with app.app_context(): yield @pytest.fixture(scope="session") def trips(app_context): """Load trip list once for all tests.""" return agenda.trip.build_trip_list() @pytest.fixture(scope="session") def travel_data(app_context): """Load travel data (bookings, accommodations, airports) once for all tests.""" data_dir = app.config["PERSONAL_DATA"] return { "bookings": travel.parse_yaml("flights", data_dir), "accommodations": travel.parse_yaml("accommodation", data_dir), "airports": travel.parse_yaml("airports", data_dir), "data_dir": data_dir, } def test_weekend_location_consistency(app_context, trips, travel_data): """Test that weekend locations are consistent with events (free=home, events=away).""" for year in range(2023, 2025): start = date(2023, 1, 1) busy_events = agenda.busy.get_busy_events(start, app.config, trips) weekends = agenda.busy.weekends( start, busy_events, trips, travel_data["data_dir"] ) for weekend in weekends: for day in "saturday", "sunday": # When free (no events), should be home (None) # When traveling (events), should be away (City name) location_exists = bool(weekend[day + "_location"][0]) has_events = bool(weekend[day]) assert location_exists == has_events, ( f"Weekend {weekend['date']} {day}: " f"location_exists={location_exists}, has_events={has_events}" ) def test_specific_home_dates(travel_data): """Test specific dates that should return home (None).""" trips = agenda.trip.build_trip_list() home_dates = [ date(2023, 4, 29), date(2025, 7, 1), date(2023, 12, 2), date(2023, 10, 7), date(2023, 2, 18), date(2025, 8, 2), ] for test_date in home_dates: location = agenda.busy.get_location_for_date( test_date, trips, ) assert not location[ 0 ], f"Expected home (None) for {test_date}, got {location[0]}" def test_specific_away_dates(travel_data): """Test specific dates that should return away locations.""" trips = agenda.trip.build_trip_list() away_cases = [ (date(2025, 2, 15), "Hackettstown"), ] for test_date, expected_city in away_cases: location = agenda.busy.get_location_for_date( test_date, trips, ) assert ( location[0] == expected_city ), f"Expected {expected_city} for {test_date}, got {location[0]}" def test_get_location_for_date_basic(travel_data): """Test basic functionality of get_location_for_date function.""" trips = agenda.trip.build_trip_list() test_date = date(2023, 1, 1) location = agenda.busy.get_location_for_date( test_date, trips, ) # Should return a tuple with (city|None, country) assert isinstance(location, tuple) assert len(location) == 2 assert location[1] is not None # Should always have a country def test_busy_event_classification(): """Test the busy_event function for different event types.""" # Busy event types busy_events = [ Event(name="event", title="Test Event", date=date(2023, 1, 1)), Event( name="conference", title="Test Conference", date=date(2023, 1, 1), going=True, ), Event(name="accommodation", title="Hotel", date=date(2023, 1, 1)), Event(name="transport", title="Flight", date=date(2023, 1, 1)), ] for event in busy_events: assert agenda.busy.busy_event(event), f"Event {event.name} should be busy" # Non-busy events non_busy_events = [ Event( name="conference", title="Test Conference", date=date(2023, 1, 1), going=False, ), Event(name="other", title="Other Event", date=date(2023, 1, 1)), Event(name="event", title="LHG Run Club", date=date(2023, 1, 1)), Event(name="event", title="IA UK board meeting", date=date(2023, 1, 1)), ] for event in non_busy_events: assert not agenda.busy.busy_event( event ), f"Event {event.name}/{event.title} should not be busy" def test_parse_datetime_field(): """Test the _parse_datetime_field helper function.""" # Test with datetime object dt = datetime(2023, 1, 1, 12, 0, 0) parsed_dt, parsed_date = _parse_datetime_field(dt) assert parsed_dt == dt assert parsed_date == date(2023, 1, 1) # Test with ISO string iso_string = "2023-01-01T12:00:00Z" parsed_dt, parsed_date = _parse_datetime_field(iso_string) assert parsed_date == date(2023, 1, 1) assert parsed_dt.year == 2023 assert parsed_dt.month == 1 assert parsed_dt.day == 1 def test_get_busy_events(app_context, trips): """Test get_busy_events function.""" start_date = date(2023, 1, 1) busy_events = agenda.busy.get_busy_events(start_date, app.config, trips) # Should return a list assert isinstance(busy_events, list) # All events should be Event objects for event in busy_events: assert hasattr(event, "name") assert hasattr(event, "as_date") # Events should be sorted by date dates = [event.as_date for event in busy_events] assert dates == sorted(dates), "Events should be sorted by date" def test_weekends_function(app_context, trips, travel_data): """Test the weekends function.""" start_date = date(2023, 1, 1) busy_events = agenda.busy.get_busy_events(start_date, app.config, trips) weekends = agenda.busy.weekends( start_date, busy_events, trips, travel_data["data_dir"] ) # Should return a list of weekend info assert isinstance(weekends, list) assert len(weekends) == 52 # Should return 52 weekends # Each weekend should have the required keys for weekend in weekends[:5]: # Check first 5 weekends assert "date" in weekend assert "saturday" in weekend assert "sunday" in weekend assert "saturday_location" in weekend assert "sunday_location" in weekend # Locations should be tuples assert isinstance(weekend["saturday_location"], tuple) assert isinstance(weekend["sunday_location"], tuple) assert len(weekend["saturday_location"]) == 2 assert len(weekend["sunday_location"]) == 2