"""Unit tests for the Deal Intelligence Engine (pure functions, no network).""" import os import sys from datetime import date sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) os.environ.setdefault("TRAVELPAYOUTS_TOKEN", "test-token") from flights import intelligence as iq PASS = 0 def check(name, cond): global PASS assert cond, f"FAIL: {name}" PASS += 1 print(f" ok {name}") # ── fair_price: hedonic baseline sanity ────────────────────────────────────── short = iq.fair_price(500) # TLV->ATH-ish medium = iq.fair_price(3200) # TLV->LON-ish long = iq.fair_price(9100) # TLV->JFK-ish check("baseline increases with distance", short < medium < long) check("short-haul in sane range", 60 <= short <= 160) check("long-haul in sane range", 300 <= long <= 900) per_km_short = short / 500 per_km_long = long / 9100 check("economies of distance (concave curve)", per_km_long < per_km_short) july_fri = iq.fair_price(3200, date(2026, 7, 3)) # Friday in July (peak+peak) feb_tue = iq.fair_price(3200, date(2026, 2, 10)) # Tuesday in February (trough) check("July Friday priced above February Tuesday", july_fri > feb_tue) check("round trip < 2x one-way", iq.fair_price(3200, round_trip=True) < 2 * iq.fair_price(3200)) # ── deal_score: monotone, labeled, explainable ─────────────────────────────── steal = iq.deal_score(0.5 * medium, medium) fairp = iq.deal_score(medium, medium) rip = iq.deal_score(1.8 * medium, medium) check("half typical scores ~95", steal["score"] >= 90) check("typical price scores 60", fairp["score"] == 60) check("1.8x typical floors", rip["score"] <= 10) check("scores monotone", steal["score"] > fairp["score"] > rip["score"]) check("labels attached", steal["label"] == "exceptional deal" and fairp["label"] == "typical price") check("vs_typical_pct signed", steal["vs_typical_pct"] == -50 and rip["vs_typical_pct"] == 80) check("degenerate fair handled", iq.deal_score(100, 0)["score"] is None) # ── annotate_offers: uses airport coords, skips unknowns ───────────────────── offers = [ {"price": 120.0, "origin": "TLV", "destination": "ATH", "date": "2026-08-10", "round_trip": False}, {"price": None, "origin": "TLV", "destination": "ATH", "date": "2026-08-10"}, # unpriced {"price": 99.0, "origin": "XXQ", "destination": "ZZQ", "date": "2026-08-10"}, # unknown route ] iq.annotate_offers(offers) check("priced known route annotated", offers[0].get("deal", {}).get("score") is not None) check("unpriced offer skipped", "deal" not in offers[1]) check("unknown route skipped, no crash", "deal" not in offers[2]) # ── date_insights: fly-Tuesday-save-$X analytics ───────────────────────────── month = [ {"price": 210.0, "date": "2026-08-03"}, # Monday {"price": 150.0, "date": "2026-08-04"}, # Tuesday <- cheapest {"price": 205.0, "date": "2026-08-07"}, # Friday {"price": 260.0, "date": "2026-08-08"}, # Saturday <- priciest {"price": 155.0, "date": "2026-08-11"}, # Tuesday ] ins = iq.date_insights(month) check("cheapest date found", ins["cheapest_date"] == "2026-08-04" and ins["cheapest_date_price"] == 150.0) check("flex savings computed", ins["max_flex_savings"] == 110.0) check("best weekday is Tuesday", ins.get("best_weekday") == "Tuesday") check("weekday savings positive", ins.get("weekday_avg_savings", 0) > 0) check("insufficient data -> None", iq.date_insights([{"price": 100.0, "date": "2026-08-03"}]) is None) # ── wander_scores: utility per dollar, best = 100 ──────────────────────────── alts = [ {"similarity": 0.90, "cheapest_flight": {"price": 300.0}}, # 0.0030 per $ {"similarity": 0.80, "cheapest_flight": {"price": 160.0}}, # 0.0050 per $ <- best value {"similarity": 0.95, "cheapest_flight": {"price": None}}, # unpriced -> no score ] iq.wander_scores(alts) check("best value scores 100", alts[1]["wander_score"] == 100) check("worse value scaled down", alts[0]["wander_score"] == 60) check("unpriced alt unscored", "wander_score" not in alts[2]) check("empty set safe", iq.wander_scores([]) == []) # ── scan_start_dates: multi-month cheapest-date engine ─────────────────────── d0 = date(2026, 11, 20) scan = iq.scan_start_dates(d0, 3) check("scan keeps start date", scan[0] == d0) check("scan hits following month firsts", scan[1] == date(2026, 12, 1)) check("scan wraps the year", scan[2] == date(2027, 1, 1)) check("scan n=1 is just start", iq.scan_start_dates(d0, 1) == [d0]) # ── price_calendar: cheapest per date, date-sorted ─────────────────────────── cal = iq.price_calendar([ {"price": 220.0, "date": "2026-08-10", "stops": 1}, {"price": 180.0, "date": "2026-08-10", "stops": 0}, # cheaper same-day wins {"price": 150.0, "date": "2026-08-04", "stops": 0}, {"price": None, "date": "2026-08-05"}, # unpriced skipped {"price": 300.0, "date": "not-a-date"}, # junk skipped ]) check("calendar keeps cheapest per date", [c["price"] for c in cal] == [150.0, 180.0]) check("calendar date-sorted", [c["date"] for c in cal] == ["2026-08-04", "2026-08-10"]) check("calendar carries stops", cal[1]["stops"] == 0) print(f"\nALL {PASS} CHECKS PASSED")