import pytest from trip_planner import combine_trips, _fmt_duration DATE = '2026-03-30' # --------------------------------------------------------------------------- # _fmt_duration # --------------------------------------------------------------------------- def test_fmt_duration_hours_and_minutes(): assert _fmt_duration(95) == '1h 35m' def test_fmt_duration_exact_hours(): assert _fmt_duration(120) == '2h' def test_fmt_duration_minutes_only(): assert _fmt_duration(45) == '45m' # --------------------------------------------------------------------------- # combine_trips — basic pairing # --------------------------------------------------------------------------- GWR_FAST = {'depart_bristol': '07:00', 'arrive_paddington': '08:45'} # 1h 45m GWR_SLOW = {'depart_bristol': '07:00', 'arrive_paddington': '09:26'} # 2h 26m — over limit ES_PARIS = {'depart_st_pancras': '10:01', 'arrive_destination': '13:34', 'destination': 'Paris Gare du Nord'} ES_EARLY = {'depart_st_pancras': '09:00', 'arrive_destination': '12:00', 'destination': 'Paris Gare du Nord'} def test_valid_trip_is_returned(): trips = combine_trips([GWR_FAST], [ES_PARIS], DATE) assert len(trips) == 1 t = trips[0] assert t['depart_bristol'] == '07:00' assert t['arrive_paddington'] == '08:45' assert t['depart_st_pancras'] == '10:01' assert t['arrive_destination'] == '13:34' assert t['destination'] == 'Paris Gare du Nord' def test_gwr_too_slow_excluded(): # 2h 26m GWR journey exceeds MAX_GWR_MINUTES (110) trips = combine_trips([GWR_SLOW], [ES_PARIS], DATE) assert trips == [] def test_eurostar_too_early_excluded(): # Eurostar departs before min connection time has elapsed trips = combine_trips([GWR_FAST], [ES_EARLY], DATE) assert trips == [] def test_no_trains_returns_empty(): assert combine_trips([], [], DATE) == [] def test_no_gwr_returns_empty(): assert combine_trips([], [ES_PARIS], DATE) == [] def test_no_eurostar_returns_empty(): assert combine_trips([GWR_FAST], [], DATE) == [] # --------------------------------------------------------------------------- # Connection window constraints # --------------------------------------------------------------------------- def test_min_connection_enforced(): # Arrive Paddington 08:45, need 75 min → earliest St Pancras 10:00 # ES at 09:59 should be excluded, 10:00 should be included es_too_close = {'depart_st_pancras': '09:59', 'arrive_destination': '13:00', 'destination': 'Paris Gare du Nord'} es_ok = {'depart_st_pancras': '10:00', 'arrive_destination': '13:00', 'destination': 'Paris Gare du Nord'} assert combine_trips([GWR_FAST], [es_too_close], DATE) == [] trips = combine_trips([GWR_FAST], [es_ok], DATE) assert len(trips) == 1 def test_max_connection_enforced(): # Arrive Paddington 08:45, max 140 min → latest St Pancras 11:05 es_ok = {'depart_st_pancras': '11:05', 'arrive_destination': '14:00', 'destination': 'Paris Gare du Nord'} es_too_late = {'depart_st_pancras': '11:06', 'arrive_destination': '14:00', 'destination': 'Paris Gare du Nord'} trips = combine_trips([GWR_FAST], [es_ok], DATE) assert len(trips) == 1 assert combine_trips([GWR_FAST], [es_too_late], DATE) == [] # --------------------------------------------------------------------------- # Only earliest valid Eurostar per GWR departure # --------------------------------------------------------------------------- def test_only_earliest_eurostar_per_gwr(): es1 = {'depart_st_pancras': '10:01', 'arrive_destination': '13:34', 'destination': 'Paris Gare du Nord'} es2 = {'depart_st_pancras': '11:01', 'arrive_destination': '14:34', 'destination': 'Paris Gare du Nord'} trips = combine_trips([GWR_FAST], [es1, es2], DATE) assert len(trips) == 1 assert trips[0]['depart_st_pancras'] == '10:01' # --------------------------------------------------------------------------- # Multiple GWR trains → multiple trips # --------------------------------------------------------------------------- def test_multiple_gwr_trains(): gwr2 = {'depart_bristol': '08:00', 'arrive_paddington': '09:45'} es = {'depart_st_pancras': '11:01', 'arrive_destination': '14:34', 'destination': 'Paris Gare du Nord'} trips = combine_trips([GWR_FAST, gwr2], [es], DATE) assert len(trips) == 2 assert trips[0]['depart_bristol'] == '07:00' assert trips[1]['depart_bristol'] == '08:00' # --------------------------------------------------------------------------- # Duration fields # --------------------------------------------------------------------------- def test_gwr_duration_in_trip(): trips = combine_trips([GWR_FAST], [ES_PARIS], DATE) assert trips[0]['gwr_duration'] == '1h 45m' def test_total_duration_in_trip(): # depart 07:00, arrive 13:34 → 6h 34m trips = combine_trips([GWR_FAST], [ES_PARIS], DATE) assert trips[0]['total_duration'] == '6h 34m' def test_connection_duration_in_trip(): # arrive Paddington 08:45, depart St Pancras 10:01 → 1h 16m trips = combine_trips([GWR_FAST], [ES_PARIS], DATE) assert trips[0]['connection_duration'] == '1h 16m'