from unittest.mock import MagicMock, patch from odoo.tests.common import TransactionCase from ..models.base_geocoder import ( NOMINATIM_MIN_INTERVAL, USER_AGENT_PARAM, USER_AGENT_PLACEHOLDER, ) _MODULE = "odoo.addons.partner_geolocalize_usability.models.base_geocoder" def _fake_response(payload, status_code=200): response = MagicMock() response.status_code = status_code response.json.return_value = payload return response class TestBaseGeocoder(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.geocoder = cls.env["base.geocoder"] cls.Param = cls.env["ir.config_parameter"].sudo() def test_uses_configured_user_agent(self): # A deployment-specific User-Agent must be sent to Nominatim. self.Param.set_param(USER_AGENT_PARAM, "acme/1.0 (contact: x@acme.test)") with patch(f"{_MODULE}.time.sleep"), patch( f"{_MODULE}.requests.get", return_value=_fake_response([{"lat": "1.5", "lon": "2.5"}]), ) as mocked_get: result = self.geocoder._call_openstreetmap("some address") self.assertEqual(result, (1.5, 2.5)) _, kwargs = mocked_get.call_args self.assertEqual( kwargs["headers"]["User-Agent"], "acme/1.0 (contact: x@acme.test)" ) def test_falls_back_to_placeholder_user_agent(self): # Without configuration, the obvious placeholder is used (never Odoo's # banned generic agent). self.Param.set_param(USER_AGENT_PARAM, False) with patch(f"{_MODULE}.time.sleep"), patch( f"{_MODULE}.requests.get", return_value=_fake_response([{"lat": "1.0", "lon": "2.0"}]), ) as mocked_get: self.geocoder._call_openstreetmap("addr") _, kwargs = mocked_get.call_args self.assertEqual( kwargs["headers"]["User-Agent"], USER_AGENT_PLACEHOLDER ) def test_throttles_before_request(self): # The 1 req/s policy is enforced by sleeping before each call. self.Param.set_param(USER_AGENT_PARAM, "acme/1.0 (contact: x@acme.test)") with patch(f"{_MODULE}.time.sleep") as mocked_sleep, patch( f"{_MODULE}.requests.get", return_value=_fake_response([{"lat": "1.0", "lon": "2.0"}]), ): self.geocoder._call_openstreetmap("addr") mocked_sleep.assert_called_once_with(NOMINATIM_MIN_INTERVAL) def test_empty_result_returns_none(self): # An unknown address (empty Nominatim result) must not raise IndexError. self.Param.set_param(USER_AGENT_PARAM, "acme/1.0 (contact: x@acme.test)") with patch(f"{_MODULE}.time.sleep"), patch( f"{_MODULE}.requests.get", return_value=_fake_response([]) ): result = self.geocoder._call_openstreetmap("nowhere at all") self.assertIsNone(result) def test_blank_address_returns_none_without_request(self): # Empty address short-circuits: no HTTP call, no throttle. with patch(f"{_MODULE}.time.sleep") as mocked_sleep, patch( f"{_MODULE}.requests.get" ) as mocked_get: result = self.geocoder._call_openstreetmap("") self.assertIsNone(result) mocked_get.assert_not_called() mocked_sleep.assert_not_called()