Skip to content

Commit

Permalink
Refactoring crime incidents, moved former unit test into integration …
Browse files Browse the repository at this point in the history
…tests

  Closes #194
  • Loading branch information
wesdrew committed Feb 8, 2019
1 parent 97a265c commit 26926f1
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import unittest
import unittest.mock as mock
import requests
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'])
39 changes: 19 additions & 20 deletions mycity/mycity/test/unit_tests/test_crime_incidents_api_utils.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
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
import requests
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
class CrimeIncidentsAPIUtilitiesTestCase(unittest.TestCase):

test_address = "46 Everdean St Boston, MA"
result = get_crime_incident_response(test_address)
self.assertEqual(
True,
result['success']
)
def test_get_crime_incident_response_returns_empty_dict_if_request_fails(self):
test_session = mock.MagicMock()
test_session.response = mock.MagicMock()
test_session.response.status_code = requests.codes.bad
to_test = get_crime_incident_response('46 Everdean St.', test_session)
self.assertEquals({}, to_test)

def test_get_crime_incident_response_returns_json_if_request_succeeds(self):
expected = 1
test_session = mock.MagicMock()
test_session.response = mock.MagicMock()
test_session.response.status_code = requests.codes.ok
test_session.response.json = lambda: expected
to_test = get_crime_incident_response('46 Everdean St.', test_session)
self.assertEquals(expected, to_test)
35 changes: 23 additions & 12 deletions mycity/mycity/utilities/crime_incidents_api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@
"""

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, test_session: typing.ClassVar = None):
"""
Executes and returns the crime incident request response
Expand All @@ -26,38 +30,45 @@ def get_crime_incident_response(address):
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:
response = session.get(CRIME_INCIDENTS_SQL_URL, params=url_parameters)
if test_session is None:
response = session.get(CRIME_INCIDENTS_SQL_URL, params=url_parameters)
else:
response = test_session.response

if response.status_code == requests.codes.ok:
return response.json()
return {}


def _build_query_string(address):
def _build_query_string(address: str, test_get_coordinates: typing.Callable[[str], list] = None)-> str:
"""
Builds the SQL query given an address
:param address: address to query
:param test_get_coordinates: injectable function for test
:return: a SQL query string
"""
coordinates = _get_coordinates_for_address(address)
coordinates = _get_coordinates_for_address(address) \
if test_get_coordinates is None else test_get_coordinates(address)
return """SELECT * FROM "{}" WHERE "lat" LIKE '{}%' AND \
"long" LIKE '{}%' LIMIT {}""" \
.format(RESOURCEID, coordinates[0], coordinates[1], QUERY_LIMIT)
.format(RESOURCE_ID, coordinates[0], coordinates[1], QUERY_LIMIT)


def _get_coordinates_for_address(address):
def _get_coordinates_for_address(address: str, test_geocode_address: typing.Callable[[str], list] = None)-> tuple:
"""
Populates the GPS coordinates for the provided address
:param address: address to query
:param test_geocode_address: injectable function for test
:return: a tuple of the form (lat, long)
"""
coordinates = geocode_address(address)
coordinates = geocode_address(address) if test_geocode_address is None else test_geocode_address(address)
logger.debug("Got coordinates: {}".format(coordinates))
_lat = "{:.2f}".format(float(coordinates[1]))
_long = "{:.2f}".format(float(coordinates[0]))
return (_lat, _long)
_lat = "{:.2f}".format(float(coordinates[LAT_INDEX]))
_long = "{:.2f}".format(float(coordinates[LONG_INDEX]))
return _lat, _long

0 comments on commit 26926f1

Please sign in to comment.