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, travel_data["bookings"], travel_data["accommodations"], travel_data["airports"], ) 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, travel_data["bookings"], travel_data["accommodations"], travel_data["airports"], ) 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, travel_data["bookings"], travel_data["accommodations"], travel_data["airports"], ) # 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_train_location_helpers(): """Test the train location helper functions.""" from agenda.busy import _get_train_location # Mock station data stations = [ {"name": "London St Pancras", "country": "gb"}, {"name": "Brussels Midi", "country": "be"}, {"name": "Edinburgh Waverley", "country": "gb"}, ] # Test UK station when not on trip (should return None for home) train_leg = {"to": "London St Pancras"} location = _get_train_location(train_leg, stations, on_trip=False) assert location[0] is None # Should be home assert location[1].alpha_2 == "GB" # Test UK station when on trip (should return city name) location = _get_train_location(train_leg, stations, on_trip=True) assert location[0] == "London St Pancras" assert location[1].alpha_2 == "GB" # Test non-UK station train_leg = {"to": "Brussels Midi"} location = _get_train_location(train_leg, stations, on_trip=False) assert location[0] == "Brussels Midi" assert location[1].alpha_2 == "BE" def test_ferry_location_helpers(): """Test the ferry location helper functions.""" from agenda.busy import _get_ferry_location # Mock terminal data terminals = [ {"name": "Dover Eastern Docks", "country": "gb", "city": "Dover"}, {"name": "Calais Ferry Terminal", "country": "fr", "city": "Calais"}, { "name": "Portsmouth Continental Terminal", "country": "gb", "city": "Portsmouth", }, ] # Test UK terminal when not on trip (should return None for home) ferry = {"to": "Dover Eastern Docks"} location = _get_ferry_location(ferry, terminals, on_trip=False) assert location[0] is None # Should be home assert location[1].alpha_2 == "GB" # Test UK terminal when on trip (should return city name) location = _get_ferry_location(ferry, terminals, on_trip=True) assert location[0] == "Dover" assert location[1].alpha_2 == "GB" # Test non-UK terminal ferry = {"to": "Calais Ferry Terminal"} location = _get_ferry_location(ferry, terminals, on_trip=False) assert location[0] == "Calais" assert location[1].alpha_2 == "FR" 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