async def test_parking_request_message(ws_url, user_id, mocker, engine): user_location = Location(0.0, 1.0) lot_location = Location(0.001, 1.0) lot_allocation = ParkingLotAllocation(100, "test_lot", 0, lot_location, 1, distance=50.0, availability=20) parking_request = ParkingRequestMessage(user_location) mocker.patch.object(engine, 'handle_allocation_request') engine.handle_allocation_request.return_value = return_async_value( lot_allocation) conn: WebSocketClientConnection = await tornado.websocket.websocket_connect( ws_url + user_id) await conn.write_message(serialize_model(parking_request)) msg = deserialize_ws_message(await conn.read_message()) engine.handle_allocation_request.assert_called_once_with( user_id, parking_request) assert isinstance(msg, ParkingAllocationMessage) assert msg.lot == lot_allocation
async def test_no_parking_lots_retry(http_client, base_url): car_id = str(uuid4()) car_cli = await CarWebsocket.create( base_url=base_url.replace('http', 'ws') + "/ws", user_id=car_id) logger.debug("car websocket client connected") response = await car_cli.send_parking_request(Location(0.0, 1.0), {}) logger.debug(f'requested allocation: {response}') allocated = await car_cli.receive(wsmodels.ErrorMessage) logger.debug(f'allocation failed: {allocated}') assert allocated.error.msg == 'No parking lot available.' cli = ParkingLotRest(base_url, http_client) lot = ParkingLot(10, 'a', 1.0, Location(0.0, 1.0)) response = await cli.create_lot(lot) lot.id = response logger.debug(f'created lot {response}') response = await car_cli.send_parking_request(Location(0.0, 1.0), {}) logger.debug(f'requested allocation: {response}') allocated = await car_cli.receive(wsmodels.ParkingAllocationMessage) logger.debug(f'allocation recieved: {allocated}') assert allocated.lot.id == lot.id
async def test_create_parking_lot_confirm(caplog, http_client, base_url): caplog.set_level(logging.INFO) # create parking lot... cli = ParkingLotRest(base_url, http_client) lot = ParkingLot(10, 'a', 1.0, Location(0.0, 1.0)) response = await cli.create_lot(lot) lot.id = response await asyncio.sleep(1) # create car car_id = str(uuid4()) car_cli = await CarWebsocket.create( base_url=base_url.replace('http', 'ws') + "/ws", user_id=car_id) response = await car_cli.send_parking_request(Location(0.0, 1.0), {}) # ask for allocation allocated = await car_cli.receive(wsmodels.ParkingAllocationMessage) assert allocated.lot.id == lot.id # accept allocation await car_cli.send_parking_acceptance(allocated.lot.id) # wait for confirmation await car_cli.receive(wsmodels.ConfirmationMessage)
async def test_recalculate_allocations(): user_ids = [uuid4(), uuid4(), uuid4()] locations = [ Location(0.0, 0.0), Location(0.0001, 0.0002), Location(0.2, 0.0) ] PARK_ID = 1 removed = [] async def fake_get_parking_lot(park_id): return { 'id': PARK_ID, 'name': 'test_lot_1', 'capacity': 10, 'lat': 0.0, 'long': 0.0001, 'price': 2, 'num_available': 1, 'num_allocated': 3 } async def fake_get_parking_lot_allocations(park_id): return [{ 'user_id': user_ids[0], 'park_id': PARK_ID }, { 'user_id': user_ids[1], 'park_id': PARK_ID }, { 'user_id': user_ids[2], 'park_id': PARK_ID }] async def fake_delete_allocation(user_id): removed.append(user_id) db = DbAccess() db.get_parking_lot = fake_get_parking_lot db.get_parking_lot_allocations = fake_get_parking_lot_allocations db.delete_allocation = fake_delete_allocation us = UserSessions() us.deallocate = lambda user_id: print(user_id) for user_id, loc in zip(user_ids, locations): us.add_user(user_id, None) us.update_user_location(user_id, loc) engine = AllocationEngine(db, us) await engine.recalculate_allocations(PARK_ID) assert len(removed) == 2
async def test_allocate_parking_lot_to_user_already_allocated_fail(event_loop): with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot1 = ParkingLot(100, 'test_name1', 0.0, Location(0.0, 1.0)) parking_lot2 = ParkingLot(100, 'test_name2', 0.0, Location(0.0, 1.0)) await db.insert_parking_lot(parking_lot1) await db.insert_parking_lot(parking_lot2) assert await db.allocate_parking_lot("test_user", 1) is True assert await db.allocate_parking_lot("test_user", 2) is False
async def test_create_parking_lot(http_client, base_url): lot = ParkingLot(100, 'test', 1.0, Location(0.0, 1.0)) response = await http_client.fetch(base_url + '/spaces', method='POST', headers=HEADERS, body=serialize_model(lot)) assert ParkingLotCreationResponse(**json.loads(response.body)).id == 1
async def test_update_parking_lot_availability(plr): lot = ParkingLot(100, 'test', 1.0, Location(0.0, 1.0)) lot_id = await plr.create_lot(lot) assert lot_id == 1 # TODO: Check that the ParkingLot availability has actually been updated using a GET # request. await plr.update_available(lot_id, 2)
async def test_delete_parking_lot(plr): lot = ParkingLot(100, 'test', 1.0, Location(0.0, 1.0)) lot_id = await plr.create_lot(lot) assert lot_id == 1 # TODO: Check that the ParkingLot has actually been deleted using a GET # request. await plr.delete_lot(lot_id)
async def test_handle_allocation_request(): async def fake_get_available_parking_lot(loc, dist, rej): lots = [{ 'id': 1, 'name': 'test_lot_1', 'capacity': 10, 'lat': 0.0, 'long': 0.0001, 'price': 2, 'num_available': 10, 'num_allocated': 0 }, { 'id': 2, 'name': 'test_lot_2', 'capacity': 10, 'lat': 0.0, 'long': 0.0001, 'price': 2, 'num_available': 10, 'num_allocated': 0 }] return lots db = DbAccess() db.get_available_parking_lots = fake_get_available_parking_lot us = UserSessions() user_id = uuid4() us.add_user(user_id, None) engine = AllocationEngine(db, us) req = ParkingRequestMessage(location=Location(0.0, 0.0001), preferences={}) lot = await engine.handle_allocation_request(user_id, req) assert lot.id == 1
async def test_allocate_parking_lot_to_user(event_loop): with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot = ParkingLot(100, 'test_name', 0.0, Location(0.0, 1.0)) await db.insert_parking_lot(parking_lot) assert await db.allocate_parking_lot("test_user", 1) is True
async def test_location_update_message(ws_url, user_id, mocker, usessions): conn: WebSocketClientConnection = await tornado.websocket.websocket_connect( ws_url + user_id) mocker.spy(usessions, 'update_user_location') location = Location(0.0, 1.0) await conn.write_message(serialize_model(LocationUpdateMessage(location))) await sleep(0.01) usessions.update_user_location.assert_called_once_with(user_id, location)
async def test_location_message_callback(http_client, ws_url): f = asyncio.Future() car_ws = await clients.CarWebsocket.create( ws_url, { wsm.LocationUpdateMessage: f.set_result, }) location = Location(0.0, 1.0) await car_ws.send_location(location) response = await f assert response.location == location
async def test_no_parking_lots(http_client, base_url): car_id = str(uuid4()) car_cli = await CarWebsocket.create( base_url=base_url.replace('http', 'ws') + "/ws", user_id=car_id) logger.debug("car websocket client connected") response = await car_cli.send_parking_request(Location(0.0, 1.0), {}) logger.debug(f'requested allocation: {response}') allocated = await car_cli.receive(wsmodels.ErrorMessage) logger.debug(f'allocation failed: {allocated}') assert allocated.error.msg == 'No parking lot available.'
async def test_delete_parking_lot(http_client, base_url): lot = ParkingLot(100, 'test', 1.0, Location(0.0, 1.0)) response = await http_client.fetch(base_url + '/spaces', method='POST', headers=HEADERS, body=serialize_model(lot)) assert ParkingLotCreationResponse(**json.loads(response.body)).id == 1 # TODO: Check that the ParkingLot has actually been deleted using a GET # request. response = await http_client.fetch(base_url + '/spaces/1', method='DELETE') assert response.code == 200
async def test_get_parking_lot(event_loop): with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot = ParkingLot(100, 'test_name', 0.0, Location(0.0, 1.0089)) park_id = await db.insert_parking_lot(parking_lot) lot = await db.get_parking_lot(park_id) assert lot['name'] == parking_lot.name
async def test_insert_parking_lot(event_loop): with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot = ParkingLot(100, 'test_name', 0.0, Location(0.0, 1.0)) park_id = await db.insert_parking_lot(parking_lot) assert park_id == 1 async with db.pool.acquire() as conn: lst: List[Record] = await conn.fetch('SELECT * from ParkingLots;') assert len(lst) == 1
async def test_create_multiple_parking_lot(http_client, base_url): cli = ParkingLotRest(base_url, http_client) lot = ParkingLot(10, 'a', 1.0, Location(0.0, 1.0)) response = await cli.create_lot(lot) lot.id = response logger.debug(f'[test_create_parking_lot] created lot {lot.id}') lot2 = ParkingLot(10, 'b', 1.0, Location(2.0, 2.0)) response = await cli.create_lot(lot2) lot2.id = response logger.debug(f'[test_create_parking_lot] created lot2 {lot2.id}') car_id = str(uuid4()) car_cli = await CarWebsocket.create( base_url=base_url.replace('http', 'ws') + "/ws", user_id=car_id) logger.debug("car websocket client connected") response = await car_cli.send_parking_request(Location(0.0, 1.0), {}) logger.debug(f'requested allocation: {response}') allocated = await car_cli.receive(wsmodels.ParkingAllocationMessage) logger.debug(f'allocation recieved: {allocated}') assert allocated.lot.id == lot.id
async def test_add_allocation(event_loop): with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot = ParkingLot(100, 'test_name', 0.0, Location(0.0, 1.0089)) user_id = str(uuid4()) park_id = await db.insert_parking_lot(parking_lot) result = await db.allocate_parking_lot(user_id, park_id) assert result allocations = await db.get_parking_lot_allocations(park_id) assert allocations[0]['park_id'] == park_id assert allocations[0]['user_id'] == user_id
async def test_get_available_parking_lots(event_loop): with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot1 = ParkingLot(100, 'test_name1', 0.0, Location(0.0, 1.0089)) parking_lot2 = ParkingLot(100, 'test_name2', 0.0, Location(0.0, 1.01)) parking_lot3 = ParkingLot(100, 'test_name2', 0.0, Location(0.0, 1.0)) await db.insert_parking_lot(parking_lot1) await db.update_parking_lot_availability(1, 50) await db.insert_parking_lot(parking_lot2) await db.update_parking_lot_availability(2, 50) await db.insert_parking_lot(parking_lot3) await db.update_parking_lot_availability(3, 50) records = await db.get_available_parking_lots(location=Location( 0.0, 1.0), dist_meters=1000, exclusions=[]) assert len(records) == 2 assert records[0]['id'] == 3 assert records[0]['distance'] == 0 assert records[1]['id'] == 1 assert round(records[1]['distance']) == 991 records2 = await db.get_available_parking_lots(location=Location( 0.0, 1.0), dist_meters=1000, exclusions=[3]) assert len(records2) == 1 assert records2[0]['id'] == 1 assert round(records2[0]['distance']) == 991
async def test_update_parking_lot_price_and_availability_unsuccessful( event_loop): new_availability = 10 new_price = 5 with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot = ParkingLot(100, 'test_name', 0.0, Location(0.0, 1.0)) await db.insert_parking_lot(parking_lot) park_id = await db.update_parking_lot_availability(2, new_availability) assert park_id is None park_id = await db.update_parking_lot_price(2, new_price) assert park_id is None
async def test_no_parking_lots_retry_waiting(http_client, base_url): car_id = str(uuid4()) car_cli = await CarWebsocket.create( base_url=base_url.replace('http', 'ws') + "/ws", user_id=car_id) logger.debug("car websocket client connected") response = await car_cli.send_parking_request(Location(0.0, 1.0), {}) logger.debug(f'requested allocation: {response}') futs = [ car_cli.receive(wsmodels.ParkingAllocationMessage), car_cli.receive(wsmodels.ErrorMessage) ] (fut, ), *_ = await asyncio.wait(futs, return_when=asyncio.FIRST_COMPLETED) space = fut.result() assert isinstance(space, wsmodels.ErrorMessage) assert space.error.msg == 'No parking lot available.'
async def test_parking_request_message_no_parking_lot(ws_url, user_id, mocker, engine): user_location = Location(0.0, 1.0) parking_request = ParkingRequestMessage(user_location) mocker.patch.object(engine, 'handle_allocation_request') engine.handle_allocation_request.return_value = return_async_value(None) conn: WebSocketClientConnection = await tornado.websocket.websocket_connect( ws_url + user_id) await conn.write_message(serialize_model(parking_request)) msg = deserialize_ws_message(await conn.read_message()) engine.handle_allocation_request.assert_called_once_with( user_id, parking_request) assert WebSocketErrorType( msg.error) == WebSocketErrorType.NO_AVAILABLE_PARKING_LOT
async def handle_allocation_request(self, user_id: str, request: ParkingRequestMessage) -> Optional[ParkingLotAllocation]: user_rejections = self.user_sessions.get_user(user_id).rejections if 'distance' in request.preferences: max_distance = request.preferences['distance'] else: max_distance = MAX_DISTANCE lots = await self.dba.get_available_parking_lots(request.location, max_distance, user_rejections) # Just take the first lot for now... if lots: lot = lots[0] lot_loc = (lot['lat'], lot['long']) req_loc = (request.location.latitude, request.location.longitude) dist = distance(lot_loc, req_loc).meters return ParkingLotAllocation(lot['capacity'], lot['name'], lot['price'], Location(lot['lat'], lot['long']), lot['id'], dist, lot['num_available']) else: return None
async def test_update_parking_lot_price_and_availability(event_loop): new_availability = 10 new_price = 5 with Postgresql() as postgresql: db = await DbAccess.create(postgresql.url(), event_loop, reset_tables=True) parking_lot = ParkingLot(100, 'test_name', 0.0, Location(0.0, 1.0)) park_id = await db.insert_parking_lot(parking_lot) park_id = await db.update_parking_lot_availability( park_id, new_availability) assert park_id == 1 park_id = await db.update_parking_lot_price(park_id, new_price) assert park_id == 1 async with db.pool.acquire() as conn: p_record: Record = await conn.fetchrow('SELECT * from ParkingLots;' ) assert p_record['num_available'] == new_availability assert p_record['price'] == new_price
def test_location_missing_arg(): with pytest.raises(TypeError): Location(0.0)
def test_location_cons(): loc = Location(0.0, 0.0) assert isinstance(loc, Location)
def test_location_incorrect_arg_type(): with pytest.raises(TypeError): Location(0.0, 'a')
def test_message_type(): msg = LocationUpdateMessage(Location(0.0, 1.0)) assert msg._type == WebSocketMessageType.LOCATION_UPDATE
async def test_create_parking_lot_new(plr): lot = ParkingLot(100, 'test', 1.0, Location(0.0, 1.0)) lot_id = await plr.create_lot(lot) assert lot_id == 1
import attr import pytest from parking.shared.location import Location from parking.shared.rest_models import (ParkingLot, ParkingLotCreationResponse, SpaceAvailableMessage, SpacePriceMessage) loc = Location(0.0, 1.0) def test_correct_parkinglot_cons(): assert isinstance(ParkingLot(100, 'test_name', 0.0, loc), ParkingLot) def test_missing_parkinglot_arg(): with pytest.raises(TypeError): ParkingLot(100, 'test_name', 0) def test_incorrect_parkinglot_arg_type(): with pytest.raises(TypeError): ParkingLot(100, 0, 0.0, loc) def test_incorrect_parkinglot_arg(): with pytest.raises(ValueError): ParkingLot(-100, 'test_name', 0.0, loc) def test_zero_capacity_parkinglot(): with pytest.raises(ValueError): ParkingLot(0, 'test_name', 0.0, loc)