364 lines
14 KiB
Python
364 lines
14 KiB
Python
# chrono - agendas system
|
|
# Copyright (C) 2023 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import datetime
|
|
|
|
import pytest
|
|
|
|
from chrono.agendas.models import Booking, Lease
|
|
from chrono.utils.timezone import now
|
|
from tests.utils import build_event_agenda, build_meetings_agenda, build_virtual_agenda
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_meetings_agenda(app, user):
|
|
'''Test fillslot on meetings agenda with lock_code'''
|
|
agenda = build_meetings_agenda(
|
|
'Agenda',
|
|
resources=['Re1'],
|
|
meeting_types=(30,),
|
|
desks={'desk-1': ['monday-friday 9:00-12:00 14:00-17:00']},
|
|
)
|
|
|
|
datetimes_url = agenda._mt_30.api_datetimes_url
|
|
|
|
# list free slots, with or without a lock
|
|
resp = app.get(datetimes_url + '?lock_code=MYLOCK')
|
|
free_slots = len(resp.json['data'])
|
|
resp = app.get(datetimes_url + '?lock_code=OTHERLOCK')
|
|
assert free_slots == len(resp.json['data'])
|
|
resp = app.get(datetimes_url)
|
|
assert free_slots == len(resp.json['data'])
|
|
|
|
# lock a slot
|
|
fillslot_url = resp.json['data'][2]['api']['fillslot_url']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
app.post_json(fillslot_url, params={'lock_code': 'MYLOCK'})
|
|
assert Booking.objects.count() == 1
|
|
assert Lease.objects.get().lock_code == 'MYLOCK'
|
|
|
|
# list free slots: one is locked ...
|
|
resp = app.get(datetimes_url)
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 1
|
|
|
|
resp = app.get(datetimes_url, params={'lock_code': 'OTHERLOCK'})
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 1
|
|
|
|
# ... unless it's MYLOCK
|
|
resp = app.get(datetimes_url, params={'lock_code': 'MYLOCK'})
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 0
|
|
|
|
# can't lock the same timeslot ...
|
|
resp_booking = app.post_json(fillslot_url, params={'lock_code': 'OTHERLOCK'})
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
# ... unless with MYLOCK (aka "relock")
|
|
resp_booking = app.post_json(fillslot_url, params={'lock_code': 'MYLOCK'})
|
|
assert resp_booking.json['err'] == 0
|
|
assert Booking.objects.count() == 1
|
|
assert Lease.objects.get().lock_code == 'MYLOCK'
|
|
|
|
# can't book the slot ...
|
|
resp_booking = app.post_json(fillslot_url)
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
resp_booking = app.post_json(fillslot_url, params={'confirm_after_lock': True})
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
resp_booking = app.post_json(fillslot_url, params={'lock_code': 'OTHERLOCK', 'confirm_after_lock': True})
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
# ... unless with MYLOCK (aka "confirm")
|
|
resp_booking = app.post_json(fillslot_url, params={'lock_code': 'MYLOCK', 'confirm_after_lock': True})
|
|
assert resp_booking.json['err'] == 0
|
|
assert Booking.objects.count() == 1
|
|
assert Lease.objects.count() == 0
|
|
|
|
|
|
def test_meetings_agenda_expiration(app, user, freezer):
|
|
'''Test fillslot on meetings agenda with lock_code'''
|
|
agenda = build_meetings_agenda(
|
|
'Agenda',
|
|
resources=['Re1'],
|
|
meeting_types=(30,),
|
|
desks={'desk-1': ['monday-friday 9:00-12:00 14:00-17:00']},
|
|
)
|
|
datetimes_url = agenda._mt_30.api_datetimes_url
|
|
|
|
# list free slots
|
|
resp = app.get(datetimes_url)
|
|
|
|
# lock a slot
|
|
fillslot_url = resp.json['data'][2]['api']['fillslot_url']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
app.post_json(fillslot_url, params={'lock_code': 'MYLOCK'})
|
|
assert Booking.objects.count() == 1
|
|
assert Lease.objects.get().lock_code == 'MYLOCK'
|
|
|
|
# list free slots: one is locked ...
|
|
resp = app.get(datetimes_url)
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 1
|
|
|
|
# after 30 minutes it is not locked anymore
|
|
freezer.move_to(datetime.timedelta(minutes=30))
|
|
resp = app.get(datetimes_url)
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 0
|
|
|
|
|
|
def test_meetings_agenda_with_resource_exclusion(app, user):
|
|
'''Test fillslot on meetings agenda with lock_code and ressources'''
|
|
agenda1 = build_meetings_agenda(
|
|
'Agenda 1',
|
|
resources=['Re1'],
|
|
meeting_types=(30,),
|
|
desks={'desk-1': ['monday-friday 9:00-12:00 14:00-17:00']},
|
|
)
|
|
agenda2 = build_meetings_agenda(
|
|
'Agenda 2',
|
|
resources=['Re1'],
|
|
meeting_types=(30,),
|
|
desks={'desk-1': ['monday-friday 9:00-12:00 14:00-17:00']},
|
|
)
|
|
resource = agenda2._re_re1
|
|
agenda1_datetimes_url = agenda1._mt_30.api_datetimes_url
|
|
agenda2_datetimes_url = agenda2._mt_30.api_datetimes_url
|
|
|
|
# list free slots, with or without a lock
|
|
resp = app.get(agenda1_datetimes_url, params={'lock_code': 'OTHERLOCK', 'resources': resource.slug})
|
|
free_slots = len(resp.json['data'])
|
|
resp = app.get(agenda1_datetimes_url, params={'lock_code': 'MYLOCK', 'resources': resource.slug})
|
|
assert free_slots == len(resp.json['data'])
|
|
resp = app.get(agenda1_datetimes_url, params={'resources': resource.slug})
|
|
assert free_slots == len(resp.json['data'])
|
|
resp = app.get(agenda1_datetimes_url)
|
|
assert free_slots == len(resp.json['data'])
|
|
|
|
# lock a slot
|
|
fillslot_url = resp.json['data'][2]['api']['fillslot_url']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
app.post_json(fillslot_url + '?resources=re1', params={'lock_code': 'MYLOCK'})
|
|
assert Booking.objects.count() == 1
|
|
assert Lease.objects.get().lock_code == 'MYLOCK'
|
|
|
|
# list free slots: one is locked ...
|
|
resp = app.get(agenda1_datetimes_url, params={'resources': resource.slug})
|
|
assert free_slots == len(resp.json['data'])
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 1
|
|
|
|
resp = app.get(agenda1_datetimes_url, params={'lock_code': 'OTHERLOCK', 'resources': resource.slug})
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 1
|
|
|
|
# ... unless it's MYLOCK
|
|
resp = app.get(agenda1_datetimes_url, params={'lock_code': 'MYLOCK', 'resources': resource.slug})
|
|
assert free_slots == len(resp.json['data'])
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 0
|
|
|
|
# check slot is also disabled on another agenda with same resource
|
|
resp = app.get(agenda2_datetimes_url)
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 0
|
|
resp = app.get(agenda2_datetimes_url, params={'resources': resource.slug})
|
|
assert len([x for x in resp.json['data'] if x.get('disabled')]) == 1
|
|
|
|
# can't lock the same timeslot ...
|
|
resp_booking = app.post_json(fillslot_url + '?resources=re1', params={'lock_code': 'OTHERLOCK'})
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
# ... unless with MYLOCK (aka "relock")
|
|
resp_booking = app.post_json(fillslot_url + '?resources=re1', params={'lock_code': 'MYLOCK'})
|
|
assert resp_booking.json['err'] == 0
|
|
assert Booking.objects.count() == 1
|
|
assert Lease.objects.get().lock_code == 'MYLOCK'
|
|
|
|
# can't book the slot ...
|
|
resp_booking = app.post_json(fillslot_url + '?resources=re1')
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
resp_booking = app.post_json(fillslot_url + '?resources=re1', params={'confirm_after_lock': True})
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
resp_booking = app.post_json(
|
|
fillslot_url + '?resources=re1', params={'lock_code': 'OTHERLOCK', 'confirm_after_lock': True}
|
|
)
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['reason'] == 'no more desk available'
|
|
|
|
# unless with MYLOCK (aka "confirm")
|
|
resp_booking = app.post_json(
|
|
fillslot_url + '?resources=re1', params={'lock_code': 'MYLOCK', 'confirm_after_lock': True}
|
|
)
|
|
assert resp_booking.json['err'] == 0
|
|
assert Booking.objects.count() == 1
|
|
assert Lease.objects.count() == 0
|
|
|
|
|
|
def test_virtual_agenda_with_external_user_id_exclusion(app, user):
|
|
'''Test lock_code use when excluding an external_user_id'''
|
|
agenda = build_virtual_agenda(
|
|
meeting_types=(30,),
|
|
agendas={
|
|
'Agenda 1': {
|
|
'desks': {
|
|
'desk': 'monday-friday 08:00-12:00 14:00-17:00',
|
|
},
|
|
},
|
|
'Agenda 2': {
|
|
'desks': {
|
|
'desk': 'monday-friday 09:00-12:00',
|
|
},
|
|
},
|
|
'Agenda 3': {
|
|
'desks': {
|
|
'desk': 'monday-friday 15:00-17:00',
|
|
},
|
|
},
|
|
},
|
|
)
|
|
|
|
datetimes_url = agenda._mt_30.api_datetimes_url
|
|
|
|
resp = app.get(datetimes_url)
|
|
slots = resp.json['data']
|
|
# get first slot between 11 and 11:30
|
|
slot = [slot for slot in slots if ' 11:00:00' in slot['datetime']][0]
|
|
fillslot_url = slot['api']['fillslot_url']
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post_json(fillslot_url, params={'lock_code': 'MYLOCK', 'user_external_id': 'abcd'})
|
|
assert resp.json['err'] == 0
|
|
|
|
# check the lease was created
|
|
assert Booking.objects.filter(user_external_id='abcd', lease__lock_code='MYLOCK').count() == 1
|
|
|
|
# check 11:00 slot is still available
|
|
resp = app.get(datetimes_url)
|
|
slots = resp.json['data']
|
|
assert any(
|
|
s['datetime'] == slot['datetime'] for s in slots if not s['disabled']
|
|
), f"slot {slot['datetime']} should be available"
|
|
|
|
# check 11:00 slot is unavailable when tested with user_external_id
|
|
resp = app.get(datetimes_url, params={'user_external_id': 'abcd'})
|
|
slots = resp.json['data']
|
|
assert not any(
|
|
s['datetime'] == slot['datetime'] for s in slots if not s['disabled']
|
|
), f"slot {slot['datetime']} should not be available"
|
|
|
|
# check 11:00 slot is available if tested with user_external_id *AND* lock_code
|
|
resp = app.get(datetimes_url, params={'lock_code': 'MYLOCK', 'user_external_id': 'abcd'})
|
|
slots = resp.json['data']
|
|
assert any(
|
|
s['datetime'] == slot['datetime'] for s in slots if not s['disabled']
|
|
), f"slot {slot['datetime']} should be available"
|
|
|
|
|
|
def test_events_agenda_no_waiting_list(app, user, freezer):
|
|
agenda = build_event_agenda(
|
|
events={
|
|
'Event 1': {
|
|
'start_datetime': now() + datetime.timedelta(days=1),
|
|
'places': 1,
|
|
}
|
|
}
|
|
)
|
|
|
|
# list events
|
|
resp = app.get(agenda.api_datetimes_url)
|
|
slot = resp.json['data'][0]
|
|
assert slot['places']['available'] == 1
|
|
assert slot['places']['full'] is False
|
|
|
|
# book first one
|
|
fillslot_url = slot['api']['fillslot_url']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post_json(fillslot_url, params={'lock_code': 'MYLOCK'})
|
|
assert resp.json['err'] == 0
|
|
|
|
# list events without lock code
|
|
resp = app.get(agenda.api_datetimes_url)
|
|
slot = resp.json['data'][0]
|
|
assert slot['places']['available'] == 0
|
|
assert slot['places']['full'] is True
|
|
|
|
# list events with lock code
|
|
resp = app.get(agenda.api_datetimes_url, params={'lock_code': 'MYLOCK', 'hide_disabled': 'true'})
|
|
slot = resp.json['data'][0]
|
|
assert slot['places']['available'] == 1
|
|
assert slot['places']['full'] is False
|
|
|
|
# re-book first one without lock code
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post_json(fillslot_url)
|
|
assert resp.json['err'] == 1
|
|
|
|
# rebook first one with lock code
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post_json(fillslot_url, params={'lock_code': 'MYLOCK'})
|
|
assert resp.json['err'] == 0
|
|
|
|
# confirm booking
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post_json(fillslot_url, params={'lock_code': 'MYLOCK', 'confirm_after_lock': True})
|
|
assert resp.json['err'] == 0
|
|
|
|
# list events without lock code, after 30 minutes slot is still booked
|
|
freezer.move_to(datetime.timedelta(minutes=30))
|
|
resp = app.get(agenda.api_datetimes_url)
|
|
slot = resp.json['data'][0]
|
|
assert slot['places']['available'] == 0
|
|
assert slot['places']['full'] is True
|
|
|
|
|
|
def test_events_agenda_no_waiting_list_expiration(app, user, freezer):
|
|
agenda = build_event_agenda(
|
|
events={
|
|
'Event 1': {
|
|
'start_datetime': now() + datetime.timedelta(days=1),
|
|
'places': 1,
|
|
}
|
|
}
|
|
)
|
|
|
|
# list events
|
|
resp = app.get(agenda.api_datetimes_url)
|
|
slot = resp.json['data'][0]
|
|
|
|
# book first one
|
|
fillslot_url = slot['api']['fillslot_url']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post_json(fillslot_url, params={'lock_code': 'MYLOCK'})
|
|
assert resp.json['err'] == 0
|
|
|
|
# list events without lock code
|
|
resp = app.get(agenda.api_datetimes_url)
|
|
slot = resp.json['data'][0]
|
|
assert slot['places']['available'] == 0
|
|
assert slot['places']['full'] is True
|
|
|
|
# list events without lock code, after 30 minutes slot is available
|
|
freezer.move_to(datetime.timedelta(minutes=30))
|
|
resp = app.get(agenda.api_datetimes_url)
|
|
slot = resp.json['data'][0]
|
|
assert slot['places']['available'] == 1
|
|
assert slot['places']['full'] is False
|