Spaces:
Sleeping
Sleeping
| """Regression tests for the adversarial-review fixes (input robustness + clarity). | |
| These lock in invariants that were previously violated: mode validation, the | |
| budget=0 == plain-route guarantee, non-Paris rejection, nonsense-vibe → neutral, | |
| the unnamed-POI demotion, and place-count consistency across UI surfaces. | |
| """ | |
| from __future__ import annotations | |
| import app | |
| from discoverroute.data import taxonomy | |
| from discoverroute.interpret.affinity import resolve_affinity | |
| from discoverroute.pipeline import plan_route | |
| from discoverroute.routing import scoring | |
| S = "Place de la République, Paris" | |
| D = "Jardin du Luxembourg, Paris" | |
| # ---- input validation ------------------------------------------------------ | |
| def test_invalid_mode_rejected(): | |
| assert plan_route(S, D, mode="car").error | |
| assert plan_route(S, D, mode="xyz").error | |
| def test_uppercase_mode_normalized(): | |
| assert plan_route(S, D, mode="WALK").error is None | |
| def test_far_apart_endpoints_rejected_not_namesake_routed(): | |
| # World support: endpoints on different continents can't form one walkable | |
| # discovery route. Resolve via lat/lon (no network) so the distance cap fires | |
| # — and crucially we get an honest error, never a fake namesake route. | |
| london, tokyo = "51.5079, -0.0877", "35.6586, 139.7454" | |
| r = plan_route(london, tokyo, vibe="quiet") | |
| assert r.error and "far" in r.error.lower() | |
| assert r.discovery is None and not r.pois | |
| def test_budget_zero_is_plain_route_even_with_pace_word(): | |
| # P0-3: an explicit 0 slider must win over a vibe pace hint ("all day"). | |
| r = plan_route(S, D, budget=0.0, vibe="I want to spend all day exploring") | |
| assert r.discovery is None and not r.pois | |
| # ---- vibe interpretation --------------------------------------------------- | |
| def test_nonsense_vibe_degrades_to_neutral(): | |
| aff, _ = resolve_affinity("quantum physics asdfgh") | |
| spread = max(aff.values()) - min(aff.values()) | |
| assert spread < 1e-6 # all categories equal → neutral | |
| def test_real_vibe_is_confident(): | |
| aff, _ = resolve_affinity("quiet green park") | |
| assert (max(aff.values()) - min(aff.values())) > 0.2 | |
| # ---- unnamed-POI demotion -------------------------------------------------- | |
| class _POI: | |
| def __init__(self, name, category="park_garden", confidence=0.5): | |
| self.name, self.category, self.confidence = name, category, confidence | |
| def test_unnamed_poi_scored_below_named(): | |
| w = scoring.Weights(category_affinity={"park_garden": 1.0}) | |
| named = scoring.base_score(_POI("Jardin X"), w, adventurousness=1.0) | |
| unnamed = scoring.base_score(_POI(None), w, adventurousness=1.0) | |
| assert unnamed < named # demoted even at max adventurousness | |
| def test_high_adventurousness_not_mostly_unnamed(): | |
| r = plan_route(S, D, vibe="hidden gems off the beaten path", | |
| budget=0.7, adventurousness=1.0) | |
| if r.pois: | |
| unnamed = sum(1 for p in r.pois | |
| if not (getattr(p, "name", None) and str(p.name).strip())) | |
| assert unnamed / len(r.pois) <= 0.5 | |
| # ---- labels & count consistency -------------------------------------------- | |
| def test_display_label_article_and_no_snake_case(): | |
| assert taxonomy.display_label(_POI(None, "artwork")) == "a piece of public art" | |
| assert taxonomy.display_label(_POI(None, "attraction")) == "a landmark" | |
| assert "_" not in taxonomy.display_label(_POI(None, "monument_historic")) | |
| assert taxonomy.display_label(_POI("Louvre")) == "Louvre" | |
| def test_place_count_consistent_across_surfaces(): | |
| r = plan_route(S, D, vibe="quiet green bookshops", budget=0.5, n_alternatives=1) | |
| if r.alternatives: | |
| a = r.alternatives[0] | |
| n = len(a.pois) | |
| assert f"{n} place" in a.summary_md | |
| assert f"{n} place" in app._alt_label(0, a, r.plain) | |
| assert f"{n} place" in a.itinerary_md | |