# ============================================================================== # Author: Simon-Pierre Boucher # File: tests/test_yelp_scrape.py # Desc: Connecteur yelp-scrape (avis/notes sans clé API) — parsing HORS # LIGNE du cache Apollo embarqué dans une page de recherche yelp.ca # (scripts data-apollo-state HTML-échappés) et croisement CONSERVATEUR # nom + civique + ville (ambigu ou fermé = ignoré). # ============================================================================== import html import json from restoka.connectors.yelp_scrape import (_city_key, match_business, parse_apollo_businesses) # Cache Apollo minimal, encodé comme sur yelp.ca (" dans un commentaire) _CACHE = { "BusinessCategory:cat1": {"__typename": "BusinessCategory", "encid": "cat1", "title": "Poutineries"}, "BusinessLocation:loc1": { "__typename": "BusinessLocation", "encid": "loc1", "address": {"__typename": "BusinessAddress", "city": "Quebec City", "addressLine1": "640 Grande Allée E"}, "neighborhoods": [], }, "Business:biz1": { "__typename": "Business", "encid": "biz1", "alias": "chez-ashton-québec-2", "name": "Chez Ashton", "rating": 3.5, "reviewCount": 42, "categories": [{"__ref": "BusinessCategory:cat1"}], "priceRange": {"__typename": "PriceRange", "display": "$"}, "location": {"__ref": "BusinessLocation:loc1"}, 'activeAlert({"deviceType":"WWW"})': None, }, "BusinessLocation:loc2": { "__typename": "BusinessLocation", "encid": "loc2", "address": {"__typename": "BusinessAddress", "city": "Levis", "addressLine1": "5430 Rue Wilfrid-Hallé"}, "neighborhoods": [], }, "Business:biz2": { # fermé définitivement -> jamais croisé "__typename": "Business", "encid": "biz2", "alias": "chez-ashton-levis", "name": "Chez Ashton", "rating": 4.0, "reviewCount": 3, "categories": [], "priceRange": None, "location": {"__ref": "BusinessLocation:loc2"}, 'activeAlert({"deviceType":"WWW"})': {"__typename": "BusinessAlert", "type": "permclosed"}, }, } def _page(cache: dict) -> str: blob = html.escape(json.dumps(cache, ensure_ascii=False), quote=True) return ('') def test_parse_apollo_businesses(): biz = parse_apollo_businesses(_page(_CACHE)) assert len(biz) == 2 b1 = next(b for b in biz if b["alias"] == "chez-ashton-québec-2") assert b1["rating"] == 3.5 and b1["review_count"] == 42 assert b1["price"] == "$" and b1["categories"] == ["Poutineries"] assert b1["city"] == "Quebec City" assert b1["address"] == "640 Grande Allée E" assert b1["closed"] is False b2 = next(b for b in biz if b["alias"] == "chez-ashton-levis") assert b2["closed"] is True def test_city_key(): assert _city_key("Quebec City") == "quebec" assert _city_key("Québec") == "quebec" assert _city_key("Montréal") == "montreal" assert _city_key("Saint-Nicolas") == "saint nicolas" def test_match_business_conservateur(): candidates = parse_apollo_businesses(_page(_CACHE)) resto = {"name": "Chez Ashton", "city": "Québec", "address": "640 Grande Allée Est"} hit = match_business(resto, candidates) assert hit and hit["alias"] == "chez-ashton-québec-2" # mauvais civique -> aucun croisement (voisin de la même rue) assert match_business({"name": "Chez Ashton", "city": "Québec", "address": "700 Grande Allée Est"}, candidates) is None # le permclosed de Lévis n'est jamais retenu, même ville exacte assert match_business({"name": "Chez Ashton", "city": "Lévis", "address": "5430 Rue Wilfrid-Hallé"}, candidates) is None # ambiguïté (deux commerces distincts qui passent) -> None cache2 = dict(_CACHE) cache2["Business:biz3"] = dict(_CACHE["Business:biz1"], encid="biz3", alias="ashton-restaurant", name="Restaurant Ashton") cands2 = parse_apollo_businesses(_page(cache2)) assert match_business(resto, cands2) is None