136 lines
4.4 KiB
Python
136 lines
4.4 KiB
Python
from typing import Any
|
|
|
|
import pytest
|
|
|
|
from flight_club.constants import NOT_AVAILABLE
|
|
from flight_club.flight_data import FlightData, find_cheapest_flight
|
|
|
|
RETURN_DATE = "2026-06-22"
|
|
|
|
SAMPLE_RESPONSE: dict[str, Any] = {
|
|
"best_flights": [
|
|
{
|
|
"flights": [
|
|
{
|
|
"departure_airport": {"id": "LHR", "time": "2026-06-15 10:30"},
|
|
"arrival_airport": {"id": "CDG", "time": "2026-06-15 12:50"},
|
|
}
|
|
],
|
|
"price": 120,
|
|
}
|
|
],
|
|
"other_flights": [
|
|
{
|
|
"flights": [
|
|
{
|
|
"departure_airport": {"id": "LHR", "time": "2026-06-16 07:00"},
|
|
"arrival_airport": {"id": "CDG", "time": "2026-06-16 09:20"},
|
|
}
|
|
],
|
|
"price": 95,
|
|
},
|
|
{
|
|
"flights": [
|
|
{
|
|
"departure_airport": {"id": "LHR", "time": "2026-06-17 18:00"},
|
|
"arrival_airport": {"id": "CDG", "time": "2026-06-17 20:20"},
|
|
}
|
|
],
|
|
"price": 210,
|
|
},
|
|
],
|
|
}
|
|
|
|
|
|
def _entry(price: Any, origin: str = "LHR", destination: str = "CDG") -> dict[str, Any]:
|
|
return {
|
|
"flights": [
|
|
{
|
|
"departure_airport": {"id": origin, "time": "2026-07-01 08:00"},
|
|
"arrival_airport": {"id": destination, "time": "2026-07-01 10:00"},
|
|
}
|
|
],
|
|
"price": price,
|
|
}
|
|
|
|
|
|
def _unavailable() -> FlightData:
|
|
return FlightData(NOT_AVAILABLE, NOT_AVAILABLE, NOT_AVAILABLE, NOT_AVAILABLE, NOT_AVAILABLE)
|
|
|
|
|
|
def test_find_cheapest_flight_picks_lowest_across_both_lists() -> None:
|
|
result = find_cheapest_flight(SAMPLE_RESPONSE, RETURN_DATE)
|
|
assert result == FlightData(95, "LHR", "CDG", "2026-06-16", RETURN_DATE)
|
|
|
|
|
|
def test_find_cheapest_flight_uses_only_other_flights_when_best_missing() -> None:
|
|
data = {"other_flights": [_entry(80), _entry(60, destination="ORY")]}
|
|
result = find_cheapest_flight(data, RETURN_DATE)
|
|
assert result.price == 60
|
|
assert result.destination_airport == "ORY"
|
|
|
|
|
|
def test_find_cheapest_flight_skips_missing_price() -> None:
|
|
data = {"best_flights": [{"flights": _entry(1)["flights"]}, _entry(150)]}
|
|
assert find_cheapest_flight(data, RETURN_DATE).price == 150
|
|
|
|
|
|
@pytest.mark.parametrize("price", ["abc", None, [], True])
|
|
def test_find_cheapest_flight_skips_non_numeric_price(price: Any) -> None:
|
|
data = {"best_flights": [_entry(price), _entry(300)]}
|
|
assert find_cheapest_flight(data, RETURN_DATE).price == 300
|
|
|
|
|
|
def test_find_cheapest_flight_returns_unavailable_for_empty_dict() -> None:
|
|
assert find_cheapest_flight({}, RETURN_DATE) == _unavailable()
|
|
|
|
|
|
def test_find_cheapest_flight_returns_unavailable_for_none(
|
|
capsys: pytest.CaptureFixture[str],
|
|
) -> None:
|
|
assert find_cheapest_flight(None, RETURN_DATE) == _unavailable()
|
|
assert capsys.readouterr().out.strip() != ""
|
|
|
|
|
|
def test_find_cheapest_flight_skips_malformed_entries() -> None:
|
|
data = {
|
|
"best_flights": [
|
|
{"price": 10},
|
|
{"flights": [], "price": 10},
|
|
{"flights": [{"departure_airport": {}}], "price": 10},
|
|
"not a dict",
|
|
_entry(200),
|
|
]
|
|
}
|
|
assert find_cheapest_flight(data, RETURN_DATE).price == 200
|
|
|
|
|
|
def test_find_cheapest_flight_returns_unavailable_when_all_malformed() -> None:
|
|
data = {"best_flights": [{"price": 10}, {"flights": [], "price": 5}]}
|
|
assert find_cheapest_flight(data, RETURN_DATE) == _unavailable()
|
|
|
|
|
|
def test_find_cheapest_flight_takes_destination_from_last_leg() -> None:
|
|
entry = {
|
|
"flights": [
|
|
{
|
|
"departure_airport": {"id": "LHR", "time": "2026-08-01 06:00"},
|
|
"arrival_airport": {"id": "AMS", "time": "2026-08-01 08:00"},
|
|
},
|
|
{
|
|
"departure_airport": {"id": "AMS", "time": "2026-08-01 09:30"},
|
|
"arrival_airport": {"id": "BCN", "time": "2026-08-01 11:45"},
|
|
},
|
|
],
|
|
"price": 140,
|
|
}
|
|
result = find_cheapest_flight({"best_flights": [entry]}, RETURN_DATE)
|
|
assert result.origin_airport == "LHR"
|
|
assert result.destination_airport == "BCN"
|
|
|
|
|
|
def test_find_cheapest_flight_trims_time_from_out_date() -> None:
|
|
result = find_cheapest_flight({"best_flights": [_entry(50)]}, RETURN_DATE)
|
|
assert result.out_date == "2026-07-01"
|
|
assert result.return_date == RETURN_DATE
|