diff --git a/mycity/mycity/test/integration_tests/test_crime_incidents_api_utils.py b/mycity/mycity/test/integration_tests/test_crime_incidents_api_utils.py new file mode 100644 index 00000000..ea5a214f --- /dev/null +++ b/mycity/mycity/test/integration_tests/test_crime_incidents_api_utils.py @@ -0,0 +1,10 @@ +import unittest +from mycity.utilities.crime_incidents_api_utils import get_crime_incident_response + + +class CrimeIncidentsAPIUtilitiesTestCase(unittest.TestCase): + + def test_get_crime_incident_response_returns_success_if_query_is_successful(self): + test_address = "46 Everdean St Boston, MA" + result = get_crime_incident_response(test_address) + self.assertEqual(True, result['success']) diff --git a/mycity/mycity/test/unit_tests/test_crime_incidents_api_utils.py b/mycity/mycity/test/unit_tests/test_crime_incidents_api_utils.py index 58d59c9e..dfa07de8 100644 --- a/mycity/mycity/test/unit_tests/test_crime_incidents_api_utils.py +++ b/mycity/mycity/test/unit_tests/test_crime_incidents_api_utils.py @@ -1,24 +1,35 @@ +import unittest import unittest.mock as mock -import mycity.test.test_constants as test_constants -import mycity.test.unit_tests.base as base -from mycity.utilities.crime_incidents_api_utils import \ - get_crime_incident_response - -class CrimeIncidentsAPIUtilitiesTestCase(base.BaseTestCase): - - @mock.patch( - 'mycity.utilities.gis_utils.geocode_address', - return_value=test_constants.GEOCODE_ADDRESS_MOCK - ) - @mock.patch('requests.get') - def test_get_crime_incident_response(self, mock_geocode_address, mock_get): - mock_resp = self._mock_response(status=200, - json_data=test_constants.GET_CRIME_INCIDENTS_API_MOCK) - mock_get.return_value = mock_resp - - test_address = "46 Everdean St Boston, MA" - result = get_crime_incident_response(test_address) - self.assertEqual( - True, - result['success'] - ) +import requests +import typing +from mycity.utilities.crime_incidents_api_utils import get_crime_incident_response + + +class ResponseStub: + + def __init__(self, status_code: int, response_data: typing.Dict = {}): + self.status_code = status_code + self.response_data = response_data + + def json(self): + return self.response_data + + +class CrimeIncidentsAPIUtilitiesTestCase(unittest.TestCase): + + def test_get_crime_incident_response_returns_empty_dict_if_request_fails(self): + requests_stub = _build_requests_stub(requests.codes.bad) + to_test = get_crime_incident_response('46 Everdean St.', requests_stub) + self.assertEquals({}, to_test) + + def test_get_crime_incident_response_returns_json_if_request_succeeds(self): + expected = {'test': 'yes, it\'s a test'} + requests_stub = _build_requests_stub(requests.codes.ok, expected) + to_test = get_crime_incident_response('46 Everdean St.', requests_stub) + self.assertEquals(expected, to_test) + + +def _build_requests_stub(status_code: int, data_from_service: typing.Dict = {}): + requests_stub = requests + requests_stub.Session.get = mock.MagicMock(return_value = ResponseStub(status_code, data_from_service)) + return requests_stub diff --git a/mycity/mycity/utilities/crime_incidents_api_utils.py b/mycity/mycity/utilities/crime_incidents_api_utils.py index cc1bcb44..56f19998 100644 --- a/mycity/mycity/utilities/crime_incidents_api_utils.py +++ b/mycity/mycity/utilities/crime_incidents_api_utils.py @@ -4,60 +4,71 @@ """ import requests -from mycity.utilities.gis_utils import geocode_address +import typing import logging +from mycity.utilities.gis_utils import geocode_address + -RESOURCEID = "12cb3883-56f5-47de-afa5-3b1cf61b257b" +RESOURCE_ID = "12cb3883-56f5-47de-afa5-3b1cf61b257b" QUERY_LIMIT = 5 CRIME_INCIDENTS_SQL_URL = \ "https://data.boston.gov/api/3/action/datastore_search_sql" +LONG_INDEX = 0 +LAT_INDEX = 1 logger = logging.getLogger(__name__) -def get_crime_incident_response(address): +def get_crime_incident_response(address: str, _requests: typing.ClassVar = requests): """ Executes and returns the crime incident request response - :param address: address to query + :param address: address to query + :param _requests: Injectable request class :return: the raw json response """ url_parameters = {"sql": _build_query_string(address)} - logger.debug("Finding crime incidents information for {} using query {}" - .format(address, url_parameters)) - with requests.Session() as session: + logger.debug("Finding crime incidents information for {} using query {}".format(address, url_parameters)) + + with _requests.Session() as session: response = session.get(CRIME_INCIDENTS_SQL_URL, params=url_parameters) - if response.status_code == requests.codes.ok: + if response.status_code == _requests.codes.ok: return response.json() return {} -def _build_query_string(address): +def _get_coordinates_for_address( + address: str, + _geocode_address: typing.Callable[[str], list] = geocode_address)-> tuple: """ - Builds the SQL query given an address + Populates the GPS coordinates for the provided address - :param address: address to query - :return: a SQL query string + :param address: address to query + :param _geocode_address: injectable function for test + :return: a tuple of the form (lat, long) """ - coordinates = _get_coordinates_for_address(address) - return """SELECT * FROM "{}" WHERE "lat" LIKE '{}%' AND \ - "long" LIKE '{}%' LIMIT {}""" \ - .format(RESOURCEID, coordinates[0], coordinates[1], QUERY_LIMIT) + coordinates = _geocode_address(address) + logger.debug("Got coordinates: {}".format(coordinates)) + _lat = "{:.2f}".format(float(coordinates[LAT_INDEX])) + _long = "{:.2f}".format(float(coordinates[LONG_INDEX])) + return _lat, _long -def _get_coordinates_for_address(address): +def _build_query_string( + address: str, + _get_coordinates_for_address: typing.Callable[[str], list] = _get_coordinates_for_address)-> str: """ - Populates the GPS coordinates for the provided address + Builds the SQL query given an address - :param address: address to query - :return: a tuple of the form (lat, long) + :param address: address to query + :param _get_coordinates_for_address: injectable function for test + :return: a SQL query string """ - coordinates = geocode_address(address) - logger.debug("Got coordinates: {}".format(coordinates)) - _lat = "{:.2f}".format(float(coordinates[1])) - _long = "{:.2f}".format(float(coordinates[0])) - return (_lat, _long) \ No newline at end of file + coordinates = _get_coordinates_for_address(address) + return """SELECT * FROM "{}" WHERE "lat" LIKE '{}%' AND \ + "long" LIKE '{}%' LIMIT {}""" \ + .format(RESOURCE_ID, coordinates[0], coordinates[1], QUERY_LIMIT)