passerelle/tests/test_planitech.py

1073 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"