| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- from __future__ import annotations
- from datetime import date, timedelta
- from app.schemas.travel_request import (
- FlightPreferences,
- HotelPreferences,
- TravelRequest,
- )
- from app.services.candidate_selection_service import (
- CandidateSelectionService,
- )
- def build_request(
- *,
- priority: str = "balanced",
- max_stops: int | None = None,
- ) -> TravelRequest:
- departure_date = (
- date.today() + timedelta(days=10)
- )
- return TravelRequest(
- origin_city="上海",
- destination_city="成都",
- departure_date=departure_date,
- return_date=(
- departure_date
- + timedelta(days=3)
- ),
- adults=2,
- flight_preferences=(
- FlightPreferences(
- priority=priority,
- max_stops=max_stops,
- )
- ),
- hotel_preferences=(
- HotelPreferences(
- max_price_per_night=600,
- minimum_rating=4.5,
- )
- ),
- )
- def test_price_priority_prefers_lower_price() -> None:
- service = CandidateSelectionService()
- flights = [
- {
- "option_id": "cheap",
- "price": 700,
- "duration_minutes": 300,
- "stop_count": 1,
- "departure_airport": "PVG",
- "arrival_airport": "TFU",
- "departure_time": (
- "2030-01-01 08:00"
- ),
- "arrival_time": (
- "2030-01-01 13:00"
- ),
- },
- {
- "option_id": "fast",
- "price": 900,
- "duration_minutes": 120,
- "stop_count": 0,
- "departure_airport": "PVG",
- "arrival_airport": "TFU",
- "departure_time": (
- "2030-01-01 09:00"
- ),
- "arrival_time": (
- "2030-01-01 11:00"
- ),
- },
- ]
- ranked, warnings = service.rank_flights(
- flights,
- build_request(priority="price"),
- )
- assert ranked[0].option_id == "cheap"
- assert warnings == []
- def test_max_stops_filters_connections() -> None:
- service = CandidateSelectionService()
- flights = [
- {
- "option_id": "direct",
- "price": 900,
- "duration_minutes": 150,
- "stop_count": 0,
- },
- {
- "option_id": "transfer",
- "price": 700,
- "duration_minutes": 280,
- "stop_count": 1,
- },
- ]
- ranked, _ = service.rank_flights(
- flights,
- build_request(max_stops=0),
- )
- assert len(ranked) == 1
- assert ranked[0].option_id == "direct"
- def test_hotel_hard_filters() -> None:
- service = CandidateSelectionService()
- hotels = [
- {
- "hotel_id": "valid",
- "name": "酒店A",
- "price_per_night": 500,
- "overall_rating": 4.6,
- "location_rating": 4.5,
- },
- {
- "hotel_id": "too-expensive",
- "name": "酒店B",
- "price_per_night": 800,
- "overall_rating": 4.8,
- "location_rating": 4.7,
- },
- {
- "hotel_id": "low-rating",
- "name": "酒店C",
- "price_per_night": 400,
- "overall_rating": 4.0,
- "location_rating": 4.2,
- },
- ]
- ranked, _ = service.rank_hotels(
- hotels,
- build_request(),
- )
- assert len(ranked) == 1
- assert ranked[0].hotel_id == "valid"
- def test_round_trip_uses_return_quote() -> None:
- service = CandidateSelectionService()
- combinations = [
- {
- "outbound": {
- "option_id": "out-1",
- "price": 1000,
- "duration_minutes": 180,
- "stop_count": 0,
- "currency": "CNY",
- },
- "return_flight": {
- "option_id": "ret-1",
- "price": 1800,
- "duration_minutes": 190,
- "stop_count": 0,
- "currency": "CNY",
- },
- }
- ]
- ranked = (
- service.rank_round_trip_combinations(
- combinations,
- build_request(),
- )
- )
- assert len(ranked) == 1
- # 不是1000 + 1800。
- assert ranked[0].quoted_price == 1800
|