gendesign/backend/tests/test_osrm_client_local.py
bot-backend f48db87d39
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / deploy (push) Successful in 1m15s
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 1m40s
Deploy / build-worker (push) Successful in 2m43s
feat(site-finder): OSRM road-distance in /analyze behind flag (#39 A2) (#1932)
2026-06-26 21:25:50 +00:00

126 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Тесты локального OSRM-клиента (#39 A2) — /table annotations=distance.
Сеть НЕ дёргается: подменяем httpx.Client на MockTransport. Покрывает:
- корректный URL (sources=0, annotations=distance, координаты в path);
- парсинг distances в МЕТРАХ + дроп self-index (row[0]);
- None для недостижимого POI;
- OsrmLocalUnavailableError на 500 / timeout / code!=Ok / битой форме;
- пустой destinations → [].
"""
from __future__ import annotations
import httpx
import pytest
from app.services.site_finder import osrm_client_local as osrm
def _install_transport(monkeypatch: pytest.MonkeyPatch, handler) -> None:
"""Подменить httpx.Client в модуле osrm_client_local на MockTransport(handler).
Захватываем реальный httpx.Client ДО патча — иначе factory рекурсивно зовёт сама себя.
"""
real_client = httpx.Client
def client_factory(*_args, **kwargs):
kwargs.pop("transport", None)
return real_client(transport=httpx.MockTransport(handler), **kwargs)
monkeypatch.setattr(osrm.httpx, "Client", client_factory)
def test_builds_correct_url_and_parses_meters(monkeypatch):
"""URL: /table/v1/driving/{coords}?sources=0&annotations=distance; distances в метрах."""
seen: dict = {}
def handler(request: httpx.Request) -> httpx.Response:
seen["path"] = request.url.path
seen["params"] = dict(request.url.params)
# distances[0] = origin→[origin, d1, d2] — метры; self = 0.
return httpx.Response(200, json={"code": "Ok", "distances": [[0.0, 1234.5, 6789.0]]})
_install_transport(monkeypatch, handler)
out = osrm.get_road_distances_m(
60.6, 56.8, [(60.61, 56.81), (60.62, 56.82)]
)
# Дорожные расстояния (метры), self-index сброшен.
assert out == [1234.5, 6789.0]
# URL: профиль driving, координаты origin первой (lon,lat) и POI.
assert seen["path"] == "/table/v1/driving/60.6,56.8;60.61,56.81;60.62,56.82"
# Параметры sources=0, annotations=distance.
assert seen["params"]["sources"] == "0"
assert seen["params"]["annotations"] == "distance"
def test_unreachable_poi_returns_none(monkeypatch):
"""OSRM вернул null (нет маршрута) → None в результате (caller оставит straight-line)."""
def handler(_request):
return httpx.Response(200, json={"code": "Ok", "distances": [[0.0, 500.0, None]]})
_install_transport(monkeypatch, handler)
out = osrm.get_road_distances_m(60.6, 56.8, [(60.61, 56.81), (60.62, 56.82)])
assert out == [500.0, None]
def test_empty_destinations_returns_empty():
"""Пустой destinations → [] без HTTP-вызова."""
assert osrm.get_road_distances_m(60.6, 56.8, []) == []
def test_http_500_raises_unavailable(monkeypatch):
"""HTTP 500 → OsrmLocalUnavailableError (не пробрасываем HTTPStatusError)."""
def handler(_request):
return httpx.Response(500, text="boom")
_install_transport(monkeypatch, handler)
with pytest.raises(osrm.OsrmLocalUnavailableError, match="HTTP 500"):
osrm.get_road_distances_m(60.6, 56.8, [(60.61, 56.81)])
def test_timeout_raises_unavailable(monkeypatch):
"""Timeout/сетевой сбой → OsrmLocalUnavailableError."""
def handler(_request):
raise httpx.ConnectTimeout("timed out")
_install_transport(monkeypatch, handler)
with pytest.raises(osrm.OsrmLocalUnavailableError, match="request failed"):
osrm.get_road_distances_m(60.6, 56.8, [(60.61, 56.81)])
def test_code_not_ok_raises_unavailable(monkeypatch):
"""code != Ok (например NoRoute) → OsrmLocalUnavailableError."""
def handler(_request):
return httpx.Response(200, json={"code": "NoRoute", "distances": None})
_install_transport(monkeypatch, handler)
with pytest.raises(osrm.OsrmLocalUnavailableError, match="code != Ok"):
osrm.get_road_distances_m(60.6, 56.8, [(60.61, 56.81)])
def test_bad_shape_raises_unavailable(monkeypatch):
"""Ответ без distances → OsrmLocalUnavailableError."""
def handler(_request):
return httpx.Response(200, json={"code": "Ok", "unexpected": True})
_install_transport(monkeypatch, handler)
with pytest.raises(osrm.OsrmLocalUnavailableError):
osrm.get_road_distances_m(60.6, 56.8, [(60.61, 56.81)])
def test_length_mismatch_raises_unavailable(monkeypatch):
"""Число расстояний != числу destinations → OsrmLocalUnavailableError (рассинхрон)."""
def handler(_request):
# 2 destinations, но distances содержит только self + 1.
return httpx.Response(200, json={"code": "Ok", "distances": [[0.0, 500.0]]})
_install_transport(monkeypatch, handler)
with pytest.raises(osrm.OsrmLocalUnavailableError, match="!= destinations"):
osrm.get_road_distances_m(60.6, 56.8, [(60.61, 56.81), (60.62, 56.82)])