From 63e8e94145a1f5ef9da0c3cad5918dc04caa1acd Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 31 Jan 2021 19:20:17 +0100 Subject: [PATCH] Add `Pixel.restaurants` property - the property loads all `Restaurant`s from the database that are within the `Pixel` --- setup.cfg | 3 +++ src/urban_meal_delivery/db/pixels.py | 18 +++++++++++++ tests/db/test_pixels.py | 39 ++++++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 46e2db8..83d92d5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -172,6 +172,9 @@ per-file-ignores = WPS202,WPS204,WPS214, # Do not check for Jones complexity in the test suite. WPS221, + # "Private" methods are really just a convention for + # fixtures without a return value. + WPS338, # We do not care about the number of "# noqa"s in the test suite. WPS402, # Allow closures. diff --git a/src/urban_meal_delivery/db/pixels.py b/src/urban_meal_delivery/db/pixels.py index f5ca091..5876f19 100644 --- a/src/urban_meal_delivery/db/pixels.py +++ b/src/urban_meal_delivery/db/pixels.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import List + import folium import sqlalchemy as sa import utm @@ -111,6 +113,22 @@ class Pixel(meta.Base): return self._southwest + @property + def restaurants(self) -> List[db.Restaurant]: # pragma: no cover + """Obtain all `Restaurant`s in `self`.""" + if not hasattr(self, '_restaurants'): # noqa:WPS421 note:d334120e + self._restaurants = ( # noqa:ECE001 + db.session.query(db.Restaurant) + .join( + db.AddressPixelAssociation, + db.Restaurant.address_id == db.AddressPixelAssociation.address_id, + ) + .filter(db.AddressPixelAssociation.pixel_id == self.id) + .all() + ) + + return self._restaurants + def clear_map(self) -> Pixel: # pragma: no cover """Shortcut to the `.city.clear_map()` method. diff --git a/tests/db/test_pixels.py b/tests/db/test_pixels.py index d5acc4a..ed7bbec 100644 --- a/tests/db/test_pixels.py +++ b/tests/db/test_pixels.py @@ -88,7 +88,7 @@ class TestProperties: assert result == 1.0 - def test_northeast(self, pixel, city): + def test_northeast(self, pixel): """Test `Pixel.northeast` property.""" result = pixel.northeast @@ -102,7 +102,7 @@ class TestProperties: assert result1 is result2 - def test_southwest(self, pixel, city): + def test_southwest(self, pixel): """Test `Pixel.southwest` property.""" result = pixel.southwest @@ -115,3 +115,38 @@ class TestProperties: result2 = pixel.southwest assert result1 is result2 + + @pytest.fixture + def _restaurants_mock(self, mocker, monkeypatch, restaurant): + """A `Mock` whose `.return_value` is `[restaurant]`.""" + mock = mocker.Mock() + query = ( # noqa:ECE001 + mock.query.return_value.join.return_value.filter.return_value.all # noqa:E501,WPS219 + ) + query.return_value = [restaurant] + monkeypatch.setattr(db, 'session', mock) + + @pytest.mark.usefixtures('_restaurants_mock') + def test_restaurants(self, pixel, restaurant): + """Test `Pixel.restaurants` property.""" + result = pixel.restaurants + + assert result == [restaurant] + + @pytest.mark.usefixtures('_restaurants_mock') + def test_restaurants_is_cached(self, pixel): + """Test `Pixel.restaurants` property.""" + result1 = pixel.restaurants + result2 = pixel.restaurants + + assert result1 is result2 + + @pytest.mark.db + def test_restaurants_with_db(self, pixel): + """Test `Pixel.restaurants` property. + + This is a trivial integration test. + """ + result = pixel.restaurants + + assert not result # = empty `list`