"""상호 → 네이버 플레이스 id 해석.""" import pytest from services.external import naver_place_lookup as lookup # 항목 사이를 채우는 잡음. _FILLER = "
방문자 리뷰 블로그 리뷰 사진 더보기
" * 40 def _html(*entries: tuple[str, str]) -> str: """검색 결과 원문 흉내.""" parts = [] for name, place_id in entries: parts.append( _FILLER + f'
  • ' f'{name}' f'주차하기 편해요
  • ' + _FILLER ) return "" def test_ampersand_in_name_is_matched_through_the_entity(): """검증: 상호에 `&` 가 있고 원문에는 `&` 로 인코딩돼 있다.""" html = _html(("누에베 풀빌라&리조트", "1064005604")) assert lookup._match_in_html(html, "누에베 풀빌라&리조트") == "1064005604" def test_plain_name_still_matches(): """검증: `&` 도 엔티티도 없는 평범한 상호.""" html = _html(("보사노바 커피로스터스 강릉점", "37093035")) assert lookup._match_in_html(html, "보사노바 커피로스터스 강릉점") == "37093035" def test_punctuation_differences_are_ignored(): """검증: 네이버는 '스테이,머뭄' 처럼 구두점을 넣어 표기한다.""" html = _html(("스테이, 머뭄", "1133638931")) assert lookup._match_in_html(html, "스테이머뭄") == "1133638931" assert lookup._match_in_html(html, "스테이,머뭄") == "1133638931" def test_unrelated_name_is_not_matched(): """검증: 원문에 id 는 있지만 그 상호는 없다.""" html = _html(("통나무파크", "13149475"), ("도치돌알파카목장", "11111111")) assert lookup._match_in_html(html, "제주양떼목장") is None def test_correct_id_is_picked_among_several(): """검증: 후보가 여럿 섞인 원문에서 특정 상호를 찾는다.""" html = _html( ("통나무파크입구", "99999999"), ("통나무파크 전기차충전소", "88888888"), ("누에베 풀빌라&리조트", "1064005604"), ) assert lookup._match_in_html(html, "누에베 풀빌라&리조트") == "1064005604" def test_empty_name_never_matches(): """검증: 상호가 빈 문자열이다.""" html = _html(("통나무파크", "13149475")) assert lookup._match_in_html(html, "") is None assert lookup._match_in_html(html, " ") is None async def test_find_place_ids_returns_only_the_ones_it_found(monkeypatch): """검증: 후보 3건 중 2건만 원문에 있다.""" calls: list[str] = [] async def _fake_fetch(query: str): calls.append(query) return _html(("누에베 풀빌라&리조트", "1064005604"), ("통나무파크", "13149475")) monkeypatch.setattr(lookup, "_fetch_search_html", _fake_fetch) found = await lookup.find_place_ids( "누에베 풀빌라 제주 애월읍", ["누에베 풀빌라&리조트", "통나무파크", "여기없는가게"], ) assert found == {"누에베 풀빌라&리조트": "1064005604", "통나무파크": "13149475"} assert calls == ["누에베 풀빌라 제주 애월읍"], "후보 수만큼 부르면 네이버가 429 로 막는다" async def test_find_place_ids_is_empty_when_search_fails(monkeypatch): async def _fake_fetch(query: str): return None monkeypatch.setattr(lookup, "_fetch_search_html", _fake_fetch) assert await lookup.find_place_ids("아무거나", ["가게"]) == {} @pytest.mark.parametrize( "raw, expected", [ ("누에베 풀빌라&리조트", "누에베풀빌라리조트"), ("스테이, 머뭄", "스테이머뭄"), ("A-1 Cafe (본점)", "a1cafe본점"), (None, ""), ], ) def test_normalize(raw, expected): """검증: 비교용 정규화 규칙.""" assert lookup._normalize(raw) == expected def test_nearby_entries_can_steal_the_id__known_limit(): """검증: 두 업소가 조회 창(앞 600자 + 뒤 300자)보다 가깝게 붙어 있다.""" dense = ( '
  • 앞가게
  • ' '
  • 뒷가게
  • ' ) assert lookup._match_in_html(dense, "뒷가게") == "11111111"