1071 lines
36 KiB
Python
1071 lines
36 KiB
Python
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"
|