import collections from datetime import datetime from unittest import mock import pytest import requests from django.contrib.contenttypes.models import ContentType from django.core.cache import cache from httmock import HTTMock, urlmatch from passerelle.base.models import AccessRight, ApiUser from passerelle.contrib.planitech import mste from passerelle.contrib.planitech.models import Pairing, PlanitechConnector, get_extensions from passerelle.utils.jsonresponse import APIError def assert_mste(data, ref_data): """skip CRC verification""" assert len(data) == len(ref_data) for i, v in enumerate(data): if i != 2: assert v == ref_data[i] @pytest.mark.parametrize( "data,mste_data", [ (None, ["MSTE0102", 6, "CRC82413E70", 0, 0, 0]), ("toto", ["MSTE0102", 7, "CRCD45ACB10", 0, 0, 21, "toto"]), # string (mste.Couple(("toto", "tata")), ["MSTE0102", 10, "CRCD45ACB10", 0, 0, 32, 21, "toto", 21, "tata"]), # couple ( [mste.Couple(("toto", "tata")), mste.Couple(("toto", "tata"))], ["MSTE0102", 14, "CRCD45ACB10", 0, 0, 31, 2, 32, 21, "toto", 21, "tata", 9, 1], ), # couple are stored in refs (["toto"], ["MSTE0102", 9, "CRCD4E14B75", 0, 0, 31, 1, 21, "toto"]), # array ( ["toto", "tata", "toto"], ["MSTE0102", 13, "CRC7311752F", 0, 0, 31, 3, 21, "toto", 21, "tata", 9, 1], ), # array with reference ({"mykey": "toto"}, ["MSTE0102", 11, "CRC1C9E9FE1", 0, 1, "mykey", 30, 1, 0, 21, "toto"]), # dictionnary ( [{"mykey": "toto"}, {"mykey": "toto"}], ["MSTE0102", 15, "CRC1C9E9FE1", 0, 1, "mykey", 31, 2, 30, 1, 0, 21, "toto", 9, 1], ), # dictionnary are stored in refs (float(2), ["MSTE0102", 7, "CRC1C9E9FE1", 0, 0, 20, 2]), # decimal ([float(2), float(2)], ["MSTE0102", 11, "CRC1C9E9FE1", 0, 0, 31, 2, 20, 2, 9, 1]), # decimal are stored in refs (mste.Uint32(1), ["MSTE0102", 7, "CRC1C9E9FE1", 0, 0, 15, 1]), # uint32 (True, ["MSTE0102", 6, "CRC1C9E9FE1", 0, 0, 1]), # True (False, ["MSTE0102", 6, "CRC1C9E9FE1", 0, 0, 2]), # False ('', ["MSTE0102", 6, "CRC1C9E9FE1", 0, 0, 3]), # empty string (datetime.fromtimestamp(1537364340), ["MSTE0102", 7, "CRC1C9E9FE1", 0, 0, 22, 1537364340]), # local date ( [datetime.fromtimestamp(1537364340), datetime.fromtimestamp(1537364340)], ["MSTE0102", 11, "CRC1C9E9FE1", 0, 0, 31, 2, 22, 1537364340, 9, 1], ), # local date in refs ], ) def test_mste(mste_data, data): assert data == mste.decode(mste_data) assert_mste(mste.encode(data), mste_data) def test_encode_unsupported_type(): with pytest.raises(TypeError): mste.encode(set()) def test_real(): mste_data = [ "MSTE0102", 128, "CRC99D9BCEB", 0, 11, "requestDate", "responseDate", "requestName", "requestedEndingTime", "availablePlaces", "label", "freeGaps", "placeIdentifier", "resourceIdentifier", "daysMask", "requestedStartingTime", 30, 7, 0, 22, 1538404500, 1, 22, 1538404500, 2, 21, "getFreeGaps", 3, 20, 600, 4, 31, 1, 30, 4, 5, 21, "M.F.F. 2", 6, 31, 15, 32, 22, 1538384400, 22, 1538388000, 32, 22, 1538470800, 22, 1538474400, 32, 22, 1538557200, 22, 1538560800, 32, 22, 1538643600, 22, 1538647200, 32, 22, 1538989200, 22, 1538992800, 32, 22, 1539075600, 22, 1539079200, 32, 22, 1539162000, 22, 1539165600, 32, 22, 1539248400, 22, 1539252000, 32, 22, 1539334800, 22, 1539338400, 32, 22, 1539507600, 22, 1539511200, 32, 22, 1539594000, 22, 1539597600, 32, 22, 1539680400, 22, 1539684000, 32, 22, 1539766800, 22, 1539770400, 32, 22, 1539853200, 22, 1539856800, 32, 22, 1539939600, 22, 1539943200, 7, 20, 2, 8, 9, 54, 9, 20, 127, 10, 20, 540, ] mste.decode(mste_data) @pytest.fixture() def connector(db): api = ApiUser.objects.create(username='all', keytype='', key='') connector = PlanitechConnector.objects.create( url='http://example.planitech.com/', username='admin', password='admin', verify_cert=False, slug='slug-planitech', custom_fields={'places': [{'name': 'some_custom_field', 'type': 'string'}]}, price_code='T1', ) obj_type = ContentType.objects.get_for_model(connector) AccessRight.objects.create( codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=connector.pk ) return connector def mock_planitech(monkeypatch, return_value=None, side_effect=None, referential=None): from passerelle.contrib.planitech import models monkeypatch.setattr(models.PlanitechConnector, '_login', mock.Mock()) kwargs = {} if return_value is not None: kwargs['return_value'] = return_value if side_effect is not None: kwargs['side_effect'] = side_effect mock_call_planitech = mock.Mock(**kwargs) monkeypatch.setattr(models.PlanitechConnector, '_call_planitech', mock_call_planitech) if referential is not None: mock_get_referential = mock.Mock(return_value=referential) monkeypatch.setattr(models.PlanitechConnector, '_raw_get_places_referential', mock_get_referential) return mock_call_planitech def test_call_planitech(connector, monkeypatch): class MockResponse: status_code = 200 content = None def __init__(self, content=None, status_code=None): if content is not None: self.content = content if status_code is not None: self.status_code = status_code def session_meth(self, *args, **kwargs): return self def json(self): return mste.encode(self.content) connector._planitech_session = True response = MockResponse(content='somestring') assert connector._call_planitech(response.session_meth, 'endpoint') == "somestring" response = MockResponse(content=set(), status_code=400) with pytest.raises(APIError) as excinfo: connector._call_planitech(response.session_meth, 'endpoint') assert str(excinfo.value) == 'Planitech error 400' response = MockResponse(content='unexpected error format', status_code=400) with pytest.raises(APIError) as excinfo: connector._call_planitech(response.session_meth, 'endpoint') assert str(excinfo.value) == 'Planitech error 400' response = MockResponse(content={'errors': 'planitech error message'}, status_code=400) with pytest.raises(APIError) as excinfo: connector._call_planitech(response.session_meth, 'endpoint') assert str(excinfo.value) == 'Planitech error 400 - planitech error message' def test_create_reservation(app, connector, monkeypatch): side_effect = [ { 'creationStatus': 'OK', 'externalUserIdentifier': '22b9c0d91fdc4f379d1356a4aaa9d38b', 'requestDate': datetime(2019, 1, 9, 15, 41), 'requestName': 'createPerson', 'responseDate': datetime(2019, 1, 9, 15, 41), 'userIdentifier': 13826.0, }, {'creationStatus': 'OK', 'reservationIdentifier': 1}, ] mock_call_planitech = mock_planitech(monkeypatch, side_effect=side_effect) params = { 'date': '2018-11-11', 'start_time': '10:00', 'end_time': '11:00', 'place_id': 1, 'price': 10, 'name_id': 'john-doe', 'first_name': 'jon', 'last_name': 'doe', 'email': 'jon.doe@localhost', 'activity_id': 1, 'object': 'reservation object', 'type_id': 1, 'vat_rate': 1, } response = app.post_json('/planitech/slug-planitech/createreservation', params=params) json_resp = response.json assert json_resp['err'] == 0 assert json_resp['data']['reservation_id'] == 1 person_args = mock_call_planitech.call_args_list[0][0] assert person_args[1] == 'createPerson' person_args = person_args[2] assert person_args['mail'] == 'jon.doe@localhost' assert person_args['firstName'] == 'jon' assert person_args['name'] == 'doe' assert person_args['pricingCode'] == 'T1' external_id = person_args['externalUserIdentifier'] pairing = Pairing.objects.get(resource=connector, name_id='john-doe') assert pairing.external_id == external_id assert pairing.price_code == 'T1' reservation_args = mock_call_planitech.call_args_list[1][0] assert reservation_args[1] == 'createReservation' reservation_args = reservation_args[2] assert reservation_args['start'] == datetime(2018, 11, 11, 10, 0) assert reservation_args['end'] == datetime(2018, 11, 11, 11, 0) assert reservation_args['places'] == [1] assert reservation_args['price'] == 10 assert reservation_args['contractorExternalIdentifier'] == pairing.external_id assert reservation_args['activityID'] == 1 assert reservation_args['object'] == 'reservation object' assert reservation_args['typeID'] == 1 assert reservation_args['vatRate'] == 1 # Second reservation for same user : no planitech person created mock_call_planitech = mock_planitech(monkeypatch, return_value=side_effect[1]) response = app.post_json('/planitech/slug-planitech/createreservation', params=params) json_resp = response.json assert json_resp['err'] == 0 assert json_resp['data']['reservation_id'] == 1 reservation_args = mock_call_planitech.call_args_list[0][0] assert reservation_args[1] == 'createReservation' assert Pairing.objects.count() == 1 # Changing price code update pairing and planitec user new_params = params.copy() new_params['price_code'] = 'T2' new_side_effect = side_effect[:] new_side_effect[0] = { 'modificationStatus': 'OK', 'externalUserIdentifier': '22b9c0d91fdc4f379d1356a4aaa9d38b', 'requestDate': datetime(2019, 1, 9, 15, 41), 'requestName': 'updatePerson', 'responseDate': datetime(2019, 1, 9, 15, 41), } mock_call_planitech = mock_planitech(monkeypatch, side_effect=new_side_effect) response = app.post_json('/planitech/slug-planitech/createreservation', params=new_params) json_resp = response.json assert json_resp['err'] == 0 assert json_resp['data']['reservation_id'] == 1 person_args = mock_call_planitech.call_args_list[0][0] assert person_args[1] == 'updatePerson' person_args = person_args[2] assert person_args['pricingCode'] == 'T2' external_id = person_args['externalUserIdentifier'] assert Pairing.objects.count() == 1 pairing = Pairing.objects.get(external_id=external_id) assert pairing.price_code == 'T2' reservation_args = mock_call_planitech.call_args_list[1][0] assert reservation_args[1] == 'createReservation' # Create reservation failed mock_call_planitech = mock_planitech( monkeypatch, return_value={'creationStatus': 'NOTOK', 'reservationIdentifier': 1} ) response = app.post_json('/planitech/slug-planitech/createreservation', params=params) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == 'Reservation creation failed: NOTOK' # Create reservation failed - nor reservation ID mock_call_planitech = mock_planitech(monkeypatch, return_value={'creationStatus': 'OK'}) response = app.post_json('/planitech/slug-planitech/createreservation', params=params) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == 'Reservation creation failed: no reservation ID' def test_get_reservation_price(app, connector, monkeypatch): side_effect = [ { 'creationStatus': 'OK', 'externalUserIdentifier': '22b9c0d91fdc4f379d1356a4aaa9d38b', 'requestDate': datetime(2019, 1, 9, 15, 41), 'requestName': 'createPerson', 'responseDate': datetime(2019, 1, 9, 15, 41), 'userIdentifier': 13826.0, }, {'calculationStatus': 'OK', 'calculatedPrice': 10}, ] mock_call_planitech = mock_planitech(monkeypatch, side_effect=side_effect) params = { 'date': '2018-11-11', 'start_time': '10:00', 'end_time': '11:00', 'place_id': 1, 'name_id': 'john-doe', 'first_name': 'jon', 'last_name': 'doe', 'email': 'jon.doe@localhost', 'activity_id': 1, 'type_id': 1, } response = app.post_json('/planitech/slug-planitech/getreservationprice', params=params) json_resp = response.json assert json_resp['err'] == 0 assert json_resp['data']['price'] == 10 person_args = mock_call_planitech.call_args_list[0][0] assert person_args[1] == 'createPerson' person_args = person_args[2] assert person_args['mail'] == 'jon.doe@localhost' assert person_args['firstName'] == 'jon' assert person_args['name'] == 'doe' assert person_args['pricingCode'] == 'T1' external_id = person_args['externalUserIdentifier'] pairing = Pairing.objects.get(resource=connector, name_id='john-doe') assert pairing.external_id == external_id assert pairing.price_code == 'T1' reservation_args = mock_call_planitech.call_args_list[1][0] assert reservation_args[1] == 'getFutureReservationPrice' reservation_args = reservation_args[2] assert reservation_args['start'] == datetime(2018, 11, 11, 10, 0) assert reservation_args['end'] == datetime(2018, 11, 11, 11, 0) assert reservation_args['places'] == [1] assert reservation_args['contractorExternalIdentifier'] == pairing.external_id assert reservation_args['activityID'] == 1 assert reservation_args['typeID'] == 1 # Second call for same user : no planitech person created mock_call_planitech = mock_planitech(monkeypatch, return_value=side_effect[1]) response = app.post_json('/planitech/slug-planitech/getreservationprice', params=params) json_resp = response.json assert json_resp['err'] == 0 assert json_resp['data']['price'] == 10 reservation_args = mock_call_planitech.call_args_list[0][0] assert reservation_args[1] == 'getFutureReservationPrice' assert Pairing.objects.count() == 1 # Failure mock_call_planitech = mock_planitech( monkeypatch, return_value={'calculationStatus': 'NOTOK', 'calculatedPrice': 10} ) response = app.post_json('/planitech/slug-planitech/getreservationprice', params=params) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == 'Get reservation price failed: NOTOK' # Create reservation failed - nor reservation ID mock_call_planitech = mock_planitech(monkeypatch, return_value={'calculationStatus': 'OK'}) response = app.post_json('/planitech/slug-planitech/getreservationprice', params=params) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == 'Get reservation price failed: no price' def test_getplaces_referential(app, connector, monkeypatch): side_effect = [ { 'placesList': [ {'identifier': 1.0, 'label': 'salle 1'}, {'identifier': 2.0, 'label': 'salle 2'}, {'identifier': 3.0, 'label': 'salle 3'}, {'identifier': 4.0, 'label': 'salle 4'}, ] }, { 'requestedPlaces': [ { 'identifier': 1.0, 'capacity': 10.0, 'streetNumber': 1, 'address1': 'rue planitech', 'city': 'thecity', 'zipCode': '00000', }, {'identifier': 2.0, 'capacity': 20.0, 'some_custom_field': 'Yes'}, {'identifier': 3.0, 'capacity': 30.0, 'some_custom_field': 'Yes'}, {'identifier': 4.0, 'capacity': None}, ] }, ] mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplacesreferential') expected_res = { '4': { 'capacity': None, 'label': 'salle 4', 'identifier': 4, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': None, }, '3': { 'capacity': 30, 'label': 'salle 3', 'identifier': 3, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', }, '2': { 'capacity': 20, 'label': 'salle 2', 'identifier': 2, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', }, '1': { 'capacity': 10, 'label': 'salle 1', 'identifier': 1, 'street_number': 1, 'address': 'rue planitech', 'city': 'thecity', 'zipcode': '00000', 'some_custom_field': None, }, } assert response.json['data'] == expected_res # Filter on custom fields mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplacesreferential?some_custom_field=Yes') assert response.json['data'] == { '3': { 'capacity': 30, 'label': 'salle 3', 'identifier': 3, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', }, '2': { 'capacity': 20, 'label': 'salle 2', 'identifier': 2, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', }, } # Filter on custom fields and capacity mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplacesreferential?some_custom_field=Yes&min_capacity=25') assert response.json['data'] == { '3': { 'capacity': 30, 'label': 'salle 3', 'identifier': 3, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', } } # Unkown filter filters nothing response = app.get('/planitech/slug-planitech/getplacesreferential?unkownfilter=foo') expected_res = { '4': { 'capacity': None, 'label': 'salle 4', 'identifier': 4, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': None, }, '3': { 'capacity': 30, 'label': 'salle 3', 'identifier': 3, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', }, '2': { 'capacity': 20, 'label': 'salle 2', 'identifier': 2, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', }, '1': { 'capacity': 10, 'label': 'salle 1', 'identifier': 1, 'street_number': 1, 'address': 'rue planitech', 'city': 'thecity', 'zipcode': '00000', 'some_custom_field': None, }, } assert response.json['data'] == expected_res def test_getplace(app, connector, monkeypatch): side_effect = [ {'placesList': [{'identifier': 1.0, 'label': 'salle 1'}, {'identifier': 2.0, 'label': 'salle 2'}]}, { 'requestedPlaces': [ { 'identifier': 1.0, 'capacity': 10.0, 'streetNumber': 1, 'address1': 'rue planitech', 'city': 'thecity', 'zipCode': '00000', }, {'identifier': 2.0, 'capacity': 20.0, 'some_custom_field': 'Yes'}, ] }, ] # filter by identifier mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplace?id=2') assert response.json['data'] == { 'capacity': 20, 'label': 'salle 2', 'identifier': 2, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, 'some_custom_field': 'Yes', } # bad value mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplace?id=toto') assert response.json['err'] == 1 assert response.json['err_desc'] == 'ID must be an integer' # missing place mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplace?id=12') assert response.json['err'] == 1 assert response.json['err_desc'] == 'No place with ID 12' def test_getplaces_referential_no_configuration(app, connector, monkeypatch): # Custom fields not returned if no configuration connector.custom_fields = None connector.save() side_effect = [ {'placesList': [{'identifier': 1.0, 'label': 'salle 1'}, {'identifier': 2.0, 'label': 'salle 2'}]}, { 'requestedPlaces': [ { 'identifier': 1.0, 'capacity': 10.0, 'streetNumber': 1, 'address1': 'rue planitech', 'city': 'thecity', 'zipCode': '00000', }, {'identifier': 2.0, 'capacity': 20.0, 'some_custom_field': 'Yes'}, ] }, ] mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplacesreferential') mock_planitech(monkeypatch, side_effect=side_effect) response = app.get('/planitech/slug-planitech/getplacesreferential') expected_res = { '2': { 'capacity': 20, 'label': 'salle 2', 'identifier': 2, 'street_number': None, 'address': None, 'city': None, 'zipcode': None, }, '1': { 'capacity': 10, 'label': 'salle 1', 'identifier': 1, 'street_number': 1, 'address': 'rue planitech', 'city': 'thecity', 'zipcode': '00000', }, } assert response.json['data'] == expected_res def test_getplaces_referential_use_cache(app, connector): cache_key = 'planitech-%s-places' % connector.id cache.set(cache_key, {'some': {'data': 'foo'}}) response = app.get('/planitech/slug-planitech/getplacesreferential') assert response.json_body['data'] == {'some': {'data': 'foo'}} cache.delete(cache_key) def test_login(connector): @urlmatch(netloc=r'(.*\.)?planitech\.com$') def planitech_mock(url, request): raise requests.exceptions.RequestException("Bad news") with HTTMock(planitech_mock): with pytest.raises(APIError) as excinfo: connector._login() assert str(excinfo.value) == 'Authentication to Planitec failed: Bad news' def test_update_reservation(app, connector, monkeypatch): mock_call_planitech = mock_planitech(monkeypatch, return_value={'modificationStatus': 'OK'}) response = app.post_json( '/planitech/slug-planitech/updatereservation', params={'status': 'confirmed', 'reservation_id': 1} ) json_resp = response.json assert json_resp['err'] == 0 assert json_resp['data']['raw_data'] == {'modificationStatus': 'OK'} call_params = mock_call_planitech.call_args[0][2] assert call_params['reservationIdentifier'] == 1 assert call_params['situation'] == 3 # Update failed mock_planitech(monkeypatch, return_value={'modificationStatus': 'NOTOK'}) response = app.post_json( '/planitech/slug-planitech/updatereservation', params={'status': 'confirmed', 'reservation_id': 1} ) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == 'Update reservation failed: NOTOK' # Connector bad param response = app.post_json( '/planitech/slug-planitech/updatereservation', params={'status': 'confirmed'}, status=400 ) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == "'reservation_id' is a required property" def test_get_reservations_infos(app, connector, monkeypatch): mock_call_planitech = mock_planitech( monkeypatch, return_value={ 'requestedReservations': [ { 'reservation': 'data', 'activity': {'reservations': 'activity_data'}, 'contractor': {'reservations': 'contractor_data'}, } ] }, ) response = app.get('/planitech/slug-planitech/getreservationsinfo/?reservation_id=1') json_resp = response.json assert json_resp['err'] == 0 assert json_resp['data'] == { 'reservation': 'data', 'activity': {}, 'contractor': {}, } assert mock_call_planitech.call_args[0][1] == 'getReservationsInfo' assert mock_call_planitech.call_args[0][2] == {'reservationIdentifiers': [1.0]} def freegaps_data(): referential = collections.OrderedDict( [ (1.0, {'capacity': 10.0, 'label': 'salle 1', 'identifier': 1.0}), ( 2.0, {'capacity': 20.0, 'label': 'salle 2', 'identifier': 2.0, 'some_custom_field': 'Yes'}, ), ( 3.0, {'capacity': 30.0, 'label': 'salle 3', 'identifier': 3.0, 'some_custom_field': 'Yes'}, ), ] ) place1_gaps = [ [ datetime(year=2018, month=11, day=11, hour=10, minute=0), datetime(year=2018, month=11, day=11, hour=11, minute=0), ], [ datetime(year=2018, month=11, day=12, hour=10, minute=0), datetime(year=2018, month=11, day=12, hour=11, minute=0), ], ] place2_gaps = [ [ datetime(year=2018, month=11, day=11, hour=10, minute=0), datetime(year=2018, month=11, day=11, hour=11, minute=0), ], [ datetime(year=2018, month=11, day=12, hour=10, minute=0), datetime(year=2018, month=11, day=12, hour=11, minute=0), ], [ datetime(year=2018, month=11, day=13, hour=10, minute=0), datetime(year=2018, month=11, day=13, hour=11, minute=0), ], ] planitech_r = { 'availablePlaces': [ {'placeIdentifier': 1.0, 'freeGaps': place1_gaps}, {'placeIdentifier': 2.0, 'freeGaps': place2_gaps}, ] } return referential, planitech_r def test_get_freegaps(app, connector, monkeypatch, settings): settings.LANGUAGE_CODE = 'fr-fr' referential, planitech_r = freegaps_data() mock_call_planitech = mock_planitech(monkeypatch, return_value=planitech_r, referential=referential) # date display response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00' '&start_date=2018-11-11&display=date' ) assert response.json['data'] == [ {'id': '2018-11-11', 'text': 'dimanche 11 novembre 2018', 'short_text': '11/11/2018'}, {'id': '2018-11-12', 'text': 'lundi 12 novembre 2018', 'short_text': '12/11/2018'}, {'id': '2018-11-13', 'text': 'mardi 13 novembre 2018', 'short_text': '13/11/2018'}, ] # place display response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00' '&start_date=2018-11-11&display=place' ) assert response.json['data'] == [{'id': 1.0, 'text': 'salle 1'}, {'id': 2.0, 'text': 'salle 2'}] # full display response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00' '&start_date=2018-11-11&display=full' ) res = response.json['data'] assert 'place' in res assert 'date' in res full = res['full'] assert len(full) == 3 place_1 = full[0] assert place_1['id'] == 1.0 assert place_1['text'] == 'salle 1' assert place_1['dates'] == [ {'available': True, 'id': '2018-11-11'}, {'available': True, 'id': '2018-11-12'}, {'available': False, 'id': '2018-11-13'}, ] place_2 = full[1] assert place_2['id'] == 2.0 assert place_2['text'] == 'salle 2' assert place_2['dates'] == [ {'available': True, 'id': '2018-11-11'}, {'available': True, 'id': '2018-11-12'}, {'available': True, 'id': '2018-11-13'}, ] place_3 = full[2] assert place_3['id'] == 3.0 assert place_3['text'] == 'salle 3' assert place_3['dates'] == [ {'available': False, 'id': '2018-11-11'}, {'available': False, 'id': '2018-11-12'}, {'available': False, 'id': '2018-11-13'}, ] # general params interpretation mock_call_planitech.reset_mock() response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00' '&start_date=2018-11-11&end_date=2018-11-12&display=date' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0) assert call_params['endingDate'] == datetime(2018, 11, 12, 00, 0) assert call_params['placeIdentifiers'] == [1.0, 2.0, 3.0] assert call_params['requestedStartingTime'] == 0.0 # means startingDate assert call_params['requestedEndingTime'] == 60.0 # means startingDate + 60 minutes assert 'reservationDays' not in call_params # no end date means only the starting date is interesting # so ending date is the day after at midnight # which gives only results for starting date mock_call_planitech.reset_mock() response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00' '&start_date=2018-11-11&display=place' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0) assert call_params['endingDate'] == datetime(2018, 11, 12, 0, 0) # capacity used against referential to restrict placeIdentifiers mock_call_planitech.reset_mock() app.get( '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00' '&start_date=2018-11-11&max_capacity=15&display=place' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['placeIdentifiers'] == [1.0] # place_id disable filtering mock_call_planitech.reset_mock() response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00' '&start_date=2018-11-11&max_capacity=15&place_id=2&display=place' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['placeIdentifiers'] == [2.0] # weekdays mock_call_planitech.reset_mock() response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00' '&start_date=2018-11-11&max_capacity=15&place_id=2&display=place' '&weekdays=0,1,2' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['reservationDays'] == [0, 1, 2] # custom field restriction mock_call_planitech.reset_mock() response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00' '&start_date=2018-11-11&some_custom_field=Yes&display=place' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['placeIdentifiers'] == [2.0, 3.0] # custom field and capacity restriction mock_call_planitech.reset_mock() response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00' '&start_date=2018-11-11&some_custom_field=Yes&min_capacity=25&display=place' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['placeIdentifiers'] == [3.0] # BAD REQUEST # bad date format response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=11:00&&end_time=14:00' '&start_date=notadate&display=place', status=400, ) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == 'invalid value for parameter "start_date (YYYY-MM-DD expected)"' # bad time format response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=notatime&&end_time=14:00' '&start_date=2018-11-11&display=place' ) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == "Invalid time format: notatime" # start_date or start_days required response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=notatime&&end_time=14:00&display=place' ) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == "start_date or start_days is required" # invalid display param response = app.get( '/planitech/slug-planitech/getfreegaps?start_time=notatime&&end_time=14:00' '&start_date=2018-11-11&display=unkown' ) json_resp = response.json assert json_resp['err'] == 1 assert json_resp['err_desc'] == "Valid display are: date, place, full" def test_get_freegaps_start_days(app, connector, monkeypatch, settings, freezer): freezer.move_to('2018-11-09 23:50:00') settings.LANGUAGE_CODE = 'fr-fr' referential, planitech_r = freegaps_data() mock_call_planitech = mock_planitech(monkeypatch, return_value=planitech_r, referential=referential) # starting_date and ending_date can be specified as days delta from now app.get( '/planitech/slug-planitech/getfreegaps?start_time=10:00&&end_time=11:00' '&start_days=2&end_days=4&display=place' ) call_params = mock_call_planitech.call_args[0][2] assert call_params['startingDate'] == datetime(2018, 11, 11, 10, 0) assert call_params['endingDate'] == datetime(2018, 11, 13, 0, 0) def test_get_extensions(app, connector): post_data = { 'extension_0_name': 'foo', 'extension_0_value': 'foo_value', 'extension_1_name': 'bar', 'extension_1_value': 'bar_value', } extensions = get_extensions(post_data) assert len(extensions) == 2 for extension in extensions: assert extension['name'] in ('foo', 'bar') assert extension['value'] == extension['name'] + '_value' assert extension['type'] == 'string' post_data = { 'extension_aa': 'foo', } with pytest.raises(APIError) as excinfo: extensions = get_extensions(post_data) assert str(excinfo.value) == "Wrong extension format for : 'extension_aa'" post_data = { 'extension_0_aa': 'foo', } with pytest.raises(APIError) as excinfo: extensions = get_extensions(post_data) assert str(excinfo.value) == "Missing 'name' in extension"