- add new ORM models `ReplaySimulation` and `ReplayedOrder` to store the data generated by the routing simulations - add migrations script to create the corresponding database tables + create "replay_simulations" and "replayed_orders" tables + add missing check constraints to "orders" table + add unique constraint to "orders" table to enable compound key + drop unnecessary check constraints from the "orders" table - add tests for the new ORM models + add `simulation`, `replayed_order`, `make_replay_order()`, and `pre_order` fixtures + add `ReplayedOrderFactor` faker class - fix some typos
585 lines
21 KiB
Python
585 lines
21 KiB
Python
"""Test the ORM's `ReplayedOrder` model."""
|
|
|
|
import datetime as dt
|
|
|
|
import pytest
|
|
import sqlalchemy as sqla
|
|
from sqlalchemy import exc as sa_exc
|
|
|
|
from tests import config as test_config
|
|
from urban_meal_delivery import db
|
|
|
|
|
|
class TestSpecialMethods:
|
|
"""Test special methods in `ReplayedOrder`."""
|
|
|
|
def test_create_order(self, replayed_order):
|
|
"""Test instantiation of a new `ReplayedOrder` object."""
|
|
assert replayed_order is not None
|
|
|
|
def test_text_representation(self, replayed_order):
|
|
"""`ReplayedOrder` has a non-literal text representation."""
|
|
result = repr(replayed_order)
|
|
|
|
assert result == f'<ReplayedOrder(#{replayed_order.actual.id})>'
|
|
|
|
|
|
@pytest.mark.db
|
|
@pytest.mark.no_cover
|
|
class TestConstraints:
|
|
"""Test the database constraints defined in `ReplayedOrder`."""
|
|
|
|
def test_insert_into_into_database(self, db_session, replayed_order):
|
|
"""Insert an instance into the (empty) database."""
|
|
assert db_session.query(db.ReplayedOrder).count() == 0
|
|
|
|
db_session.add(replayed_order)
|
|
db_session.commit()
|
|
|
|
assert db_session.query(db.ReplayedOrder).count() == 1
|
|
|
|
def test_delete_a_referenced_simulation(self, db_session, replayed_order):
|
|
"""Remove a record that is referenced with a FK."""
|
|
db_session.add(replayed_order)
|
|
db_session.commit()
|
|
|
|
# Must delete without ORM as otherwise an UPDATE statement is emitted.
|
|
stmt = sqla.delete(db.ReplaySimulation).where(
|
|
db.ReplaySimulation.id == replayed_order.simulation.id,
|
|
)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='fk_replayed_orders_to_replay_simulations',
|
|
):
|
|
db_session.execute(stmt)
|
|
|
|
def test_delete_a_referenced_actual_order(self, db_session, replayed_order):
|
|
"""Remove a record that is referenced with a FK."""
|
|
db_session.add(replayed_order)
|
|
db_session.commit()
|
|
|
|
# Must delete without ORM as otherwise an UPDATE statement is emitted.
|
|
stmt = sqla.delete(db.Order).where(db.Order.id == replayed_order.actual.id)
|
|
|
|
with pytest.raises(sa_exc.IntegrityError, match='fk_replayed_orders_to_orders'):
|
|
db_session.execute(stmt)
|
|
|
|
def test_delete_a_referenced_courier(
|
|
self, db_session, replayed_order, make_courier,
|
|
):
|
|
"""Remove a record that is referenced with a FK."""
|
|
# Need a second courier, one that is
|
|
# not associated with the `.actual` order.
|
|
replayed_order.courier = make_courier()
|
|
|
|
db_session.add(replayed_order)
|
|
db_session.commit()
|
|
|
|
# Must delete without ORM as otherwise an UPDATE statement is emitted.
|
|
stmt = sqla.delete(db.Courier).where(db.Courier.id == replayed_order.courier.id)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='fk_replayed_orders_to_couriers',
|
|
):
|
|
db_session.execute(stmt)
|
|
|
|
def test_ad_hoc_order_with_scheduled_delivery_at(self, db_session, replayed_order):
|
|
"""Insert an instance with invalid data."""
|
|
assert replayed_order.ad_hoc is True
|
|
|
|
replayed_order.scheduled_delivery_at = dt.datetime(*test_config.DATE, 18, 0)
|
|
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='either_ad_hoc_or_scheduled_order',
|
|
):
|
|
db_session.commit()
|
|
|
|
def test_scheduled_order_without_scheduled_delivery_at(
|
|
self, db_session, make_replay_order,
|
|
):
|
|
"""Insert an instance with invalid data."""
|
|
replay_order = make_replay_order(scheduled=True)
|
|
|
|
assert replay_order.ad_hoc is False
|
|
|
|
replay_order.scheduled_delivery_at = None
|
|
|
|
db_session.add(replay_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='either_ad_hoc_or_scheduled_order',
|
|
):
|
|
db_session.commit()
|
|
|
|
def test_ad_hoc_order_too_early(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data."""
|
|
replay_order = make_replay_order(
|
|
placed_at=dt.datetime(*test_config.DATE, 10, 0),
|
|
)
|
|
|
|
db_session.add(replay_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='ad_hoc_orders_within_business_hours',
|
|
):
|
|
db_session.commit()
|
|
|
|
def test_ad_hoc_order_too_late(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data."""
|
|
replay_order = make_replay_order(
|
|
placed_at=dt.datetime(*test_config.DATE, 23, 0),
|
|
)
|
|
|
|
db_session.add(replay_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='ad_hoc_orders_within_business_hours',
|
|
):
|
|
db_session.commit()
|
|
|
|
def test_scheduled_order_way_too_early(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data."""
|
|
scheduled_delivery_at = dt.datetime(*test_config.DATE, 10, 0)
|
|
replay_pre_order = make_replay_order(
|
|
scheduled=True,
|
|
placed_at=scheduled_delivery_at - dt.timedelta(hours=12),
|
|
scheduled_delivery_at=scheduled_delivery_at,
|
|
restaurant_notified_at=scheduled_delivery_at + dt.timedelta(hours=1),
|
|
dispatch_at=scheduled_delivery_at + dt.timedelta(hours=1),
|
|
)
|
|
|
|
db_session.add(replay_pre_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='scheduled_orders_within_business_hours',
|
|
):
|
|
db_session.commit()
|
|
|
|
def test_scheduled_order_a_bit_too_early(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data."""
|
|
scheduled_delivery_at = dt.datetime(*test_config.DATE, 11, 30)
|
|
replay_pre_order = make_replay_order(
|
|
scheduled=True,
|
|
placed_at=scheduled_delivery_at - dt.timedelta(hours=14),
|
|
scheduled_delivery_at=scheduled_delivery_at,
|
|
restaurant_notified_at=scheduled_delivery_at + dt.timedelta(hours=1),
|
|
dispatch_at=scheduled_delivery_at + dt.timedelta(hours=1),
|
|
)
|
|
|
|
db_session.add(replay_pre_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='scheduled_orders_within_business_hours',
|
|
):
|
|
db_session.commit()
|
|
|
|
def test_scheduled_order_not_too_early(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data.
|
|
|
|
11.45 is the only time outside noon to 11 pm when a scheduled order is allowed.
|
|
"""
|
|
scheduled_delivery_at = dt.datetime(*test_config.DATE, 11, 45)
|
|
replay_pre_order = make_replay_order(
|
|
scheduled=True,
|
|
placed_at=scheduled_delivery_at - dt.timedelta(hours=14),
|
|
scheduled_delivery_at=scheduled_delivery_at,
|
|
restaurant_notified_at=scheduled_delivery_at + dt.timedelta(hours=1),
|
|
dispatch_at=scheduled_delivery_at + dt.timedelta(hours=1),
|
|
)
|
|
|
|
assert db_session.query(db.Order).count() == 0
|
|
|
|
db_session.add(replay_pre_order)
|
|
db_session.commit()
|
|
|
|
assert db_session.query(db.Order).count() == 1
|
|
|
|
def test_scheduled_order_too_late(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data."""
|
|
scheduled_delivery_at = dt.datetime(*test_config.DATE, 23, 0)
|
|
replay_pre_order = make_replay_order(
|
|
scheduled=True,
|
|
placed_at=scheduled_delivery_at - dt.timedelta(hours=10),
|
|
scheduled_delivery_at=scheduled_delivery_at,
|
|
restaurant_notified_at=scheduled_delivery_at + dt.timedelta(minutes=30),
|
|
dispatch_at=scheduled_delivery_at + dt.timedelta(minutes=30),
|
|
)
|
|
|
|
db_session.add(replay_pre_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='scheduled_orders_within_business_hours',
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize('minute', [min_ for min_ in range(60) if min_ % 15 != 0])
|
|
def test_scheduled_order_at_non_quarter_of_an_hour(
|
|
self, db_session, make_replay_order, minute,
|
|
):
|
|
"""Insert an instance with invalid data."""
|
|
scheduled_delivery_at = dt.datetime( # `minute` is not 0, 15, 30, or 45
|
|
*test_config.DATE, test_config.NOON, minute,
|
|
)
|
|
replay_pre_order = make_replay_order(
|
|
scheduled=True,
|
|
placed_at=scheduled_delivery_at - dt.timedelta(hours=10),
|
|
scheduled_delivery_at=scheduled_delivery_at,
|
|
restaurant_notified_at=scheduled_delivery_at + dt.timedelta(minutes=30),
|
|
dispatch_at=scheduled_delivery_at + dt.timedelta(minutes=30),
|
|
)
|
|
|
|
db_session.add(replay_pre_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError,
|
|
match='scheduled_orders_must_be_at_quart', # constraint name too long
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize('second', list(range(1, 60)))
|
|
def test_scheduled_order_at_non_quarter_of_an_hour_by_seconds(
|
|
self, db_session, make_replay_order, second,
|
|
):
|
|
"""Insert an instance with invalid data."""
|
|
scheduled_delivery_at = dt.datetime(
|
|
*test_config.DATE, test_config.NOON, 0, second,
|
|
)
|
|
replay_pre_order = make_replay_order(
|
|
scheduled=True,
|
|
placed_at=scheduled_delivery_at - dt.timedelta(hours=10),
|
|
scheduled_delivery_at=scheduled_delivery_at,
|
|
restaurant_notified_at=scheduled_delivery_at + dt.timedelta(minutes=30),
|
|
dispatch_at=scheduled_delivery_at + dt.timedelta(minutes=30),
|
|
)
|
|
|
|
db_session.add(replay_pre_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError,
|
|
match='scheduled_orders_must_be_at_quart', # constraint name too long
|
|
):
|
|
db_session.commit()
|
|
|
|
def test_scheduled_order_too_soon(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data.
|
|
|
|
Scheduled orders must be at least 30 minutes into the future.
|
|
"""
|
|
# Create an ad-hoc order first and then make that a scheduled order.
|
|
# This way, it is less work to keep the timestamps consistent.
|
|
replay_pre_order = make_replay_order(scheduled=False)
|
|
|
|
# Make the `scheduled_delivery_at` the quarter of an hour
|
|
# following the next quarter of an hour (i.e., the timestamp
|
|
# is between 15 and 30 minutes into the future).
|
|
replay_pre_order.ad_hoc = False
|
|
replay_pre_order.actual.ad_hoc = False
|
|
minutes_to_next_quarter = 15 - (replay_pre_order.placed_at.minute % 15)
|
|
scheduled_delivery_at = (
|
|
# `.placed_at` may have non-0 seconds.
|
|
replay_pre_order.placed_at.replace(second=0)
|
|
+ dt.timedelta(minutes=(minutes_to_next_quarter + 15))
|
|
)
|
|
replay_pre_order.scheduled_delivery_at = scheduled_delivery_at
|
|
replay_pre_order.actual.scheduled_delivery_at = scheduled_delivery_at
|
|
replay_pre_order.actual.scheduled_delivery_at_corrected = False
|
|
|
|
db_session.add(replay_pre_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='scheduled_orders_not_within_30_minutes',
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize(
|
|
'column',
|
|
[
|
|
'restaurant_notified_at',
|
|
'restaurant_confirmed_at',
|
|
'restaurant_ready_at',
|
|
'dispatch_at',
|
|
'courier', # not `.courier_id`
|
|
'courier_notified_at',
|
|
'courier_accepted_at',
|
|
'reached_pickup_at',
|
|
'pickup_at',
|
|
'left_pickup_at',
|
|
'reached_delivery_at',
|
|
'delivery_at',
|
|
],
|
|
)
|
|
def test_not_fully_simulated_order(self, db_session, replayed_order, column):
|
|
"""Insert an instance with invalid data."""
|
|
assert replayed_order.cancelled_at is None
|
|
|
|
setattr(replayed_order, column, None)
|
|
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError,
|
|
match='either_cancelled_o', # constraint name too long
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize(
|
|
'column',
|
|
[
|
|
'restaurant_notified_at',
|
|
'restaurant_confirmed_at',
|
|
'restaurant_ready_at',
|
|
'dispatch_at',
|
|
'courier_id',
|
|
'courier_notified_at',
|
|
'courier_accepted_at',
|
|
'reached_pickup_at',
|
|
],
|
|
)
|
|
def test_simulated_cancellation(self, db_session, replayed_order, column):
|
|
"""Insert an instance with invalid data.
|
|
|
|
Cancelled orders may have missing timestamps
|
|
"""
|
|
replayed_order.cancelled_at = replayed_order.pickup_at
|
|
|
|
replayed_order.pickup_at = None
|
|
replayed_order.left_pickup_at = None
|
|
replayed_order.reached_delivery_at = None
|
|
replayed_order.delivery_at = None
|
|
|
|
setattr(replayed_order, column, None)
|
|
|
|
db_session.add(replayed_order)
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize(
|
|
'column', ['pickup_at', 'left_pickup_at', 'reached_delivery_at', 'delivery_at'],
|
|
)
|
|
def test_no_simulated_cancellation_after_pickup(
|
|
self, db_session, replayed_order, column,
|
|
):
|
|
"""Insert an instance with invalid data."""
|
|
# Setting `.cancelled_at` to the end of a day
|
|
# ensures the timestamps are logically ok.
|
|
replayed_order.cancelled_at = dt.datetime(*test_config.DATE, 23)
|
|
|
|
# Set all timestamps after `.reached_pickup_at` to NULL
|
|
# except the one under test.
|
|
for unset_column in (
|
|
'pickup_at',
|
|
'left_pickup_at',
|
|
'reached_delivery_at',
|
|
'delivery_at',
|
|
):
|
|
if unset_column != column:
|
|
setattr(replayed_order, unset_column, None)
|
|
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError,
|
|
match='cancellations_may_only_occur_befo', # constraint name too long
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize('duration', [-1, 3601])
|
|
def test_estimated_prep_duration_out_of_range(
|
|
self, db_session, replayed_order, duration,
|
|
):
|
|
"""Insert an instance with invalid data."""
|
|
replayed_order.estimated_prep_duration = duration
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='between_0', # constraint name too long
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize('duration', [1, 59, 119, 3599])
|
|
def test_estimated_prep_duration_not_whole_minute(
|
|
self, db_session, replayed_order, duration,
|
|
):
|
|
"""Insert an instance with invalid data."""
|
|
replayed_order.estimated_prep_duration = duration
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='must_be_w', # constraint name too long
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize('utilization', [-1, 101])
|
|
def test_utilization_out_of_range(self, db_session, replayed_order, utilization):
|
|
"""Insert an instance with invalid data."""
|
|
replayed_order.utilization = utilization
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='between_0_and_100',
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize('column', ['restaurant_notified_at', 'dispatch_at'])
|
|
@pytest.mark.parametrize('hour', [0, 10])
|
|
def test_order_dispatched_in_non_business_hour(
|
|
self, db_session, replayed_order, column, hour,
|
|
):
|
|
"""Insert an instance with invalid data."""
|
|
orig_timestamp = getattr(replayed_order, column)
|
|
new_timestamp = orig_timestamp.replace(hour=hour)
|
|
|
|
replayed_order.placed_at = new_timestamp - dt.timedelta(minutes=1)
|
|
setattr(replayed_order, column, new_timestamp)
|
|
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(
|
|
sa_exc.IntegrityError, match='business_hours',
|
|
):
|
|
db_session.commit()
|
|
|
|
@pytest.mark.parametrize(
|
|
'comparison',
|
|
[
|
|
'placed_at < restaurant_notified_at',
|
|
'placed_at < restaurant_confirmed_at',
|
|
'placed_at < restaurant_ready_at',
|
|
'placed_at < dispatch_at',
|
|
'placed_at < first_estimated_delivery_at',
|
|
'placed_at < courier_notified_at',
|
|
'placed_at < courier_accepted_at',
|
|
'placed_at < reached_pickup_at',
|
|
'placed_at < pickup_at',
|
|
'placed_at < left_pickup_at',
|
|
'placed_at < reached_delivery_at',
|
|
'placed_at < delivery_at',
|
|
'restaurant_notified_at < restaurant_confirmed_at',
|
|
'restaurant_notified_at < restaurant_ready_at',
|
|
'restaurant_notified_at < pickup_at',
|
|
'restaurant_confirmed_at < restaurant_ready_at',
|
|
'restaurant_confirmed_at < pickup_at',
|
|
'restaurant_ready_at < pickup_at',
|
|
'dispatch_at < first_estimated_delivery_at',
|
|
'dispatch_at < courier_notified_at',
|
|
'dispatch_at < courier_accepted_at',
|
|
'dispatch_at < reached_pickup_at',
|
|
'dispatch_at < pickup_at',
|
|
'dispatch_at < left_pickup_at',
|
|
'dispatch_at < reached_delivery_at',
|
|
'dispatch_at < delivery_at',
|
|
'courier_notified_at < courier_accepted_at',
|
|
'courier_notified_at < reached_pickup_at',
|
|
'courier_notified_at < pickup_at',
|
|
'courier_notified_at < left_pickup_at',
|
|
'courier_notified_at < reached_delivery_at',
|
|
'courier_notified_at < delivery_at',
|
|
'courier_accepted_at < reached_pickup_at',
|
|
'courier_accepted_at < pickup_at',
|
|
'courier_accepted_at < left_pickup_at',
|
|
'courier_accepted_at < reached_delivery_at',
|
|
'courier_accepted_at < delivery_at',
|
|
'reached_pickup_at < pickup_at',
|
|
'reached_pickup_at < left_pickup_at',
|
|
'reached_pickup_at < reached_delivery_at',
|
|
'reached_pickup_at < delivery_at',
|
|
'pickup_at < left_pickup_at',
|
|
'pickup_at < reached_delivery_at',
|
|
'pickup_at < delivery_at',
|
|
'left_pickup_at < reached_delivery_at',
|
|
'left_pickup_at < delivery_at',
|
|
'reached_delivery_at < delivery_at',
|
|
],
|
|
)
|
|
def test_timestamps_unordered(
|
|
self, db_session, replayed_order, comparison,
|
|
):
|
|
"""Insert an instance with invalid data.
|
|
|
|
There are two special cases for this test case below,
|
|
where other attributes on `replayed_order` must be unset.
|
|
"""
|
|
smaller, bigger = comparison.split(' < ')
|
|
|
|
assert smaller is not None
|
|
|
|
violating_timestamp = getattr(replayed_order, smaller) - dt.timedelta(seconds=1)
|
|
setattr(replayed_order, bigger, violating_timestamp)
|
|
|
|
db_session.add(replayed_order)
|
|
|
|
with pytest.raises(sa_exc.IntegrityError, match='ordered_timestamps'):
|
|
db_session.commit()
|
|
|
|
def test_timestamps_unordered_scheduled(self, db_session, make_replay_order):
|
|
"""Insert an instance with invalid data.
|
|
|
|
This is one of two special cases. See the generic case above.
|
|
"""
|
|
replayed_pre_order = make_replay_order(scheduled=True)
|
|
# As we subtract 1 second in the generic case,
|
|
# choose one second after a quarter of an hour.
|
|
replayed_pre_order.placed_at = dt.datetime(*test_config.DATE, 11, 45, 1)
|
|
self.test_timestamps_unordered(
|
|
db_session, replayed_pre_order, 'placed_at < scheduled_delivery_at',
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
|
'comparison',
|
|
[
|
|
'placed_at < cancelled_at',
|
|
'restaurant_notified_at < cancelled_at',
|
|
'restaurant_confirmed_at < cancelled_at',
|
|
'restaurant_ready_at < cancelled_at',
|
|
'dispatch_at < cancelled_at',
|
|
'courier_notified_at < cancelled_at',
|
|
'courier_accepted_at < cancelled_at',
|
|
'reached_pickup_at < cancelled_at',
|
|
],
|
|
)
|
|
def test_timestamps_unordered_cancelled(
|
|
self, db_session, replayed_order, comparison,
|
|
):
|
|
"""Insert an instance with invalid data.
|
|
|
|
This is one of two special cases. See the generic case above.
|
|
"""
|
|
replayed_order.pickup_at = None
|
|
replayed_order.left_pickup_at = None
|
|
replayed_order.reached_delivery_at = None
|
|
replayed_order.delivery_at = None
|
|
|
|
self.test_timestamps_unordered(db_session, replayed_order, comparison)
|
|
|
|
|
|
class TestProperties:
|
|
"""Test properties in `ReplayedOrder`.
|
|
|
|
The `replayed_order` fixture uses the defaults specified in
|
|
`factories.ReplayedOrderFactory` and provided by the `make_replay_order` fixture.
|
|
"""
|
|
|
|
def test_has_customer(self, replayed_order):
|
|
"""Test `ReplayedOrder.customer` property."""
|
|
result = replayed_order.customer
|
|
|
|
assert result is replayed_order.actual.customer
|
|
|
|
def test_has_restaurant(self, replayed_order):
|
|
"""Test `ReplayedOrder.restaurant` property."""
|
|
result = replayed_order.restaurant
|
|
|
|
assert result is replayed_order.actual.restaurant
|
|
|
|
def test_has_pickup_address(self, replayed_order):
|
|
"""Test `ReplayedOrder.pickup_address` property."""
|
|
result = replayed_order.pickup_address
|
|
|
|
assert result is replayed_order.actual.pickup_address
|
|
|
|
def test_has_delivery_address(self, replayed_order):
|
|
"""Test `ReplayedOrder.delivery_address` property."""
|
|
result = replayed_order.delivery_address
|
|
|
|
assert result is replayed_order.actual.delivery_address
|