Refactor tests.db.fake_data.factories
into a package
This commit is contained in:
parent
7e23033d84
commit
f891fac3dc
10 changed files with 236 additions and 172 deletions
|
@ -167,6 +167,8 @@ per-file-ignores =
|
||||||
S311,
|
S311,
|
||||||
# Shadowing outer scopes occurs naturally with mocks.
|
# Shadowing outer scopes occurs naturally with mocks.
|
||||||
WPS442,
|
WPS442,
|
||||||
|
# `utils` should be a valid module name.
|
||||||
|
WPS100,
|
||||||
# Test names may be longer than 40 characters.
|
# Test names may be longer than 40 characters.
|
||||||
WPS118,
|
WPS118,
|
||||||
# Modules may have many test cases.
|
# Modules may have many test cases.
|
||||||
|
|
8
tests/db/fake_data/factories/__init__.py
Normal file
8
tests/db/fake_data/factories/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
"""Factories to create instances for the SQLAlchemy models."""
|
||||||
|
|
||||||
|
from tests.db.fake_data.factories.addresses import AddressFactory
|
||||||
|
from tests.db.fake_data.factories.couriers import CourierFactory
|
||||||
|
from tests.db.fake_data.factories.customers import CustomerFactory
|
||||||
|
from tests.db.fake_data.factories.orders import AdHocOrderFactory
|
||||||
|
from tests.db.fake_data.factories.orders import ScheduledOrderFactory
|
||||||
|
from tests.db.fake_data.factories.restaurants import RestaurantFactory
|
40
tests/db/fake_data/factories/addresses.py
Normal file
40
tests/db/fake_data/factories/addresses.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"""Factory to create `Address` instances."""
|
||||||
|
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
import factory
|
||||||
|
from factory import alchemy
|
||||||
|
|
||||||
|
from tests.db.fake_data.factories import utils
|
||||||
|
from urban_meal_delivery import db
|
||||||
|
|
||||||
|
|
||||||
|
class AddressFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
|
"""Create instances of the `db.Address` model."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = db.Address
|
||||||
|
sqlalchemy_get_or_create = ('id',)
|
||||||
|
|
||||||
|
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
||||||
|
created_at = factory.LazyFunction(utils.early_in_the_morning)
|
||||||
|
|
||||||
|
# When testing, all addresses are considered primary ones.
|
||||||
|
# As non-primary addresses have no different behavior and
|
||||||
|
# the property is only kept from the original dataset for
|
||||||
|
# completeness sake, that is ok to do.
|
||||||
|
primary_id = factory.LazyAttribute(lambda obj: obj.id)
|
||||||
|
|
||||||
|
# Mimic a Google Maps Place ID with just random characters.
|
||||||
|
place_id = factory.LazyFunction(
|
||||||
|
lambda: ''.join(random.choice(string.ascii_lowercase) for _ in range(20)),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Place the addresses somewhere in downtown Paris.
|
||||||
|
latitude = factory.Faker('coordinate', center=48.855, radius=0.01)
|
||||||
|
longitude = factory.Faker('coordinate', center=2.34, radius=0.03)
|
||||||
|
# city -> set by the `make_address` fixture as there is only one `city`
|
||||||
|
city_name = 'Paris'
|
||||||
|
zip_code = factory.LazyFunction(lambda: random.randint(75001, 75020))
|
||||||
|
street = factory.Faker('street_address', locale='fr_FR')
|
24
tests/db/fake_data/factories/couriers.py
Normal file
24
tests/db/fake_data/factories/couriers.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
"""Factory to create `Courier` instances."""
|
||||||
|
|
||||||
|
|
||||||
|
import factory
|
||||||
|
from factory import alchemy
|
||||||
|
|
||||||
|
from tests.db.fake_data.factories import utils
|
||||||
|
from urban_meal_delivery import db
|
||||||
|
|
||||||
|
|
||||||
|
class CourierFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
|
"""Create instances of the `db.Courier` model."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = db.Courier
|
||||||
|
sqlalchemy_get_or_create = ('id',)
|
||||||
|
|
||||||
|
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
||||||
|
created_at = factory.LazyFunction(utils.early_in_the_morning)
|
||||||
|
vehicle = 'bicycle'
|
||||||
|
historic_speed = 7.89
|
||||||
|
capacity = 100
|
||||||
|
pay_per_hour = 750
|
||||||
|
pay_per_order = 200
|
16
tests/db/fake_data/factories/customers.py
Normal file
16
tests/db/fake_data/factories/customers.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
"""Factory to create `Customers` instances."""
|
||||||
|
|
||||||
|
import factory
|
||||||
|
from factory import alchemy
|
||||||
|
|
||||||
|
from urban_meal_delivery import db
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
|
"""Create instances of the `db.Customer` model."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = db.Customer
|
||||||
|
sqlalchemy_get_or_create = ('id',)
|
||||||
|
|
||||||
|
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
4
tests/db/fake_data/factories/orders/__init__.py
Normal file
4
tests/db/fake_data/factories/orders/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"""Factory to create `Order` instances."""
|
||||||
|
|
||||||
|
from tests.db.fake_data.factories.orders.ad_hoc import AdHocOrderFactory
|
||||||
|
from tests.db.fake_data.factories.orders.scheduled import ScheduledOrderFactory
|
|
@ -1,114 +1,17 @@
|
||||||
"""Factories to create instances for the SQLAlchemy models."""
|
"""Factory to create ad-hoc `Order` instances."""
|
||||||
|
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import random
|
import random
|
||||||
import string
|
|
||||||
|
|
||||||
import factory
|
import factory
|
||||||
import faker
|
|
||||||
from factory import alchemy
|
from factory import alchemy
|
||||||
from geopy import distance
|
from geopy import distance
|
||||||
|
|
||||||
from tests import config as test_config
|
from tests import config as test_config
|
||||||
|
from tests.db.fake_data.factories import utils
|
||||||
from urban_meal_delivery import db
|
from urban_meal_delivery import db
|
||||||
|
|
||||||
|
|
||||||
def _random_timespan( # noqa:WPS211
|
|
||||||
*,
|
|
||||||
min_hours=0,
|
|
||||||
min_minutes=0,
|
|
||||||
min_seconds=0,
|
|
||||||
max_hours=0,
|
|
||||||
max_minutes=0,
|
|
||||||
max_seconds=0,
|
|
||||||
):
|
|
||||||
"""A randomized `timedelta` object between the specified arguments."""
|
|
||||||
total_min_seconds = min_hours * 3600 + min_minutes * 60 + min_seconds
|
|
||||||
total_max_seconds = max_hours * 3600 + max_minutes * 60 + max_seconds
|
|
||||||
return dt.timedelta(seconds=random.randint(total_min_seconds, total_max_seconds))
|
|
||||||
|
|
||||||
|
|
||||||
def _early_in_the_morning():
|
|
||||||
"""A randomized `datetime` object early in the morning."""
|
|
||||||
early = dt.datetime(test_config.YEAR, test_config.MONTH, test_config.DAY, 3, 0)
|
|
||||||
return early + _random_timespan(max_hours=2)
|
|
||||||
|
|
||||||
|
|
||||||
class AddressFactory(alchemy.SQLAlchemyModelFactory):
|
|
||||||
"""Create instances of the `db.Address` model."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = db.Address
|
|
||||||
sqlalchemy_get_or_create = ('id',)
|
|
||||||
|
|
||||||
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
|
||||||
created_at = factory.LazyFunction(_early_in_the_morning)
|
|
||||||
|
|
||||||
# When testing, all addresses are considered primary ones.
|
|
||||||
# As non-primary addresses have no different behavior and
|
|
||||||
# the property is only kept from the original dataset for
|
|
||||||
# completeness sake, that is ok to do.
|
|
||||||
primary_id = factory.LazyAttribute(lambda obj: obj.id)
|
|
||||||
|
|
||||||
# Mimic a Google Maps Place ID with just random characters.
|
|
||||||
place_id = factory.LazyFunction(
|
|
||||||
lambda: ''.join(random.choice(string.ascii_lowercase) for _ in range(20)),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Place the addresses somewhere in downtown Paris.
|
|
||||||
latitude = factory.Faker('coordinate', center=48.855, radius=0.01)
|
|
||||||
longitude = factory.Faker('coordinate', center=2.34, radius=0.03)
|
|
||||||
# city -> set by the `make_address` fixture as there is only one `city`
|
|
||||||
city_name = 'Paris'
|
|
||||||
zip_code = factory.LazyFunction(lambda: random.randint(75001, 75020))
|
|
||||||
street = factory.Faker('street_address', locale='fr_FR')
|
|
||||||
|
|
||||||
|
|
||||||
class CourierFactory(alchemy.SQLAlchemyModelFactory):
|
|
||||||
"""Create instances of the `db.Courier` model."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = db.Courier
|
|
||||||
sqlalchemy_get_or_create = ('id',)
|
|
||||||
|
|
||||||
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
|
||||||
created_at = factory.LazyFunction(_early_in_the_morning)
|
|
||||||
vehicle = 'bicycle'
|
|
||||||
historic_speed = 7.89
|
|
||||||
capacity = 100
|
|
||||||
pay_per_hour = 750
|
|
||||||
pay_per_order = 200
|
|
||||||
|
|
||||||
|
|
||||||
class CustomerFactory(alchemy.SQLAlchemyModelFactory):
|
|
||||||
"""Create instances of the `db.Customer` model."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = db.Customer
|
|
||||||
sqlalchemy_get_or_create = ('id',)
|
|
||||||
|
|
||||||
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
|
||||||
|
|
||||||
|
|
||||||
_restaurant_names = faker.Faker()
|
|
||||||
|
|
||||||
|
|
||||||
class RestaurantFactory(alchemy.SQLAlchemyModelFactory):
|
|
||||||
"""Create instances of the `db.Restaurant` model."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = db.Restaurant
|
|
||||||
sqlalchemy_get_or_create = ('id',)
|
|
||||||
|
|
||||||
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
|
||||||
created_at = factory.LazyFunction(_early_in_the_morning)
|
|
||||||
name = factory.LazyFunction(
|
|
||||||
lambda: f"{_restaurant_names.first_name()}'s Restaurant",
|
|
||||||
)
|
|
||||||
# address -> set by the `make_restaurant` fixture as there is only one `city`
|
|
||||||
estimated_prep_duration = 1000
|
|
||||||
|
|
||||||
|
|
||||||
class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
"""Create instances of the `db.Order` model.
|
"""Create instances of the `db.Order` model.
|
||||||
|
|
||||||
|
@ -145,7 +48,7 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
cancel_=True,
|
cancel_=True,
|
||||||
cancelled_at=factory.LazyAttribute(
|
cancelled_at=factory.LazyAttribute(
|
||||||
lambda obj: obj.dispatch_at
|
lambda obj: obj.dispatch_at
|
||||||
+ _random_timespan(
|
+ utils.random_timespan(
|
||||||
max_seconds=(obj.pickup_at - obj.dispatch_at).total_seconds(),
|
max_seconds=(obj.pickup_at - obj.dispatch_at).total_seconds(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -154,7 +57,7 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
cancel_=True,
|
cancel_=True,
|
||||||
cancelled_at=factory.LazyAttribute(
|
cancelled_at=factory.LazyAttribute(
|
||||||
lambda obj: obj.pickup_at
|
lambda obj: obj.pickup_at
|
||||||
+ _random_timespan(
|
+ utils.random_timespan(
|
||||||
max_seconds=(obj.delivery_at - obj.pickup_at).total_seconds(),
|
max_seconds=(obj.delivery_at - obj.pickup_at).total_seconds(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -170,7 +73,7 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
lambda: dt.datetime(
|
lambda: dt.datetime(
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 11, 45,
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 11, 45,
|
||||||
)
|
)
|
||||||
+ _random_timespan(max_hours=2, max_minutes=30),
|
+ utils.random_timespan(max_hours=2, max_minutes=30),
|
||||||
)
|
)
|
||||||
ad_hoc = True
|
ad_hoc = True
|
||||||
scheduled_delivery_at = None
|
scheduled_delivery_at = None
|
||||||
|
@ -194,12 +97,13 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
# Restaurant-related attributes
|
# Restaurant-related attributes
|
||||||
# restaurant -> set by the `make_order` fixture for better control
|
# restaurant -> set by the `make_order` fixture for better control
|
||||||
restaurant_notified_at = factory.LazyAttribute(
|
restaurant_notified_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.placed_at + _random_timespan(min_seconds=30, max_seconds=90),
|
lambda obj: obj.placed_at
|
||||||
|
+ utils.random_timespan(min_seconds=30, max_seconds=90),
|
||||||
)
|
)
|
||||||
restaurant_notified_at_corrected = False
|
restaurant_notified_at_corrected = False
|
||||||
restaurant_confirmed_at = factory.LazyAttribute(
|
restaurant_confirmed_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.restaurant_notified_at
|
lambda obj: obj.restaurant_notified_at
|
||||||
+ _random_timespan(min_seconds=30, max_seconds=150),
|
+ utils.random_timespan(min_seconds=30, max_seconds=150),
|
||||||
)
|
)
|
||||||
restaurant_confirmed_at_corrected = False
|
restaurant_confirmed_at_corrected = False
|
||||||
# Use the database defaults of the historic data.
|
# Use the database defaults of the historic data.
|
||||||
|
@ -210,17 +114,18 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
# Dispatch-related columns
|
# Dispatch-related columns
|
||||||
# courier -> set by the `make_order` fixture for better control
|
# courier -> set by the `make_order` fixture for better control
|
||||||
dispatch_at = factory.LazyAttribute(
|
dispatch_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.placed_at + _random_timespan(min_seconds=600, max_seconds=1080),
|
lambda obj: obj.placed_at
|
||||||
|
+ utils.random_timespan(min_seconds=600, max_seconds=1080),
|
||||||
)
|
)
|
||||||
dispatch_at_corrected = False
|
dispatch_at_corrected = False
|
||||||
courier_notified_at = factory.LazyAttribute(
|
courier_notified_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.dispatch_at
|
lambda obj: obj.dispatch_at
|
||||||
+ _random_timespan(min_seconds=100, max_seconds=140),
|
+ utils.random_timespan(min_seconds=100, max_seconds=140),
|
||||||
)
|
)
|
||||||
courier_notified_at_corrected = False
|
courier_notified_at_corrected = False
|
||||||
courier_accepted_at = factory.LazyAttribute(
|
courier_accepted_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.courier_notified_at
|
lambda obj: obj.courier_notified_at
|
||||||
+ _random_timespan(min_seconds=15, max_seconds=45),
|
+ utils.random_timespan(min_seconds=15, max_seconds=45),
|
||||||
)
|
)
|
||||||
courier_accepted_at_corrected = False
|
courier_accepted_at_corrected = False
|
||||||
# Sample a realistic utilization.
|
# Sample a realistic utilization.
|
||||||
|
@ -230,16 +135,17 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
# pickup_address -> aligned with `restaurant.address` by the `make_order` fixture
|
# pickup_address -> aligned with `restaurant.address` by the `make_order` fixture
|
||||||
reached_pickup_at = factory.LazyAttribute(
|
reached_pickup_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.courier_accepted_at
|
lambda obj: obj.courier_accepted_at
|
||||||
+ _random_timespan(min_seconds=300, max_seconds=600),
|
+ utils.random_timespan(min_seconds=300, max_seconds=600),
|
||||||
)
|
)
|
||||||
pickup_at = factory.LazyAttribute(
|
pickup_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.reached_pickup_at
|
lambda obj: obj.reached_pickup_at
|
||||||
+ _random_timespan(min_seconds=120, max_seconds=600),
|
+ utils.random_timespan(min_seconds=120, max_seconds=600),
|
||||||
)
|
)
|
||||||
pickup_at_corrected = False
|
pickup_at_corrected = False
|
||||||
pickup_not_confirmed = False
|
pickup_not_confirmed = False
|
||||||
left_pickup_at = factory.LazyAttribute(
|
left_pickup_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.pickup_at + _random_timespan(min_seconds=60, max_seconds=180),
|
lambda obj: obj.pickup_at
|
||||||
|
+ utils.random_timespan(min_seconds=60, max_seconds=180),
|
||||||
)
|
)
|
||||||
left_pickup_at_corrected = False
|
left_pickup_at_corrected = False
|
||||||
|
|
||||||
|
@ -247,11 +153,11 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
# delivery_address -> set by the `make_order` fixture as there is only one `city`
|
# delivery_address -> set by the `make_order` fixture as there is only one `city`
|
||||||
reached_delivery_at = factory.LazyAttribute(
|
reached_delivery_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.left_pickup_at
|
lambda obj: obj.left_pickup_at
|
||||||
+ _random_timespan(min_seconds=240, max_seconds=480),
|
+ utils.random_timespan(min_seconds=240, max_seconds=480),
|
||||||
)
|
)
|
||||||
delivery_at = factory.LazyAttribute(
|
delivery_at = factory.LazyAttribute(
|
||||||
lambda obj: obj.reached_delivery_at
|
lambda obj: obj.reached_delivery_at
|
||||||
+ _random_timespan(min_seconds=240, max_seconds=660),
|
+ utils.random_timespan(min_seconds=240, max_seconds=660),
|
||||||
)
|
)
|
||||||
delivery_at_corrected = False
|
delivery_at_corrected = False
|
||||||
delivery_not_confirmed = False
|
delivery_not_confirmed = False
|
||||||
|
@ -316,63 +222,3 @@ class AdHocOrderFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
obj.delivery_at_corrected = None
|
obj.delivery_at_corrected = None
|
||||||
obj.delivery_not_confirmed = None
|
obj.delivery_not_confirmed = None
|
||||||
obj._courier_waited_at_delivery = None
|
obj._courier_waited_at_delivery = None
|
||||||
|
|
||||||
|
|
||||||
class ScheduledOrderFactory(AdHocOrderFactory):
|
|
||||||
"""Create instances of the `db.Order` model.
|
|
||||||
|
|
||||||
This class takes care of the various timestamps for pre-orders.
|
|
||||||
|
|
||||||
Pre-orders are placed long before the test day's lunch time starts.
|
|
||||||
All timestamps are relative to either `.dispatch_at` or `.restaurant_notified_at`
|
|
||||||
and calculated backwards from `.scheduled_delivery_at`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Attributes regarding the specialization of an `Order`: ad-hoc or scheduled.
|
|
||||||
placed_at = factory.LazyFunction(_early_in_the_morning)
|
|
||||||
ad_hoc = False
|
|
||||||
# Discrete `datetime` objects in the "core" lunch time are enough.
|
|
||||||
scheduled_delivery_at = factory.LazyFunction(
|
|
||||||
lambda: random.choice(
|
|
||||||
[
|
|
||||||
dt.datetime(
|
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 0,
|
|
||||||
),
|
|
||||||
dt.datetime(
|
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 15,
|
|
||||||
),
|
|
||||||
dt.datetime(
|
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 30,
|
|
||||||
),
|
|
||||||
dt.datetime(
|
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 45,
|
|
||||||
),
|
|
||||||
dt.datetime(
|
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 13, 0,
|
|
||||||
),
|
|
||||||
dt.datetime(
|
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 13, 15,
|
|
||||||
),
|
|
||||||
dt.datetime(
|
|
||||||
test_config.YEAR, test_config.MONTH, test_config.DAY, 13, 30,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
scheduled_delivery_at_corrected = False
|
|
||||||
# Assume the `Order` is on time.
|
|
||||||
first_estimated_delivery_at = factory.LazyAttribute(
|
|
||||||
lambda obj: obj.scheduled_delivery_at,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Restaurant-related attributes
|
|
||||||
restaurant_notified_at = factory.LazyAttribute(
|
|
||||||
lambda obj: obj.scheduled_delivery_at
|
|
||||||
- _random_timespan(min_minutes=45, max_minutes=50),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Dispatch-related attributes
|
|
||||||
dispatch_at = factory.LazyAttribute(
|
|
||||||
lambda obj: obj.scheduled_delivery_at
|
|
||||||
- _random_timespan(min_minutes=40, max_minutes=45),
|
|
||||||
)
|
|
70
tests/db/fake_data/factories/orders/scheduled.py
Normal file
70
tests/db/fake_data/factories/orders/scheduled.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
"""Factory to create scheduled `Order` instances."""
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
|
import random
|
||||||
|
|
||||||
|
import factory
|
||||||
|
|
||||||
|
from tests import config as test_config
|
||||||
|
from tests.db.fake_data.factories import utils
|
||||||
|
from tests.db.fake_data.factories.orders import ad_hoc
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduledOrderFactory(ad_hoc.AdHocOrderFactory):
|
||||||
|
"""Create instances of the `db.Order` model.
|
||||||
|
|
||||||
|
This class takes care of the various timestamps for pre-orders.
|
||||||
|
|
||||||
|
Pre-orders are placed long before the test day's lunch time starts.
|
||||||
|
All timestamps are relative to either `.dispatch_at` or `.restaurant_notified_at`
|
||||||
|
and calculated backwards from `.scheduled_delivery_at`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Attributes regarding the specialization of an `Order`: ad-hoc or scheduled.
|
||||||
|
placed_at = factory.LazyFunction(utils.early_in_the_morning)
|
||||||
|
ad_hoc = False
|
||||||
|
# Discrete `datetime` objects in the "core" lunch time are enough.
|
||||||
|
scheduled_delivery_at = factory.LazyFunction(
|
||||||
|
lambda: random.choice(
|
||||||
|
[
|
||||||
|
dt.datetime(
|
||||||
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 0,
|
||||||
|
),
|
||||||
|
dt.datetime(
|
||||||
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 15,
|
||||||
|
),
|
||||||
|
dt.datetime(
|
||||||
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 30,
|
||||||
|
),
|
||||||
|
dt.datetime(
|
||||||
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 12, 45,
|
||||||
|
),
|
||||||
|
dt.datetime(
|
||||||
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 13, 0,
|
||||||
|
),
|
||||||
|
dt.datetime(
|
||||||
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 13, 15,
|
||||||
|
),
|
||||||
|
dt.datetime(
|
||||||
|
test_config.YEAR, test_config.MONTH, test_config.DAY, 13, 30,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
scheduled_delivery_at_corrected = False
|
||||||
|
# Assume the `Order` is on time.
|
||||||
|
first_estimated_delivery_at = factory.LazyAttribute(
|
||||||
|
lambda obj: obj.scheduled_delivery_at,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Restaurant-related attributes
|
||||||
|
restaurant_notified_at = factory.LazyAttribute(
|
||||||
|
lambda obj: obj.scheduled_delivery_at
|
||||||
|
- utils.random_timespan(min_minutes=45, max_minutes=50),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dispatch-related attributes
|
||||||
|
dispatch_at = factory.LazyAttribute(
|
||||||
|
lambda obj: obj.scheduled_delivery_at
|
||||||
|
- utils.random_timespan(min_minutes=40, max_minutes=45),
|
||||||
|
)
|
27
tests/db/fake_data/factories/restaurants.py
Normal file
27
tests/db/fake_data/factories/restaurants.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
"""Factory to create `Restaurant` instances."""
|
||||||
|
|
||||||
|
import factory
|
||||||
|
import faker
|
||||||
|
from factory import alchemy
|
||||||
|
|
||||||
|
from tests.db.fake_data.factories import utils
|
||||||
|
from urban_meal_delivery import db
|
||||||
|
|
||||||
|
|
||||||
|
_restaurant_names = faker.Faker()
|
||||||
|
|
||||||
|
|
||||||
|
class RestaurantFactory(alchemy.SQLAlchemyModelFactory):
|
||||||
|
"""Create instances of the `db.Restaurant` model."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = db.Restaurant
|
||||||
|
sqlalchemy_get_or_create = ('id',)
|
||||||
|
|
||||||
|
id = factory.Sequence(lambda num: num) # noqa:WPS125
|
||||||
|
created_at = factory.LazyFunction(utils.early_in_the_morning)
|
||||||
|
name = factory.LazyFunction(
|
||||||
|
lambda: f"{_restaurant_names.first_name()}'s Restaurant",
|
||||||
|
)
|
||||||
|
# address -> set by the `make_restaurant` fixture as there is only one `city`
|
||||||
|
estimated_prep_duration = 1000
|
27
tests/db/fake_data/factories/utils.py
Normal file
27
tests/db/fake_data/factories/utils.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
"""Utilities used in all `*Factory` classes."""
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
|
import random
|
||||||
|
|
||||||
|
from tests import config as test_config
|
||||||
|
|
||||||
|
|
||||||
|
def random_timespan( # noqa:WPS211
|
||||||
|
*,
|
||||||
|
min_hours=0,
|
||||||
|
min_minutes=0,
|
||||||
|
min_seconds=0,
|
||||||
|
max_hours=0,
|
||||||
|
max_minutes=0,
|
||||||
|
max_seconds=0,
|
||||||
|
):
|
||||||
|
"""A randomized `timedelta` object between the specified arguments."""
|
||||||
|
total_min_seconds = min_hours * 3600 + min_minutes * 60 + min_seconds
|
||||||
|
total_max_seconds = max_hours * 3600 + max_minutes * 60 + max_seconds
|
||||||
|
return dt.timedelta(seconds=random.randint(total_min_seconds, total_max_seconds))
|
||||||
|
|
||||||
|
|
||||||
|
def early_in_the_morning():
|
||||||
|
"""A randomized `datetime` object early in the morning."""
|
||||||
|
early = dt.datetime(test_config.YEAR, test_config.MONTH, test_config.DAY, 3, 0)
|
||||||
|
return early + random_timespan(max_hours=2)
|
Loading…
Reference in a new issue