| """Geocode, festivals, events, advisories tools with mocked HTTP.""" |
|
|
| import requests as requests_lib |
|
|
| import pytest |
|
|
| from app.tools import advisories, events, festivals, geocode |
|
|
|
|
| |
|
|
| def test_geocode_returns_best_match(monkeypatch, fake_response, fake_keys): |
| captured = {"urls": [], "params": []} |
|
|
| def fake_get(url, params=None, timeout=None): |
| captured["urls"].append(url) |
| captured["params"].append(params) |
| return fake_response( |
| { |
| "results": [ |
| { |
| "position": {"lat": 26.8512, "lon": 80.9462}, |
| "address": {"freeformAddress": "Hazratganj, Lucknow"}, |
| "poi": {"name": "Hazratganj Market"}, |
| } |
| ] |
| } |
| ) |
|
|
| monkeypatch.setattr(geocode.requests, "get", fake_get) |
| result = geocode.geocode_place("Hazratganj") |
|
|
| assert result["lat"] == 26.8512 |
| assert result["name"] == "Hazratganj Market" |
| assert any("Lucknow" in url for url in captured["urls"]) |
| assert captured["params"][0]["countrySet"] == "IN" |
|
|
|
|
| def test_geocode_no_results_raises(monkeypatch, fake_response, fake_keys): |
| monkeypatch.setattr(geocode.requests, "get", lambda *a, **k: fake_response({"results": []})) |
| with pytest.raises(RuntimeError, match="No location found"): |
| geocode.geocode_place("Nonexistent Place XYZ") |
|
|
|
|
| def test_geocode_prefers_poi_over_city_geography(monkeypatch, fake_response, fake_keys): |
| """'iiit lucknow' must resolve to the campus POI, not Lucknow city center.""" |
| payload = { |
| "results": [ |
| { |
| "type": "Geography", |
| "position": {"lat": 26.8467, "lon": 80.9462}, |
| "address": {"freeformAddress": "Lucknow, Uttar Pradesh"}, |
| }, |
| { |
| "type": "POI", |
| "position": {"lat": 26.7991, "lon": 81.0220}, |
| "address": {"freeformAddress": "IIIT Lucknow, Ahmamau"}, |
| "poi": {"name": "Indian Institute of Information Technology Lucknow"}, |
| }, |
| ] |
| } |
| monkeypatch.setattr(geocode.requests, "get", lambda *a, **k: fake_response(payload)) |
|
|
| result = geocode.geocode_place("iiit lucknow") |
| assert result["lat"] == 26.7991 |
| assert "Information Technology" in result["name"] |
|
|
|
|
| def test_geocode_falls_back_to_geography_when_nothing_else(monkeypatch, fake_response, fake_keys): |
| payload = { |
| "results": [ |
| { |
| "type": "Geography", |
| "position": {"lat": 26.8467, "lon": 80.9462}, |
| "address": {"freeformAddress": "Lucknow, Uttar Pradesh"}, |
| } |
| ] |
| } |
| monkeypatch.setattr(geocode.requests, "get", lambda *a, **k: fake_response(payload)) |
| result = geocode.geocode_place("Lucknow") |
| assert result["lat"] == 26.8467 |
|
|
|
|
| def test_geocode_rejects_results_outside_lucknow(monkeypatch, fake_response, fake_keys): |
| payload = { |
| "results": [ |
| { |
| "type": "POI", |
| "position": {"lat": 28.6139, "lon": 77.2090}, |
| "address": {"freeformAddress": "Connaught Place, Delhi"}, |
| "poi": {"name": "Some Place Delhi"}, |
| } |
| ] |
| } |
| monkeypatch.setattr(geocode.requests, "get", lambda *a, **k: fake_response(payload)) |
| with pytest.raises(RuntimeError, match="No location found"): |
| geocode.geocode_place("Some Place") |
|
|
|
|
| def test_geocode_no_double_city_suffix(monkeypatch, fake_response, fake_keys): |
| captured = {"urls": []} |
|
|
| def fake_get(url, params=None, timeout=None): |
| captured["urls"].append(url) |
| return fake_response( |
| { |
| "results": [ |
| { |
| "type": "POI", |
| "position": {"lat": 26.80, "lon": 81.02}, |
| "address": {"freeformAddress": "IIIT Lucknow"}, |
| "poi": {"name": "IIIT Lucknow"}, |
| } |
| ] |
| } |
| ) |
|
|
| monkeypatch.setattr(geocode.requests, "get", fake_get) |
| geocode.geocode_place("iiit lucknow") |
| |
| assert "Uttar%20Pradesh" not in captured["urls"][0] |
|
|
|
|
| |
|
|
| def test_festivals_merges_calendarific_and_curated(monkeypatch, fake_response, fake_keys): |
| calendarific_payload = { |
| "meta": {"code": 200}, |
| "response": { |
| "holidays": [ |
| { |
| "name": "Some National Holiday", |
| "description": "A big holiday", |
| "date": {"iso": "2026-06-09"}, |
| "type": ["National holiday"], |
| } |
| ] |
| }, |
| } |
| monkeypatch.setattr( |
| festivals.requests, "get", lambda *a, **k: fake_response(calendarific_payload) |
| ) |
|
|
| |
| result = festivals.get_festivals("2026-06-09") |
| names = [festival["name"] for festival in result["festivals"]] |
| assert "Some National Holiday" in names |
| assert "Bada Mangal" in names |
| assert result["impact"] == 2 |
|
|
|
|
| def test_festivals_survives_api_failure(monkeypatch, fake_keys): |
| def boom(*args, **kwargs): |
| raise requests_lib.ConnectionError("network down") |
|
|
| monkeypatch.setattr(festivals.requests, "get", boom) |
| result = festivals.get_festivals("2026-03-02") |
| assert result["festivals"] == [] |
| assert result["impact"] == 0 |
|
|
|
|
| |
|
|
| def test_events_classifies_stadium_as_high_impact(monkeypatch, fake_response, fake_keys): |
| payload = { |
| "_embedded": { |
| "events": [ |
| { |
| "name": "IPL Match", |
| "dates": {"start": {"localTime": "19:30:00"}}, |
| "_embedded": {"venues": [{"name": "Ekana Cricket Stadium"}]}, |
| } |
| ] |
| } |
| } |
| monkeypatch.setattr(events.requests, "get", lambda *a, **k: fake_response(payload)) |
|
|
| result = events.get_events("2026-06-12") |
| assert result["impact"] == 2 |
| assert result["events"][0]["traffic_impact"] == "high" |
|
|
|
|
| def test_events_empty_when_api_fails(monkeypatch, fake_keys): |
| def boom(*args, **kwargs): |
| raise requests_lib.Timeout("slow") |
|
|
| monkeypatch.setattr(events.requests, "get", boom) |
| result = events.get_events("2026-06-12") |
| assert result == {"events": [], "impact": 0} |
|
|
|
|
| def test_events_empty_without_key(monkeypatch): |
| from app import config |
|
|
| monkeypatch.setattr(config, "ticketmaster_key", lambda: None) |
| result = events.get_events("2026-06-12") |
| assert result == {"events": [], "impact": 0} |
|
|
|
|
| |
|
|
| def test_advisories_parses_relevant_results(monkeypatch, fake_response): |
| html = """ |
| <a class="result__a" href="#">Lucknow traffic police announces route diversion for procession</a> |
| <a class="result__snippet" href="#">Roads near Chowk will remain closed on Friday evening...</a> |
| <a class="result__a" href="#">Weather in Mumbai today</a> |
| <a class="result__snippet" href="#">Sunny skies expected across the city</a> |
| """ |
| monkeypatch.setattr( |
| advisories.requests, "post", lambda *a, **k: fake_response(text=html) |
| ) |
|
|
| result = advisories.get_police_advisories() |
| assert result["count"] == 1 |
| assert "diversion" in result["advisories"][0]["title"].lower() |
| assert result["advisories"][0]["title"].startswith("News:") |
|
|
|
|
| def test_advisories_searches_past_week_and_unescapes(monkeypatch, fake_response): |
| captured = {} |
|
|
| def fake_post(url, data=None, headers=None, timeout=None): |
| captured["data"] = data |
| html = ( |
| '<a class="result__a" href="#">Lucknow "Bada Mangal" route diversion advisory</a>' |
| '<a class="result__snippet" href="#">Roads closed near Aliganj & Hazratganj</a>' |
| ) |
| return fake_response(text=html) |
|
|
| monkeypatch.setattr(advisories.requests, "post", fake_post) |
| result = advisories.get_police_advisories() |
|
|
| assert captured["data"]["df"] == "w" |
| assert '"' in result["advisories"][0]["title"] |
| assert """ not in result["advisories"][0]["title"] |
| assert "&" not in result["advisories"][0]["detail"] |
|
|
|
|
| def test_advisories_empty_on_network_failure(monkeypatch): |
| def boom(*args, **kwargs): |
| raise requests_lib.ConnectionError("blocked") |
|
|
| monkeypatch.setattr(advisories.requests, "post", boom) |
| result = advisories.get_police_advisories() |
| assert result == {"advisories": [], "count": 0} |
|
|