2804 lines
120 KiB
Python
2804 lines
120 KiB
Python
import datetime
|
|
import urllib.parse as urlparse
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.db import connection
|
|
from django.test.utils import CaptureQueriesContext
|
|
from django.utils.timezone import localtime, now
|
|
|
|
from chrono.agendas.models import (
|
|
Agenda,
|
|
Booking,
|
|
BookingColor,
|
|
Desk,
|
|
Event,
|
|
MeetingType,
|
|
Resource,
|
|
TimePeriod,
|
|
VirtualMember,
|
|
)
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_booking_api(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
|
|
|
# unauthenticated
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.id), status=401)
|
|
|
|
for agenda_key in (agenda.slug, agenda.id): # acces datetimes via agenda slug or id (legacy)
|
|
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda_key)
|
|
event_fillslot_url = [x for x in resp_datetimes.json['data'] if x['id'] == event.slug][0]['api'][
|
|
'fillslot_url'
|
|
]
|
|
assert urlparse.urlparse(event_fillslot_url).path == '/api/agenda/%s/fillslot/%s/' % (
|
|
agenda.slug,
|
|
event.slug,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.id))
|
|
Booking.objects.get(id=resp.json['booking_id'])
|
|
assert resp.json['datetime'] == localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
|
assert 'booking_url' in resp.json['api']
|
|
assert 'accept_url' in resp.json['api']
|
|
assert 'suspend_url' in resp.json['api']
|
|
assert 'cancel_url' in resp.json['api']
|
|
assert 'ics_url' in resp.json['api']
|
|
assert 'anonymize_url' in resp.json['api']
|
|
assert urlparse.urlparse(resp.json['api']['booking_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['accept_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['suspend_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['cancel_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['ics_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['anonymize_url']).netloc
|
|
assert Booking.objects.count() == 1
|
|
|
|
# access by slug
|
|
event.slug = 'bar'
|
|
event.save()
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.slug))
|
|
assert Booking.objects.count() == 2
|
|
assert Booking.objects.filter(event__agenda=agenda).count() == 2
|
|
|
|
# test with additional data
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
|
params={
|
|
'label': 'foo',
|
|
'user_name': 'bar',
|
|
'backoffice_url': 'http://example.net/',
|
|
'cancel_callback_url': 'http://example.net/jump/trigger/',
|
|
'user_email': 'bar@bar.com',
|
|
'user_phone_number': '+33123456789',
|
|
'form_url': 'http://example.net/',
|
|
},
|
|
)
|
|
booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
assert booking.label == 'foo'
|
|
assert booking.user_first_name == ''
|
|
assert booking.user_last_name == 'bar'
|
|
assert booking.backoffice_url == 'http://example.net/'
|
|
assert booking.cancel_callback_url == 'http://example.net/jump/trigger/'
|
|
assert booking.user_email == 'bar@bar.com'
|
|
assert booking.user_phone_number == '+33123456789'
|
|
assert booking.form_url == 'http://example.net/'
|
|
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
|
params={
|
|
'label': 'foo',
|
|
'user_first_name': 'foo',
|
|
'user_last_name': 'bar',
|
|
'backoffice_url': 'http://example.net/',
|
|
'cancel_callback_url': 'http://example.net/jump/trigger/',
|
|
'user_email': 'bar@bar.com',
|
|
'user_phone_number': '+33123456789',
|
|
'form_url': 'http://example.net/',
|
|
},
|
|
)
|
|
booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
assert booking.label == 'foo'
|
|
assert booking.user_first_name == 'foo'
|
|
assert booking.user_last_name == 'bar'
|
|
assert booking.backoffice_url == 'http://example.net/'
|
|
assert booking.cancel_callback_url == 'http://example.net/jump/trigger/'
|
|
assert booking.user_email == 'bar@bar.com'
|
|
assert booking.user_phone_number == '+33123456789'
|
|
assert booking.form_url == 'http://example.net/'
|
|
|
|
# blank data are OK
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
|
params={'label': '', 'user_first_name': '', 'user_last_name': '', 'backoffice_url': ''},
|
|
)
|
|
assert Booking.objects.get(id=resp.json['booking_id']).label == ''
|
|
assert Booking.objects.get(id=resp.json['booking_id']).user_first_name == ''
|
|
assert Booking.objects.get(id=resp.json['booking_id']).user_last_name == ''
|
|
assert Booking.objects.get(id=resp.json['booking_id']).backoffice_url == ''
|
|
|
|
# extra data stored in extra_data field
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
|
params={'label': 'l', 'user_last_name': 'u', 'backoffice_url': '', 'foo': 'bar'},
|
|
)
|
|
assert Booking.objects.get(id=resp.json['booking_id']).label == 'l'
|
|
assert Booking.objects.get(id=resp.json['booking_id']).user_last_name == 'u'
|
|
assert Booking.objects.get(id=resp.json['booking_id']).backoffice_url == ''
|
|
assert Booking.objects.get(id=resp.json['booking_id']).extra_data == {'foo': 'bar'}
|
|
|
|
# anonymize
|
|
booking_id = resp.json['booking_id']
|
|
resp = app.post(resp.json['api']['anonymize_url'])
|
|
assert Booking.objects.get(id=booking_id).anonymization_datetime is not None
|
|
|
|
# test invalid data are refused
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
|
params={'user_last_name': {'foo': 'bar'}},
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid payload' # legacy
|
|
assert resp.json['err_class'] == 'invalid payload'
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
assert len(resp.json['errors']) == 1
|
|
assert 'user_last_name' in resp.json['errors']
|
|
|
|
# test parameters wrongly passed in query are refused
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/?backoffice_url=https://example.com&label=test' % (agenda.id, event.id),
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert (
|
|
resp.json['err_class']
|
|
== 'parameters "backoffice_url, label" must be included in request body, not query'
|
|
)
|
|
assert (
|
|
resp.json['err_desc']
|
|
== 'parameters "backoffice_url, label" must be included in request body, not query'
|
|
)
|
|
|
|
resp = app.post('/api/agenda/foobar/fillslot/%s/' % event.id, status=404)
|
|
|
|
resp = app.post('/api/agenda/0/fillslot/%s/' % event.id, status=404)
|
|
|
|
|
|
def test_booking_api_check_delays(app, user):
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
|
|
)
|
|
event = Event.objects.create(
|
|
slug='event-slug',
|
|
start_datetime=localtime() + datetime.timedelta(days=5),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
|
|
assert resp.json['err'] == 0
|
|
|
|
# test minimal_booking_delay
|
|
agenda.minimal_booking_delay = 6
|
|
agenda.save()
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'bypass_delays': True})
|
|
assert resp.json['err'] == 0
|
|
|
|
# test maximal_booking_delay
|
|
agenda.minimal_booking_delay = 0
|
|
agenda.maximal_booking_delay = 3
|
|
agenda.save()
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'bypass_delays': True})
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-02-23')
|
|
def test_booking_api_exclude_slots(app, user):
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
|
|
)
|
|
event = Event.objects.create(
|
|
slug='event-slug',
|
|
start_datetime=localtime().replace(hour=10, minute=0),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
Booking.objects.create(event=event, user_external_id='42')
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'user_external_id': '42'}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug),
|
|
params={'user_external_id': '42', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event is already booked by user'
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'exclude_user': True})
|
|
assert resp.json['err'] == 0
|
|
|
|
event.delete()
|
|
|
|
# recurrent event
|
|
start_datetime = localtime().replace(hour=12, minute=0)
|
|
event = Event.objects.create(
|
|
slug='recurrent',
|
|
start_datetime=start_datetime,
|
|
recurrence_days=[start_datetime.weekday()],
|
|
places=3,
|
|
agenda=agenda,
|
|
)
|
|
first_recurrence = event.get_or_create_event_recurrence(event.start_datetime)
|
|
Booking.objects.create(event=first_recurrence, user_external_id='42')
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, first_recurrence.slug),
|
|
params={'user_external_id': '42'},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, first_recurrence.slug),
|
|
params={'user_external_id': '42', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event is already booked by user'
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, first_recurrence.slug), params={'exclude_user': True}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-02-25')
|
|
def test_booking_api_meetings_agenda_exclude_slots(app, user):
|
|
tomorrow = now() + datetime.timedelta(days=1)
|
|
agenda = Agenda.objects.create(
|
|
label='Agenda', kind='meetings', minimal_booking_delay=0, maximal_booking_delay=10
|
|
)
|
|
desk = Desk.objects.create(agenda=agenda, slug='desk')
|
|
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo-bar')
|
|
TimePeriod.objects.create(
|
|
weekday=tomorrow.date().weekday(),
|
|
start_time=datetime.time(9, 0),
|
|
end_time=datetime.time(17, 00),
|
|
desk=desk,
|
|
)
|
|
desk.duplicate()
|
|
desk.duplicate()
|
|
event = Event.objects.create(
|
|
agenda=agenda,
|
|
meeting_type=meeting_type,
|
|
places=1,
|
|
start_datetime=localtime(tomorrow).replace(hour=9, minute=0),
|
|
desk=desk,
|
|
)
|
|
Booking.objects.create(event=event, user_external_id='42')
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (agenda.slug, meeting_type.slug),
|
|
params={'user_external_id': '42'},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (agenda.slug, meeting_type.slug),
|
|
params={'user_external_id': '42', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (agenda.slug, meeting_type.slug),
|
|
params={'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
def test_booking_api_fillslots(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
events_ids = [x.id for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]
|
|
events_slugs = [x.slug for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]
|
|
assert len(events_ids) == 3
|
|
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0] # first event
|
|
|
|
# unauthenticated
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, status=401)
|
|
|
|
for agenda_key in (agenda.slug, agenda.id): # acces datetimes via agenda slug or id (legacy)
|
|
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda_key)
|
|
api_event_slugs = [x['id'] for x in resp_datetimes.json['data']]
|
|
assert api_event_slugs == events_slugs
|
|
|
|
assert Booking.objects.count() == 0
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': events_ids})
|
|
primary_booking_id = resp.json['booking_id']
|
|
Booking.objects.get(id=primary_booking_id)
|
|
assert resp.json['datetime'] == localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
|
assert 'booking_url' in resp.json['api']
|
|
assert 'accept_url' in resp.json['api']
|
|
assert 'suspend_url' in resp.json['api']
|
|
assert 'cancel_url' in resp.json['api']
|
|
assert urlparse.urlparse(resp.json['api']['booking_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['accept_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['suspend_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['cancel_url']).netloc
|
|
assert Booking.objects.count() == 3
|
|
# these 3 bookings are related, the first is the primary one
|
|
bookings = Booking.objects.all().order_by('pk')
|
|
assert bookings[0].primary_booking is None
|
|
assert bookings[1].primary_booking.id == bookings[0].id == primary_booking_id
|
|
assert bookings[2].primary_booking.id == bookings[0].id == primary_booking_id
|
|
|
|
# access by slug
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': events_slugs})
|
|
primary_booking_id_2 = resp.json['booking_id']
|
|
assert Booking.objects.count() == 6
|
|
assert Booking.objects.filter(event__agenda=agenda).count() == 6
|
|
# 6 = 2 primary + 2*2 secondary
|
|
assert Booking.objects.filter(event__agenda=agenda, primary_booking__isnull=True).count() == 2
|
|
assert Booking.objects.filter(event__agenda=agenda, primary_booking=primary_booking_id).count() == 2
|
|
assert Booking.objects.filter(event__agenda=agenda, primary_booking=primary_booking_id_2).count() == 2
|
|
|
|
# test with additional data
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslots/' % agenda.id,
|
|
params={
|
|
'slots': events_ids,
|
|
'label': 'foo',
|
|
'user_external_id': 'some_external_id',
|
|
'user_last_name': 'bar',
|
|
'user_display_label': 'foo',
|
|
'backoffice_url': 'http://example.net/',
|
|
},
|
|
)
|
|
booking_id = resp.json['booking_id']
|
|
booking = Booking.objects.get(pk=booking_id)
|
|
assert booking.label == 'foo'
|
|
assert booking.user_external_id == 'some_external_id'
|
|
assert booking.user_last_name == 'bar'
|
|
assert booking.user_display_label == 'foo'
|
|
assert booking.backoffice_url == 'http://example.net/'
|
|
assert Booking.objects.filter(primary_booking=booking_id, label='foo').count() == 2
|
|
# cancel
|
|
cancel_url = resp.json['api']['cancel_url']
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 0
|
|
assert Booking.objects.get(id=booking_id).cancellation_datetime is None
|
|
resp_cancel = app.post(cancel_url)
|
|
assert resp_cancel.json['err'] == 0
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 3
|
|
assert Booking.objects.get(id=booking_id).cancellation_datetime is not None
|
|
|
|
# extra data stored in extra_data field
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslots/' % agenda.id,
|
|
params={'slots': events_ids, 'label': 'l', 'user_last_name': 'u', 'backoffice_url': '', 'foo': 'bar'},
|
|
)
|
|
assert Booking.objects.get(id=resp.json['booking_id']).label == 'l'
|
|
assert Booking.objects.get(id=resp.json['booking_id']).user_last_name == 'u'
|
|
assert Booking.objects.get(id=resp.json['booking_id']).backoffice_url == ''
|
|
assert Booking.objects.get(id=resp.json['booking_id']).extra_data == {'foo': 'bar'}
|
|
for booking in Booking.objects.filter(primary_booking=resp.json['booking_id']):
|
|
assert booking.extra_data == {'foo': 'bar'}
|
|
|
|
# test invalid data are refused
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslots/' % agenda.id,
|
|
params={'slots': events_ids, 'user_last_name': {'foo': 'bar'}},
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid payload' # legacy
|
|
assert resp.json['err_class'] == 'invalid payload'
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
assert len(resp.json['errors']) == 1
|
|
assert 'user_last_name' in resp.json['errors']
|
|
|
|
# empty or missing slots
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.id, params={'slots': []}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid payload' # legacy
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
assert resp.json['errors']['slots'] == ['This field is required.']
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.id, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid payload' # legacy
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
assert resp.json['errors']['slots'] == ['This field is required.']
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.id, params={'slots': ''}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
assert resp.json['errors']['slots'] == ['This field is required.']
|
|
# invalid slots format
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.id, params={'slots': 'foobar'}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid slugs: foobar' # legacy
|
|
assert resp.json['err_class'] == 'invalid slugs: foobar'
|
|
assert resp.json['err_desc'] == 'invalid slugs: foobar'
|
|
|
|
# unknown agendas
|
|
resp = app.post('/api/agenda/foobar/fillslots/', status=404)
|
|
resp = app.post('/api/agenda/0/fillslots/', status=404)
|
|
|
|
# check bookable period
|
|
with mock.patch('chrono.agendas.models.Event.in_bookable_period') as in_bookable_period:
|
|
in_bookable_period.return_value = True
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslots/' % agenda.id,
|
|
params={'slots': events_ids},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
in_bookable_period.return_value = False
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslots/' % agenda.id,
|
|
params={'slots': events_ids},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'event not bookable' # legacy
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
assert resp.json['err_desc'] == 'event foo-bar-event is not bookable'
|
|
|
|
|
|
def test_booking_api_fillslots_slots_string_param(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
events_ids = [x.id for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()]
|
|
assert len(events_ids) == 3
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
# empty string
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': ''}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'invalid payload'
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
|
|
slots_string_param = ','.join([str(e) for e in events_ids])
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots_string_param})
|
|
primary_booking_id = resp.json['booking_id']
|
|
Booking.objects.get(id=primary_booking_id)
|
|
assert Booking.objects.count() == 3
|
|
|
|
start = now() + datetime.timedelta(days=2)
|
|
Event.objects.create(label='Long Slug', slug='a' * 100, start_datetime=start, places=2, agenda=agenda)
|
|
Event.objects.create(label='Long Slug', slug='b' * 100, start_datetime=start, places=2, agenda=agenda)
|
|
events_ids = [x.id for x in Event.objects.filter(label='Long Slug')]
|
|
slots_string_param = ','.join([str(e) for e in events_ids])
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots_string_param})
|
|
assert Booking.objects.count() == 5
|
|
|
|
|
|
def test_booking_api_meeting(app, meetings_agenda, user):
|
|
agenda_id = meetings_agenda.slug
|
|
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
event_id = resp.json['data'][2]['id']
|
|
assert urlparse.urlparse(
|
|
resp.json['data'][2]['api']['fillslot_url']
|
|
).path == '/api/agenda/%s/fillslot/%s/' % (meetings_agenda.slug, event_id)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
# verify malformed event_pk returns a 400
|
|
resp_booking = app.post('/api/agenda/%s/fillslot/None/' % agenda_id, status=400)
|
|
assert resp_booking.json['err'] == 1
|
|
|
|
# make a booking
|
|
resp_booking = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert Booking.objects.count() == 1
|
|
assert resp_booking.json['datetime'] == localtime(Booking.objects.all()[0].event.start_datetime).strftime(
|
|
'%Y-%m-%d %H:%M:%S'
|
|
)
|
|
assert resp_booking.json['end_datetime'] == localtime(
|
|
Booking.objects.all()[0].event.end_datetime
|
|
).strftime('%Y-%m-%d %H:%M:%S')
|
|
assert resp_booking.json['duration'] == 30
|
|
|
|
resp2 = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
assert len(resp.json['data']) == len([x for x in resp2.json['data'] if not x.get('disabled')]) + 1
|
|
|
|
# try booking the same timeslot
|
|
resp2 = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert resp2.json['err'] == 1
|
|
assert resp2.json['reason'] == 'no more desk available' # legacy
|
|
assert resp2.json['err_class'] == 'no more desk available'
|
|
assert resp2.json['err_desc'] == 'no more desk available'
|
|
|
|
# try booking another timeslot
|
|
event_id = resp.json['data'][3]['id']
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert resp.json['err'] == 0
|
|
assert Booking.objects.count() == 2
|
|
|
|
|
|
def test_booking_api_meeting_colors(app, user):
|
|
agenda = Agenda.objects.create(
|
|
label='foo', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=56
|
|
)
|
|
meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah', duration=30)
|
|
default_desk = Desk.objects.create(agenda=agenda, label='Desk 1')
|
|
TimePeriod.objects.create(
|
|
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=default_desk
|
|
)
|
|
datetimes_resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
event_id = datetimes_resp.json['data'][2]['id']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
|
params={
|
|
'use_color_for': 'Cooking',
|
|
},
|
|
)
|
|
booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
assert booking.color.label == 'Cooking'
|
|
assert booking.color.index == 0
|
|
|
|
event_id = datetimes_resp.json['data'][3]['id']
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
|
params={
|
|
'use_color_for': 'Cooking',
|
|
},
|
|
)
|
|
new_booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
assert new_booking.color.index == 0
|
|
|
|
event_id = datetimes_resp.json['data'][4]['id']
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
|
params={
|
|
'use_color_for': 'Swimming',
|
|
},
|
|
)
|
|
new_booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
assert new_booking.color.label == 'Swimming'
|
|
assert new_booking.color.index == 1
|
|
|
|
for i in range((BookingColor.COLOR_COUNT * 2) - 2):
|
|
event_id = datetimes_resp.json['data'][i]['id']
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
|
params={
|
|
'use_color_for': str(i),
|
|
},
|
|
)
|
|
assert BookingColor.objects.count() == BookingColor.COLOR_COUNT * 2
|
|
assert BookingColor.objects.distinct('index').order_by().count() == BookingColor.COLOR_COUNT
|
|
|
|
|
|
def test_booking_api_meeting_with_resources(app, user):
|
|
tomorrow = datetime.date.today() + datetime.timedelta(days=1)
|
|
tomorrow_str = tomorrow.isoformat()
|
|
agenda = Agenda.objects.create(
|
|
label='Agenda', kind='meetings', minimal_booking_delay=0, maximal_booking_delay=10
|
|
)
|
|
resource1 = Resource.objects.create(label='Resource 1')
|
|
resource2 = Resource.objects.create(label='Resource 2')
|
|
resource3 = Resource.objects.create(label='Resource 3')
|
|
agenda.resources.add(resource1, resource2, resource3)
|
|
desk = Desk.objects.create(agenda=agenda, slug='desk')
|
|
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo-bar')
|
|
TimePeriod.objects.create(
|
|
weekday=tomorrow.weekday(),
|
|
start_time=datetime.time(9, 0),
|
|
end_time=datetime.time(17, 00),
|
|
desk=desk,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
# make a booking without resource
|
|
resp = app.post('/api/agenda/%s/fillslot/%s:%s-1000/' % (agenda.pk, meeting_type.pk, tomorrow_str))
|
|
assert resp.json['datetime'] == '%s 10:00:00' % tomorrow_str
|
|
assert resp.json['end_datetime'] == '%s 10:30:00' % tomorrow_str
|
|
assert resp.json['duration'] == 30
|
|
assert resp.json['resources'] == []
|
|
booking = Booking.objects.latest('pk')
|
|
assert list(booking.event.resources.all()) == []
|
|
|
|
# now try to book also a resource - slot not free
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-1000/?resources=%s'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str, resource1.slug)
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'no more desk available' # legacy
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
assert resp.json['err_desc'] == 'no more desk available'
|
|
|
|
# slot is free
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-0900/?resources=%s'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str, resource1.slug)
|
|
)
|
|
assert resp.json['datetime'] == '%s 09:00:00' % tomorrow_str
|
|
assert resp.json['end_datetime'] == '%s 09:30:00' % tomorrow_str
|
|
assert resp.json['duration'] == 30
|
|
assert resp.json['resources'] == [resource1.slug]
|
|
booking = Booking.objects.latest('pk')
|
|
assert list(booking.event.resources.all()) == [resource1]
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-0930/?resources=%s,%s'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str, resource1.slug, resource2.slug)
|
|
)
|
|
assert resp.json['datetime'] == '%s 09:30:00' % tomorrow_str
|
|
assert resp.json['end_datetime'] == '%s 10:00:00' % tomorrow_str
|
|
assert resp.json['duration'] == 30
|
|
assert resp.json['resources'] == [resource1.slug, resource2.slug]
|
|
booking = Booking.objects.latest('pk')
|
|
assert list(booking.event.resources.all()) == [resource1, resource2]
|
|
|
|
# resource is unknown or not valid for this agenda
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-0900/?resources=foobarblah'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str),
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid slugs: foobarblah' # legacy
|
|
assert resp.json['err_class'] == 'invalid slugs: foobarblah'
|
|
assert resp.json['err_desc'] == 'invalid slugs: foobarblah'
|
|
agenda.resources.remove(resource3)
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-0900/?resources=%s'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str, resource3.slug),
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid slugs: resource-3' # legacy
|
|
assert resp.json['err_class'] == 'invalid slugs: resource-3'
|
|
assert resp.json['err_desc'] == 'invalid slugs: resource-3'
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-0900/?resources=%s,foobarblah'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str, resource3.slug),
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid slugs: foobarblah, resource-3' # legacy
|
|
assert resp.json['err_class'] == 'invalid slugs: foobarblah, resource-3'
|
|
assert resp.json['err_desc'] == 'invalid slugs: foobarblah, resource-3'
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-0900/?resources=%s,foobarblah'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str, resource1.slug),
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid slugs: foobarblah' # legacy
|
|
assert resp.json['err_class'] == 'invalid slugs: foobarblah'
|
|
assert resp.json['err_desc'] == 'invalid slugs: foobarblah'
|
|
|
|
# booking is canceled: slot is free
|
|
booking.cancel()
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:%s-0930/?resources=%s,%s'
|
|
% (agenda.pk, meeting_type.pk, tomorrow_str, resource1.slug, resource2.slug)
|
|
)
|
|
assert resp.json['datetime'] == '%s 09:30:00' % tomorrow_str
|
|
assert resp.json['end_datetime'] == '%s 10:00:00' % tomorrow_str
|
|
assert resp.json['duration'] == 30
|
|
assert resp.json['resources'] == [resource1.slug, resource2.slug]
|
|
booking = Booking.objects.latest('pk')
|
|
assert list(booking.event.resources.all()) == [resource1, resource2]
|
|
|
|
|
|
def test_booking_api_meeting_fillslots(app, meetings_agenda, user):
|
|
agenda_id = meetings_agenda.slug
|
|
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
slots = [resp.json['data'][0]['id'], resp.json['data'][1]['id']]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp_booking = app.post('/api/agenda/%s/fillslots/' % agenda_id, params={'slots': slots})
|
|
assert Booking.objects.count() == 2
|
|
primary_booking = Booking.objects.filter(primary_booking__isnull=True).first()
|
|
secondary_booking = Booking.objects.filter(primary_booking=primary_booking.id).first()
|
|
assert resp_booking.json['datetime'] == localtime(primary_booking.event.start_datetime).strftime(
|
|
'%Y-%m-%d %H:%M:%S'
|
|
)
|
|
assert resp_booking.json['end_datetime'] == localtime(secondary_booking.event.end_datetime).strftime(
|
|
'%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
resp2 = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
assert len(resp.json['data']) == len([x for x in resp2.json['data'] if not x.get('disabled')]) + 2
|
|
|
|
# try booking the same timeslots
|
|
resp2 = app.post('/api/agenda/%s/fillslots/' % agenda_id, params={'slots': slots})
|
|
assert resp2.json['err'] == 1
|
|
assert resp2.json['reason'] == 'no more desk available' # legacy
|
|
assert resp2.json['err_class'] == 'no more desk available'
|
|
assert resp2.json['err_desc'] == 'no more desk available'
|
|
|
|
# try booking partially free timeslots (one free, one busy)
|
|
nonfree_slots = [resp.json['data'][0]['id'], resp.json['data'][2]['id']]
|
|
resp2 = app.post('/api/agenda/%s/fillslots/' % agenda_id, params={'slots': nonfree_slots})
|
|
assert resp2.json['err'] == 1
|
|
assert resp2.json['reason'] == 'no more desk available' # legacy
|
|
assert resp2.json['err_class'] == 'no more desk available'
|
|
assert resp2.json['err_desc'] == 'no more desk available'
|
|
|
|
# booking other free timeslots
|
|
free_slots = [resp.json['data'][3]['id'], resp.json['data'][2]['id']]
|
|
resp2 = app.post('/api/agenda/%s/fillslots/' % agenda_id, params={'slots': free_slots})
|
|
assert resp2.json['err'] == 0
|
|
cancel_url = resp2.json['api']['cancel_url']
|
|
assert Booking.objects.count() == 4
|
|
# 4 = 2 primary + 2 secondary
|
|
assert Booking.objects.filter(primary_booking__isnull=True).count() == 2
|
|
assert Booking.objects.filter(primary_booking__isnull=False).count() == 2
|
|
# cancel
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 0
|
|
resp_cancel = app.post(cancel_url)
|
|
assert resp_cancel.json['err'] == 0
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 2
|
|
|
|
|
|
def test_booking_api_meeting_fillslots_wrong_slot(app, user):
|
|
agenda = Agenda.objects.create(label='Foo', kind='meetings')
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
impossible_slots = ['1:2017-05-22-1130', '2:2017-05-22-1100']
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': impossible_slots}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'all slots must have the same meeting type id (1)' # legacy
|
|
assert resp.json['err_class'] == 'all slots must have the same meeting type id (1)'
|
|
assert resp.json['err_desc'] == 'all slots must have the same meeting type id (1)'
|
|
|
|
unknown_slots = ['0:2017-05-22-1130']
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': unknown_slots}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid meeting type id: 0' # legacy
|
|
assert resp.json['err_class'] == 'invalid meeting type id: 0'
|
|
assert resp.json['err_desc'] == 'invalid meeting type id: 0'
|
|
unknown_slots = ['foobar:2017-05-22-1130']
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': unknown_slots}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'invalid meeting type id: foobar' # legacy
|
|
assert resp.json['err_class'] == 'invalid meeting type id: foobar'
|
|
assert resp.json['err_desc'] == 'invalid meeting type id: foobar'
|
|
|
|
badformat_slots = ['foo:2020-10-28-14h00']
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': badformat_slots}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'bad datetime format: 2020-10-28-14h00' # legacy
|
|
assert resp.json['err_class'] == 'bad datetime format: 2020-10-28-14h00'
|
|
assert resp.json['err_desc'] == 'bad datetime format: 2020-10-28-14h00'
|
|
|
|
|
|
def test_booking_api_meeting_across_daylight_saving_time(app, meetings_agenda, user):
|
|
meetings_agenda.maximal_booking_delay = 365
|
|
meetings_agenda.save()
|
|
|
|
agenda_id = meetings_agenda.slug
|
|
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
event_index = 26 * 18
|
|
event_id = resp.json['data'][event_index]['id']
|
|
assert event_id[-4:] == resp.json['data'][2 * 18]['id'][-4:]
|
|
assert urlparse.urlparse(
|
|
resp.json['data'][event_index]['api']['fillslot_url']
|
|
).path == '/api/agenda/%s/fillslot/%s/' % (meetings_agenda.slug, event_id)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp_booking = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert Booking.objects.count() == 1
|
|
assert resp_booking.json['datetime'] == localtime(Booking.objects.all()[0].event.start_datetime).strftime(
|
|
'%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
assert resp.json['data'][event_index]['disabled']
|
|
|
|
|
|
def test_booking_api_with_data(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id), params={'hello': 'world'})
|
|
assert Booking.objects.count() == 1
|
|
assert Booking.objects.all()[0].extra_data == {'hello': 'world'}
|
|
|
|
|
|
def test_booking_api_available(app, user):
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
|
|
for i in range(0, 10):
|
|
event = Event.objects.create(
|
|
slug='event-slug%i' % i,
|
|
start_datetime=(now() + datetime.timedelta(days=5)).replace(hour=10, minute=i),
|
|
places=20,
|
|
agenda=agenda,
|
|
)
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
|
assert len(ctx.captured_queries) == 4
|
|
assert resp.json['data'][-1]['places']['total'] == 20
|
|
assert resp.json['data'][-1]['places']['available'] == 20
|
|
assert resp.json['data'][-1]['places']['reserved'] == 0
|
|
assert resp.json['data'][-1]['places']['full'] is False
|
|
assert 'waiting_list_total' not in resp.json['data'][-1]['places']
|
|
|
|
resp = app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk))
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['places']['total'] == 20
|
|
assert resp.json['places']['available'] == 19
|
|
assert resp.json['places']['reserved'] == 1
|
|
assert resp.json['places']['full'] is False
|
|
assert 'waiting_list_total' not in resp.json['places']
|
|
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
|
assert resp.json['data'][-1]['places']['total'] == 20
|
|
assert resp.json['data'][-1]['places']['available'] == 19
|
|
assert resp.json['data'][-1]['places']['reserved'] == 1
|
|
assert resp.json['data'][-1]['places']['full'] is False
|
|
assert 'waiting_list_total' not in resp.json['data'][-1]['places']
|
|
|
|
Booking.objects.create(event=event, in_waiting_list=True)
|
|
event.waiting_list_places = 5
|
|
event.save()
|
|
|
|
resp = app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk))
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['places']['total'] == 20
|
|
assert resp.json['places']['available'] == 19
|
|
assert resp.json['places']['reserved'] == 1
|
|
assert resp.json['places']['full'] is False
|
|
assert resp.json['places']['waiting_list_total'] == 5
|
|
assert resp.json['places']['waiting_list_available'] == 3
|
|
assert resp.json['places']['waiting_list_reserved'] == 2
|
|
assert resp.json['places']['waiting_list_activated'] is True
|
|
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
|
assert resp.json['data'][-1]['places']['total'] == 20
|
|
assert resp.json['data'][-1]['places']['available'] == 19
|
|
assert resp.json['data'][-1]['places']['reserved'] == 1
|
|
assert resp.json['data'][-1]['places']['full'] is False
|
|
assert resp.json['data'][-1]['places']['waiting_list_total'] == 5
|
|
assert resp.json['data'][-1]['places']['waiting_list_available'] == 3
|
|
assert resp.json['data'][-1]['places']['waiting_list_reserved'] == 2
|
|
assert resp.json['data'][-1]['places']['waiting_list_activated'] is True
|
|
|
|
# not for mettings agenda
|
|
meetings_agenda = Agenda.objects.create(label='meetings', kind='meetings')
|
|
desk = Desk.objects.create(label='foo', agenda=meetings_agenda)
|
|
meeting_type = MeetingType.objects.create(agenda=meetings_agenda, label='Blah', duration=30)
|
|
TimePeriod.objects.create(
|
|
weekday=3, start_time=datetime.time(11, 0), end_time=datetime.time(12, 30), desk=desk
|
|
)
|
|
resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (meetings_agenda.slug, meeting_type.slug))
|
|
assert 'places' not in resp.json['data'][0]
|
|
resp = app.post_json('/api/agenda/%s/fillslot/%s/' % (meetings_agenda.pk, resp.json['data'][0]['id']))
|
|
assert resp.json['err'] == 0
|
|
assert 'places' not in resp.json
|
|
|
|
# not for multiple booking
|
|
events = [
|
|
x for x in Event.objects.filter(agenda=agenda).order_by('start_datetime') if x.in_bookable_period()
|
|
][:2]
|
|
slots = [x.pk for x in events]
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': '3'})
|
|
assert resp.json['err'] == 0
|
|
assert 'places' not in resp.json
|
|
|
|
|
|
def test_booking_api_force_waiting_list(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
# no waiting list
|
|
assert event.waiting_list_places == 0
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk), params={'force_waiting_list': True}
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'no waiting list' # legacy
|
|
assert resp.json['err_class'] == 'no waiting list'
|
|
assert resp.json['err_desc'] == 'no waiting list'
|
|
|
|
event.waiting_list_places = 2
|
|
event.save()
|
|
# add a booking
|
|
resp = app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk))
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['places']['total'] == 20
|
|
assert resp.json['places']['available'] == 19
|
|
assert resp.json['places']['reserved'] == 1
|
|
assert resp.json['places']['full'] is False
|
|
assert resp.json['places']['waiting_list_total'] == 2
|
|
assert resp.json['places']['waiting_list_available'] == 2
|
|
assert resp.json['places']['waiting_list_reserved'] == 0
|
|
assert resp.json['places']['waiting_list_activated'] is False
|
|
|
|
# add another booking
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk), params={'force_waiting_list': False}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['places']['total'] == 20
|
|
assert resp.json['places']['available'] == 18
|
|
assert resp.json['places']['reserved'] == 2
|
|
assert resp.json['places']['full'] is False
|
|
assert resp.json['places']['waiting_list_total'] == 2
|
|
assert resp.json['places']['waiting_list_available'] == 2
|
|
assert resp.json['places']['waiting_list_reserved'] == 0
|
|
assert resp.json['places']['waiting_list_activated'] is False
|
|
|
|
# add a booking, but in waiting list
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk), params={'force_waiting_list': True}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['places']['total'] == 20
|
|
assert resp.json['places']['available'] == 18
|
|
assert resp.json['places']['reserved'] == 2
|
|
assert resp.json['places']['full'] is False
|
|
assert resp.json['places']['waiting_list_total'] == 2
|
|
assert resp.json['places']['waiting_list_available'] == 1
|
|
assert resp.json['places']['waiting_list_reserved'] == 1
|
|
assert resp.json['places']['waiting_list_activated'] is True
|
|
|
|
# add a booking => booked in waiting list
|
|
resp = app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk))
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['places']['total'] == 20
|
|
assert resp.json['places']['available'] == 18
|
|
assert resp.json['places']['reserved'] == 2
|
|
assert resp.json['places']['full'] is True
|
|
assert resp.json['places']['waiting_list_total'] == 2
|
|
assert resp.json['places']['waiting_list_available'] == 0
|
|
assert resp.json['places']['waiting_list_reserved'] == 2
|
|
assert resp.json['places']['waiting_list_activated'] is True
|
|
|
|
# waiting list is full
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event.pk), params={'force_waiting_list': True}
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
|
|
|
|
def test_booking_api_with_cancel_booking(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
event_0, event_1, event_2 = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][
|
|
0:4
|
|
]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.id, event_0.id))
|
|
assert Booking.objects.count() == 1
|
|
first_booking = Booking.objects.first()
|
|
|
|
# Book a new event and cancel previous booking
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event_1.id),
|
|
params={'cancel_booking_id': first_booking.pk},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['cancelled_booking_id'] == first_booking.pk
|
|
assert Booking.objects.count() == 2
|
|
first_booking = Booking.objects.get(pk=first_booking.pk)
|
|
assert first_booking.cancellation_datetime
|
|
|
|
# Cancelling an already cancelled booking returns an error
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event_1.id),
|
|
params={'cancel_booking_id': first_booking.pk},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'cancel booking: booking already cancelled' # legacy
|
|
assert resp.json['err_class'] == 'cancel booking: booking already cancelled'
|
|
assert resp.json['err_desc'] == 'cancel booking: booking already cancelled'
|
|
assert Booking.objects.count() == 2
|
|
|
|
# Cancelling a non existent booking returns an error
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event_1.id), params={'cancel_booking_id': '-1'}
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'cancel booking: booking does no exist' # legacy
|
|
assert resp.json['err_class'] == 'cancel booking: booking does no exist'
|
|
assert resp.json['err_desc'] == 'cancel booking: booking does no exist'
|
|
assert Booking.objects.count() == 2
|
|
|
|
# Cancelling booking with different count than new booking
|
|
resp = app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.id, event_2.id), params={'count': 2})
|
|
assert resp.json['err'] == 0
|
|
assert Booking.objects.count() == 4
|
|
booking_id = resp.json['booking_id']
|
|
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event_1.id),
|
|
params={'cancel_booking_id': booking_id, 'count': 1},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'cancel booking: count is different' # legacy
|
|
assert resp.json['err_class'] == 'cancel booking: count is different'
|
|
assert resp.json['err_desc'] == 'cancel booking: count is different'
|
|
assert Booking.objects.count() == 4
|
|
|
|
# cancel_booking_id must be an integer
|
|
app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event_0.id),
|
|
params={'cancel_booking_id': 'no an integer'},
|
|
status=400,
|
|
)
|
|
|
|
# cancel_booking_id can be empty or null
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event_0.id), params={'cancel_booking_id': ''}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
assert 'cancelled_booking_id' not in resp.json
|
|
assert Booking.objects.count() == 5
|
|
|
|
resp = app.post_json(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event_0.id), params={'cancel_booking_id': None}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
assert 'cancelled_booking_id' not in resp.json
|
|
assert Booking.objects.count() == 6
|
|
|
|
|
|
def test_soldout(app, some_data, user):
|
|
agenda_id = Agenda.objects.filter(label='Foo bar')[0].id
|
|
event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
|
|
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
|
|
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == 3
|
|
assert event.slug in [x['id'] for x in resp.json['data']]
|
|
|
|
for _ in range(event.places):
|
|
Booking(event=event).save()
|
|
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
|
|
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == 2
|
|
assert event.slug not in [x['id'] for x in resp.json['data'] if not x.get('disabled')]
|
|
assert event.slug in [x['id'] for x in resp.json['data'] if x.get('disabled')]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event.id), status=200)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
|
|
|
|
def test_waiting_list_booking(app, some_data, user):
|
|
agenda_id = Agenda.objects.filter(label='Foo bar')[0].id
|
|
event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
|
|
event.waiting_list_places = 5
|
|
event.save()
|
|
|
|
for _ in range(event.places):
|
|
Booking(event=event).save()
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event.id), status=200)
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['in_waiting_list'] is True
|
|
assert 'accept_url' in resp.json['api']
|
|
assert 'suspend_url' in resp.json['api']
|
|
assert 'cancel_url' in resp.json['api']
|
|
assert 'ics_url' in resp.json['api']
|
|
assert urlparse.urlparse(resp.json['api']['accept_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['cancel_url']).netloc
|
|
assert urlparse.urlparse(resp.json['api']['ics_url']).netloc
|
|
|
|
# cancel a booking that was not on the waiting list
|
|
booking = Booking.objects.filter(event=event, in_waiting_list=False)[0]
|
|
booking.cancel()
|
|
|
|
# check new booking is still done on the waiting list
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event.id), status=200)
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['in_waiting_list'] is True
|
|
|
|
# fill the waiting list
|
|
for _ in range(event.waiting_list_places):
|
|
Booking(event=event, in_waiting_list=True).save()
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event.id), status=200)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
|
|
|
|
def test_multiple_booking_api(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=NaN' % (agenda.slug, event.id), status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == "invalid value for count (NaN)" # legacy
|
|
assert resp.json['err_class'] == "invalid value for count (NaN)"
|
|
assert resp.json['err_desc'] == "invalid value for count (NaN)"
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=0' % (agenda.slug, event.id), status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == "count cannot be less than or equal to zero" # legacy
|
|
assert resp.json['err_class'] == "count cannot be less than or equal to zero"
|
|
assert resp.json['err_desc'] == "count cannot be less than or equal to zero"
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=-3' % (agenda.slug, event.id), status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == "count cannot be less than or equal to zero" # legacy
|
|
assert resp.json['err_class'] == "count cannot be less than or equal to zero"
|
|
assert resp.json['err_desc'] == "count cannot be less than or equal to zero"
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=3' % (agenda.slug, event.id))
|
|
Booking.objects.get(id=resp.json['booking_id'])
|
|
assert resp.json['datetime'] == localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
|
assert 'accept_url' in resp.json['api']
|
|
assert 'cancel_url' in resp.json['api']
|
|
assert Event.objects.get(id=event.id).booked_places == 3
|
|
|
|
app.post('/api/agenda/%s/fillslot/%s/?count=2' % (agenda.slug, event.id))
|
|
assert Event.objects.get(id=event.id).booked_places == 5
|
|
|
|
resp = app.post(resp.json['api']['cancel_url'])
|
|
assert Event.objects.get(id=event.id).booked_places == 2
|
|
|
|
# check available places overflow
|
|
event.places = 3
|
|
event.waiting_list_places = 8
|
|
event.save()
|
|
|
|
resp3 = app.post('/api/agenda/%s/fillslot/%s/?count=5' % (agenda.slug, event.id))
|
|
assert Event.objects.get(id=event.id).booked_places == 2
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 5
|
|
|
|
# check waiting list overflow
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=5' % (agenda.slug, event.id))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
assert Event.objects.get(id=event.id).booked_places == 2
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 5
|
|
|
|
# accept the waiting list
|
|
resp = app.post(resp3.json['api']['accept_url'])
|
|
assert Event.objects.get(id=event.id).booked_places == 7
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 0
|
|
|
|
# check with a short waiting list
|
|
Booking.objects.all().delete()
|
|
event.places = 4
|
|
event.waiting_list_places = 2
|
|
event.save()
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=5' % (agenda.slug, event.id))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=3' % (agenda.slug, event.id))
|
|
assert resp.json['err'] == 0
|
|
assert Event.objects.get(id=event.id).booked_places == 3
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 0
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=3' % (agenda.slug, event.id))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?count=2' % (agenda.slug, event.id))
|
|
assert resp.json['err'] == 0
|
|
assert Event.objects.get(id=event.id).booked_places == 3
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 2
|
|
|
|
|
|
def test_multiple_booking_api_fillslots(app, some_data, user):
|
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
|
# get slots of first 2 events
|
|
events = [
|
|
x for x in Event.objects.filter(agenda=agenda).order_by('start_datetime') if x.in_bookable_period()
|
|
][:2]
|
|
events_slugs = [x.slug for x in events]
|
|
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda.id)
|
|
slots = [x['id'] for x in resp_datetimes.json['data'] if x['id'] in events_slugs]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslots/?count=NaN' % agenda.slug, params={'slots': slots}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == "invalid value for count (NaN)" # legacy
|
|
assert resp.json['err_class'] == "invalid value for count (NaN)"
|
|
assert resp.json['err_desc'] == "invalid value for count (NaN)"
|
|
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 'NaN'}, status=400
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == "invalid payload" # legacy
|
|
assert resp.json['err_class'] == "invalid payload"
|
|
assert resp.json['err_desc'] == "invalid payload"
|
|
assert 'count' in resp.json['errors']
|
|
|
|
# get 3 places on 2 slots
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': '3'})
|
|
# one booking with 5 children
|
|
booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
cancel_url = resp.json['api']['cancel_url']
|
|
assert Booking.objects.filter(primary_booking=booking).count() == 5
|
|
assert resp.json['datetime'] == localtime(events[0].start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
|
assert 'accept_url' in resp.json['api']
|
|
assert 'cancel_url' in resp.json['api']
|
|
assert 'ics_url' in resp.json['api']
|
|
resp_events = resp.json['events']
|
|
assert len(resp_events) == len(events)
|
|
for (e, resp_e) in zip(events, resp_events):
|
|
assert e.slug == resp_e['slug']
|
|
assert e.description == resp_e['description']
|
|
assert str(e) == resp_e['text']
|
|
assert localtime(e.start_datetime).strftime('%Y-%m-%d %H:%M:%S') == resp_e['datetime']
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 3
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 2})
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 5
|
|
|
|
resp = app.post(cancel_url)
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 2
|
|
|
|
# check available places overflow
|
|
# NB: limit only the first event !
|
|
events[0].places = 3
|
|
events[0].waiting_list_places = 8
|
|
events[0].save()
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 5})
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 2
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 5
|
|
accept_url = resp.json['api']['accept_url']
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 5})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 2
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 5
|
|
|
|
# accept the waiting list
|
|
resp = app.post(accept_url)
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 7
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 0
|
|
|
|
# check with a short waiting list
|
|
Booking.objects.all().delete()
|
|
# NB: limit only the first event !
|
|
events[0].places = 4
|
|
events[0].waiting_list_places = 2
|
|
events[0].save()
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 5})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 3})
|
|
assert resp.json['err'] == 0
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 3
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 0
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 3})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'sold out' # legacy
|
|
assert resp.json['err_class'] == 'sold out'
|
|
assert resp.json['err_desc'] == 'sold out'
|
|
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': '2'})
|
|
assert resp.json['err'] == 0
|
|
for event in events:
|
|
assert Event.objects.get(id=event.id).booked_places == 3
|
|
assert Event.objects.get(id=event.id).booked_waiting_list_places == 2
|
|
|
|
|
|
def test_multiple_booking_move_booking(app, user):
|
|
agenda = Agenda(label='Foo bar')
|
|
agenda.save()
|
|
first_date = localtime(now()).replace(hour=17, minute=0, second=0, microsecond=0)
|
|
first_date += datetime.timedelta(days=1)
|
|
events = []
|
|
for i in range(10):
|
|
event = Event(start_datetime=first_date + datetime.timedelta(days=i), places=20, agenda=agenda)
|
|
event.save()
|
|
events.append(event)
|
|
|
|
first_two_events = events[:2]
|
|
events_slugs = [x.slug for x in first_two_events]
|
|
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda.id)
|
|
slots = [x['id'] for x in resp_datetimes.json['data'] if x['id'] in events_slugs]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
# get 1 place on 2 slots
|
|
resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots})
|
|
booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
assert Booking.objects.filter(primary_booking=booking).count() == 1
|
|
for event in first_two_events:
|
|
assert Event.objects.get(id=event.id).booked_places == 1
|
|
|
|
# change, 1 place on 2 other slots
|
|
last_two_events = events[-2:]
|
|
events_slugs = [x.slug for x in last_two_events]
|
|
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda.id)
|
|
slots = [x['id'] for x in resp_datetimes.json['data'] if x['id'] in events_slugs]
|
|
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'cancel_booking_id': booking.pk}
|
|
)
|
|
booking = Booking.objects.get(id=resp.json['booking_id'])
|
|
assert Booking.objects.filter(primary_booking=booking).count() == 1
|
|
for event in first_two_events:
|
|
assert Event.objects.get(id=event.id).booked_places == 0
|
|
for event in last_two_events:
|
|
assert Event.objects.get(id=event.id).booked_places == 1
|
|
|
|
|
|
def test_agenda_meeting_api_multiple_desk(app, meetings_agenda, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda_id = meetings_agenda.slug
|
|
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
|
|
|
# add booking of another meeting type
|
|
meeting_type2 = MeetingType.objects.create(agenda=meetings_agenda, label='Tux kart', duration=60)
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type2.id)
|
|
event_id = resp.json['data'][0]['id']
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
cancel_url = resp.json['api']['cancel_url']
|
|
|
|
# add a second desk
|
|
time_period = meetings_agenda.desk_set.first().timeperiod_set.first()
|
|
desk2 = Desk.objects.create(label='Desk 2', agenda=meetings_agenda)
|
|
TimePeriod.objects.create(
|
|
start_time=time_period.start_time,
|
|
end_time=time_period.end_time,
|
|
weekday=time_period.weekday,
|
|
desk=desk2,
|
|
)
|
|
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
event_id = resp.json['data'][1]['id']
|
|
resp_booking = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert Booking.objects.count() == 2
|
|
assert resp_booking.json['datetime'] == localtime(Booking.objects.last().event.start_datetime).strftime(
|
|
'%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
resp2 = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
assert len(resp.json['data']) == len([x for x in resp2.json['data'] if not x['disabled']]) + 1
|
|
|
|
# try booking the same timeslot and fail
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert Booking.objects.count() == 2
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'no more desk available' # legacy
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
assert resp.json['err_desc'] == 'no more desk available'
|
|
|
|
# cancel first booking and retry
|
|
resp = app.post(cancel_url)
|
|
# capture number of queries made for datetime endpoint with few bookings
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
queries_count_datetime1 = len(ctx.captured_queries)
|
|
assert len(resp2.json['data']) == len([x for x in resp.json['data'] if not x['disabled']])
|
|
|
|
# capture number of queries made for fillslot endpoint with few bookings
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
queries_count_fillslot1 = len(ctx.captured_queries)
|
|
|
|
assert resp_booking.json['datetime'] == localtime(Booking.objects.last().event.start_datetime).strftime(
|
|
'%Y-%m-%d %H:%M:%S'
|
|
)
|
|
cancel_url = resp.json['api']['cancel_url']
|
|
|
|
resp3 = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
assert len(resp2.json['data']) == len([x for x in resp3.json['data'] if not x['disabled']]) + 1
|
|
|
|
# cancel a booking
|
|
resp = app.post(cancel_url)
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
assert len(resp.json['data']) == len(resp2.json['data'])
|
|
|
|
# try booking the same slot to make sure that cancelled booking has freed the slot
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert Booking.objects.count() == 4
|
|
assert Booking.objects.exclude(cancellation_datetime__isnull=True).count() == 2
|
|
assert resp_booking.json['datetime'] == localtime(Booking.objects.last().event.start_datetime).strftime(
|
|
'%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
# try booking the same timeslot again and fail
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'no more desk available' # legacy
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
assert resp.json['err_desc'] == 'no more desk available'
|
|
|
|
# fill the agenda and make sure big O is O(1)
|
|
for event_data in resp2.json['data'][2:10]:
|
|
booking_url = event_data['api']['fillslot_url']
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
app.post(booking_url)
|
|
assert len(ctx.captured_queries) == queries_count_fillslot1
|
|
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
assert queries_count_datetime1 == len(ctx.captured_queries)
|
|
|
|
|
|
def test_agenda_meeting_api_fillslots_multiple_desks(app, meetings_agenda, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda_id = meetings_agenda.slug
|
|
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
|
|
|
# add a second desk, same timeperiods
|
|
time_period = meetings_agenda.desk_set.first().timeperiod_set.first()
|
|
desk2 = Desk.objects.create(label='Desk 2', agenda=meetings_agenda)
|
|
TimePeriod.objects.create(
|
|
start_time=time_period.start_time,
|
|
end_time=time_period.end_time,
|
|
weekday=time_period.weekday,
|
|
desk=desk2,
|
|
)
|
|
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
slots = [x['id'] for x in resp.json['data'][:3]]
|
|
|
|
def get_free_places():
|
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
|
return len([x for x in resp.json['data'] if not x['disabled']])
|
|
|
|
start_free_places = get_free_places()
|
|
|
|
# booking 3 slots on desk 1
|
|
fillslots_url = '/api/agenda/%s/fillslots/' % agenda_id
|
|
resp = app.post(fillslots_url, params={'slots': slots})
|
|
assert resp.json['err'] == 0
|
|
desk1 = resp.json['desk']['slug']
|
|
cancel_url = resp.json['api']['cancel_url']
|
|
assert get_free_places() == start_free_places
|
|
|
|
# booking same slots again, will be on desk 2
|
|
resp = app.post(fillslots_url, params={'slots': slots})
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['desk']['slug'] != desk2
|
|
# 3 places are disabled in datetimes list
|
|
assert get_free_places() == start_free_places - len(slots)
|
|
|
|
# try booking again: no desk available
|
|
resp = app.post(fillslots_url, params={'slots': slots})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'no more desk available' # legacy
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
assert resp.json['err_desc'] == 'no more desk available'
|
|
assert get_free_places() == start_free_places - len(slots)
|
|
|
|
# cancel desk 1 booking
|
|
resp = app.post(cancel_url)
|
|
assert resp.json['err'] == 0
|
|
# all places are free again
|
|
assert get_free_places() == start_free_places
|
|
|
|
# booking a single slot (must be on desk 1)
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, slots[1]))
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['desk']['slug'] == desk1
|
|
cancel_url = resp.json['api']['cancel_url']
|
|
assert get_free_places() == start_free_places - 1
|
|
|
|
# try booking the 3 slots again: no desk available, one slot is not fully available
|
|
resp = app.post(fillslots_url, params={'slots': slots})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['reason'] == 'no more desk available' # legacy
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
assert resp.json['err_desc'] == 'no more desk available'
|
|
|
|
# cancel last signel slot booking, desk1 will be free
|
|
resp = app.post(cancel_url)
|
|
assert resp.json['err'] == 0
|
|
assert get_free_places() == start_free_places
|
|
|
|
# booking again is ok, on desk 1
|
|
resp = app.post(fillslots_url, params={'slots': slots})
|
|
assert resp.json['err'] == 0
|
|
assert resp.json['desk']['slug'] == desk1
|
|
assert get_free_places() == start_free_places - len(slots)
|
|
|
|
|
|
def test_agenda_meeting_same_day(app, meetings_agenda, mock_now, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda = Agenda(label='Foo', kind='meetings')
|
|
agenda.minimal_booking_delay = 0
|
|
agenda.maximal_booking_delay = 15
|
|
agenda.save()
|
|
meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah', duration=30)
|
|
datetime_url = '/api/agenda/meetings/%s/datetimes/' % meeting_type.id
|
|
desk1 = Desk.objects.create(label='foo', agenda=agenda)
|
|
desk2 = Desk.objects.create(label='bar', agenda=agenda)
|
|
for weekday in range(7):
|
|
TimePeriod.objects.create(
|
|
weekday=weekday, start_time=datetime.time(11, 0), end_time=datetime.time(12, 30), desk=desk1
|
|
)
|
|
TimePeriod.objects.create(
|
|
weekday=weekday, start_time=datetime.time(11, 0), end_time=datetime.time(12, 30), desk=desk2
|
|
)
|
|
resp = app.get(datetime_url)
|
|
event_data = resp.json['data'][0]
|
|
# check first proposed date is on the same day unless we're past the last
|
|
# open timeperiod.
|
|
event_datetime = datetime.datetime.strptime(event_data['datetime'], '%Y-%m-%d %H:%M:%S').timetuple()
|
|
assert (
|
|
event_datetime[:3] == mock_now.timetuple()[:3] and event_datetime[3:5] >= mock_now.timetuple()[3:5]
|
|
) or (event_datetime[:3] > mock_now.timetuple()[:3] and event_datetime[3:5] < mock_now.timetuple()[3:5])
|
|
|
|
# check booking works
|
|
first_booking_url = resp.json['data'][0]['api']['fillslot_url']
|
|
assert app.post(first_booking_url).json['err'] == 0
|
|
assert app.post(first_booking_url).json['err'] == 0
|
|
assert app.post(first_booking_url).json['err'] == 1
|
|
|
|
last_booking_url = resp.json['data'][-1]['api']['fillslot_url']
|
|
assert app.post(last_booking_url).json['err'] == 0
|
|
assert app.post(last_booking_url).json['err'] == 0
|
|
assert app.post(last_booking_url).json['err'] == 1
|
|
|
|
# check full datetimes are marked as disabled
|
|
resp = app.get(datetime_url)
|
|
assert resp.json['data'][0]['disabled']
|
|
assert not resp.json['data'][1]['disabled']
|
|
assert resp.json['data'][-1]['disabled']
|
|
assert not resp.json['data'][-2]['disabled']
|
|
|
|
|
|
def test_virtual_agendas_meetings_booking(app, mock_now, user):
|
|
foo_agenda = Agenda.objects.create(
|
|
label='Foo Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
|
|
)
|
|
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30)
|
|
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
|
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=foo_desk_1,
|
|
)
|
|
TimePeriod.objects.create(
|
|
weekday=1,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=foo_desk_1,
|
|
)
|
|
bar_agenda = Agenda.objects.create(
|
|
label='Bar Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
|
|
)
|
|
MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30)
|
|
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
|
|
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=bar_desk_1,
|
|
)
|
|
TimePeriod.objects.create(
|
|
weekday=1,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=bar_desk_1,
|
|
)
|
|
|
|
virt_agenda = Agenda.objects.create(
|
|
label='Virtual Agenda', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=5
|
|
)
|
|
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda)
|
|
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda)
|
|
virt_meeting_type = virt_agenda.iter_meetingtypes()[0]
|
|
# We are saturday and we can book for next monday and tuesday, 4 slots available each day
|
|
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (virt_agenda.slug, virt_meeting_type.slug)
|
|
resp = app.get(api_url)
|
|
assert len(resp.json['data']) == 8
|
|
|
|
# make a booking
|
|
fillslot_url = resp.json['data'][0]['api']['fillslot_url']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp_booking = app.post(fillslot_url)
|
|
assert Booking.objects.count() == 1
|
|
booking = Booking.objects.get(pk=resp_booking.json['booking_id'])
|
|
assert (
|
|
resp_booking.json['datetime']
|
|
== localtime(booking.event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
|
== resp.json['data'][0]['datetime']
|
|
)
|
|
first_booking_agenda = resp_booking.json['agenda']['slug']
|
|
|
|
assert resp_booking.json['end_datetime'] == localtime(
|
|
Booking.objects.all()[0].event.end_datetime
|
|
).strftime('%Y-%m-%d %H:%M:%S')
|
|
assert resp_booking.json['duration'] == 30
|
|
|
|
# second booking on the same slot (available on the second real agenda)
|
|
resp_booking = app.post(fillslot_url)
|
|
assert Booking.objects.count() == 2
|
|
booking = Booking.objects.get(pk=resp_booking.json['booking_id'])
|
|
assert (
|
|
resp_booking.json['datetime']
|
|
== localtime(booking.event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
|
== resp.json['data'][0]['datetime']
|
|
)
|
|
second_booking_agenda = resp_booking.json['agenda']['slug']
|
|
assert {first_booking_agenda, second_booking_agenda} == {'foo-meeting', 'bar-meeting'}
|
|
|
|
# try booking the same timeslot a third time: full
|
|
resp_booking = app.post(fillslot_url)
|
|
assert resp_booking.json['err'] == 1
|
|
assert resp_booking.json['err_class'] == 'no more desk available'
|
|
assert resp_booking.json['err_desc'] == 'no more desk available'
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-02-25')
|
|
def test_virtual_agendas_meetings_booking_exclude_slots(app, user):
|
|
tomorrow = now() + datetime.timedelta(days=1)
|
|
agenda = Agenda.objects.create(
|
|
label='Agenda', kind='meetings', minimal_booking_delay=0, maximal_booking_delay=10
|
|
)
|
|
desk = Desk.objects.create(agenda=agenda, slug='desk')
|
|
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo-bar')
|
|
TimePeriod.objects.create(
|
|
weekday=tomorrow.date().weekday(),
|
|
start_time=datetime.time(9, 0),
|
|
end_time=datetime.time(17, 00),
|
|
desk=desk,
|
|
)
|
|
agenda2 = agenda.duplicate()
|
|
agenda3 = agenda.duplicate()
|
|
virt_agenda = Agenda.objects.create(
|
|
label='Virtual Agenda', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=10
|
|
)
|
|
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=agenda)
|
|
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=agenda2)
|
|
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=agenda3)
|
|
|
|
event = Event.objects.create(
|
|
agenda=agenda,
|
|
meeting_type=meeting_type,
|
|
places=1,
|
|
start_datetime=localtime(tomorrow).replace(hour=9, minute=0),
|
|
desk=desk,
|
|
)
|
|
Booking.objects.create(event=event, user_external_id='42')
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
|
|
params={'user_external_id': '42'},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
|
|
params={'user_external_id': '42', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
|
|
params={'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
virt_agenda.minimal_booking_delay = None
|
|
virt_agenda.maximal_booking_delay = None
|
|
virt_agenda.save()
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
|
|
params={'user_external_id': '42', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'no more desk available'
|
|
|
|
|
|
def test_virtual_agendas_meetings_booking_default_policy(app, mock_now, user):
|
|
foo_agenda = Agenda.objects.create(
|
|
label='Foo Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
|
|
)
|
|
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30)
|
|
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
|
foo_desk_2 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 2')
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=foo_desk_1,
|
|
)
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=foo_desk_2,
|
|
)
|
|
|
|
bar_agenda = Agenda.objects.create(
|
|
label='Bar Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
|
|
)
|
|
MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30)
|
|
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
|
|
bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2')
|
|
bar_desk_3 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
|
|
bar_desk_4 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=bar_desk_1,
|
|
)
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=bar_desk_2,
|
|
)
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=bar_desk_3,
|
|
)
|
|
TimePeriod.objects.create(
|
|
weekday=0,
|
|
start_time=datetime.time(10, 0),
|
|
end_time=datetime.time(12, 0),
|
|
desk=bar_desk_4,
|
|
)
|
|
|
|
virt_agenda = Agenda.objects.create(
|
|
label='Virtual Agenda', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=5
|
|
)
|
|
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda)
|
|
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda)
|
|
virt_meeting_type = virt_agenda.iter_meetingtypes()[0]
|
|
# We are saturday and we can book for next monday and tuesday, 4 slots available each day
|
|
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (virt_agenda.slug, virt_meeting_type.slug)
|
|
resp = app.get(api_url)
|
|
# We are saturday and we can book for next monday, 4 slots available each day
|
|
assert len(resp.json['data']) == 4
|
|
|
|
# there are 6 desks so we can make 6 bookings on the same slot
|
|
fillslot_url = resp.json['data'][0]['api']['fillslot_url']
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
for i in range(1, 7):
|
|
foo_num_bookings = Booking.objects.filter(event__desk__agenda=foo_agenda).count()
|
|
bar_num_bookings = Booking.objects.filter(event__desk__agenda=bar_agenda).count()
|
|
foo_fill_rate = foo_num_bookings / 2
|
|
bar_fill_rate = bar_num_bookings / 4
|
|
next_agenda = None
|
|
if i != 1:
|
|
if foo_fill_rate < bar_fill_rate:
|
|
next_agenda = foo_agenda
|
|
elif foo_fill_rate > bar_fill_rate:
|
|
next_agenda = bar_agenda
|
|
elif foo_fill_rate == bar_fill_rate:
|
|
next_agenda = None
|
|
|
|
resp_booking = app.post(fillslot_url)
|
|
assert Booking.objects.count() == i
|
|
booking = Booking.objects.get(pk=resp_booking.json['booking_id'])
|
|
assert (
|
|
resp_booking.json['datetime']
|
|
== localtime(booking.event.start_datetime).strftime('%Y-%m-%d %H:%M:%S')
|
|
== resp.json['data'][0]['datetime']
|
|
)
|
|
if next_agenda:
|
|
assert booking.event.agenda == next_agenda
|
|
|
|
foo_num_bookings = Booking.objects.filter(event__desk__agenda=foo_agenda).count()
|
|
bar_num_bookings = Booking.objects.filter(event__desk__agenda=bar_agenda).count()
|
|
assert foo_num_bookings == 2
|
|
assert bar_num_bookings == 4
|
|
|
|
|
|
@pytest.mark.freeze_time('2017-04-01')
|
|
def test_duration_on_booking_api_fillslot_response(app, user):
|
|
agenda = Agenda(label='Foo bar')
|
|
agenda.save()
|
|
first_date = datetime.datetime(2017, 5, 20, 1, 12)
|
|
durations = [None, 0, 45]
|
|
evt = []
|
|
for i in range(3):
|
|
evt.append(
|
|
Event(
|
|
start_datetime=first_date + datetime.timedelta(days=i),
|
|
duration=durations[i],
|
|
places=20,
|
|
agenda=agenda,
|
|
)
|
|
)
|
|
evt[i].save()
|
|
|
|
assert evt[0].end_datetime is None
|
|
assert evt[1].end_datetime == evt[1].start_datetime
|
|
assert evt[2].end_datetime == evt[2].start_datetime + datetime.timedelta(minutes=45)
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, evt[0].id))
|
|
assert resp.json['datetime'] == '2017-05-20 01:12:00'
|
|
assert resp.json['end_datetime'] is None
|
|
assert 'ics_url' in resp.json['api']
|
|
ics = app.get(resp.json['api']['ics_url']).text
|
|
assert 'DTSTART:20170519T231200Z' in ics
|
|
assert 'DTEND:' not in ics
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, evt[1].id))
|
|
assert resp.json['datetime'] == '2017-05-21 01:12:00'
|
|
assert resp.json['end_datetime'] == resp.json['datetime']
|
|
assert 'ics_url' in resp.json['api']
|
|
ics = app.get(resp.json['api']['ics_url']).text
|
|
assert 'DTSTART:20170520T231200Z' in ics
|
|
assert 'DTEND:20170520T231200Z' in ics
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, evt[2].id))
|
|
assert resp.json['datetime'] == '2017-05-22 01:12:00'
|
|
assert resp.json['end_datetime'] == '2017-05-22 01:57:00'
|
|
assert 'ics_url' in resp.json['api']
|
|
ics = app.get(resp.json['api']['ics_url']).text
|
|
assert 'DTSTART:20170521T231200Z' in ics
|
|
assert 'DTEND:20170521T235700Z' in ics
|
|
|
|
|
|
@pytest.mark.freeze_time('2017-04-01')
|
|
def test_duration_on_booking_api_fillslots_response(app, user):
|
|
agenda = Agenda(label='Foo bar')
|
|
agenda.save()
|
|
first_date = datetime.datetime(2017, 5, 20, 1, 12)
|
|
durations = [None, 0, 45]
|
|
evt = []
|
|
for i in range(3):
|
|
evt.append(
|
|
Event(
|
|
start_datetime=first_date + datetime.timedelta(days=i),
|
|
duration=durations[i],
|
|
places=20,
|
|
agenda=agenda,
|
|
)
|
|
)
|
|
evt[i].save()
|
|
|
|
assert evt[0].end_datetime is None
|
|
assert evt[1].end_datetime == evt[1].start_datetime
|
|
assert evt[2].end_datetime == evt[2].start_datetime + datetime.timedelta(minutes=45)
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
# first event having null duration
|
|
string_param = ','.join([str(e.id) for e in evt[::-1]]) # unordered parameters
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': string_param})
|
|
r_evt = resp.json['events']
|
|
|
|
assert r_evt[0]['datetime'] == '2017-05-20 01:12:00'
|
|
assert r_evt[0]['end_datetime'] is None
|
|
assert r_evt[1]['datetime'] == '2017-05-21 01:12:00'
|
|
assert r_evt[1]['end_datetime'] == r_evt[1]['datetime']
|
|
assert r_evt[2]['datetime'] == '2017-05-22 01:12:00'
|
|
assert r_evt[2]['end_datetime'] == '2017-05-22 01:57:00'
|
|
assert 'ics_url' in resp.json['api']
|
|
ics = app.get(resp.json['api']['ics_url']).text
|
|
assert 'DTSTART:20170519T231200Z' in ics
|
|
assert 'DTEND:' not in ics
|
|
|
|
# first event having duration
|
|
evt[0].duration = 90
|
|
evt[0].save()
|
|
resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': string_param})
|
|
r_evt = resp.json['events']
|
|
|
|
assert r_evt[0]['datetime'] == '2017-05-20 01:12:00'
|
|
assert r_evt[0]['end_datetime'] == '2017-05-20 02:42:00'
|
|
assert 'ics_url' in resp.json['api']
|
|
ics = app.get(resp.json['api']['ics_url']).text
|
|
assert 'DTSTART:20170519T231200Z' in ics
|
|
assert 'DTEND:20170520T004200Z' in ics
|
|
|
|
|
|
def test_fillslot_past_event(app, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
|
)
|
|
event1 = Event.objects.create(
|
|
label='Today before now',
|
|
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
event2 = Event.objects.create(
|
|
label='Today after now',
|
|
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event1.slug))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event1.slug
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event1.slug), params={'events': 'future'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event1.slug
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event1.slug), params={'events': 'past'})
|
|
assert resp.json['err'] == 0
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event1.slug), params={'events': 'all'})
|
|
assert resp.json['err'] == 0
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event2.slug))
|
|
assert resp.json['err'] == 0
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event2.slug), params={'events': 'future'})
|
|
assert resp.json['err'] == 0
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event2.slug), params={'events': 'past'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event2.slug
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/?events=past' % (agenda.slug, event2.slug))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event2.slug
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/?events=all' % (agenda.slug, event2.slug), params={'events': 'past'}
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event2.slug
|
|
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event2.slug), params={'events': 'all'})
|
|
assert resp.json['err'] == 0
|
|
|
|
# check canceled
|
|
event1.cancel()
|
|
event2.cancel()
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event1.slug), params={'events': 'all'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is cancelled' % event1.slug
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event2.slug), params={'events': 'all'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is cancelled' % event2.slug
|
|
|
|
|
|
def test_fillslot_past_event_places(app, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
|
)
|
|
event = Event.objects.create(
|
|
label='Today before now',
|
|
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
|
|
places=1,
|
|
agenda=agenda,
|
|
)
|
|
Booking.objects.create(event=event)
|
|
|
|
# always bookable if past
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'events': 'past'})
|
|
assert resp.json['err'] == 0
|
|
|
|
event.waiting_list_places = 1
|
|
event.save()
|
|
Booking.objects.create(event=event, in_waiting_list=True)
|
|
# always bookable if past
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'events': 'past'})
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
def test_fillslot_past_events_min_places(app, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
|
)
|
|
event = Event.objects.create(
|
|
label='Today before now',
|
|
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
|
|
places=1,
|
|
agenda=agenda,
|
|
)
|
|
|
|
# always bookable if past
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'events': 'past', 'count': 2}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
def test_fillslot_past_events_exclude_slots(app, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
|
)
|
|
event = Event.objects.create(
|
|
label='Today before now',
|
|
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
Booking.objects.create(event=event, user_external_id='42')
|
|
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug),
|
|
params={'events': 'past', 'user_external_id': '35', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug),
|
|
params={'events': 'past', 'user_external_id': '35', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is already booked by user' % event.slug
|
|
|
|
|
|
def test_fillslot_past_events_recurring_event(app, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
|
)
|
|
start_datetime = localtime(now() - datetime.timedelta(days=3 * 7))
|
|
event = Event.objects.create(
|
|
label='Recurring',
|
|
start_datetime=start_datetime,
|
|
recurrence_days=[start_datetime.weekday()],
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
event_slug = '%s:%s' % (
|
|
event.slug,
|
|
(event.start_datetime - datetime.timedelta(days=7)).strftime('%Y-%m-%d-%H%M'),
|
|
) # too soon
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'all'}, status=400
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'invalid datetime for event %s' % event_slug
|
|
|
|
event_slug = '%s:%s' % (
|
|
event.slug,
|
|
(event.start_datetime + datetime.timedelta(days=7)).strftime('%Y-%m-%d-%H%M'),
|
|
)
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'past'})
|
|
assert resp.json['err'] == 0
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'all'})
|
|
assert resp.json['err'] == 0
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'future'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event_slug.replace(':', '--')
|
|
|
|
event_slug = '%s:%s' % (
|
|
event.slug,
|
|
(event.start_datetime + datetime.timedelta(days=50 * 7)).strftime('%Y-%m-%d-%H%M'),
|
|
) # too late
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'all'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event_slug.replace(':', '--')
|
|
|
|
event_slug = '%s:%s' % (
|
|
event.slug,
|
|
(event.start_datetime + datetime.timedelta(days=4 * 7)).strftime('%Y-%m-%d-%H%M'),
|
|
)
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'past'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event_slug.replace(':', '--')
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'all'})
|
|
assert resp.json['err'] == 0
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event_slug), params={'events': 'future'})
|
|
assert resp.json['err'] == 0
|
|
|
|
# check exclude_user_external_id
|
|
first_recurrence = event.get_or_create_event_recurrence(event.start_datetime)
|
|
Booking.objects.create(event=first_recurrence, user_external_id='42')
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, first_recurrence.slug),
|
|
params={'events': 'past', 'user_external_id': '42', 'exclude_user': True},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is already booked by user' % first_recurrence.slug
|
|
|
|
# check canceled
|
|
first_recurrence.cancel()
|
|
resp = app.post(
|
|
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, first_recurrence.slug), params={'events': 'past'}
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is cancelled' % first_recurrence.slug
|
|
|
|
|
|
def test_recurring_events_api_fillslots(app, user, freezer):
|
|
freezer.move_to('2021-09-06 12:00')
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now(),
|
|
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
|
|
places=2,
|
|
waiting_list_places=1,
|
|
agenda=agenda,
|
|
recurrence_end_date=now() + datetime.timedelta(days=364),
|
|
)
|
|
event.create_all_recurrences()
|
|
sunday_event = Event.objects.create(
|
|
label='Sunday Event',
|
|
start_datetime=now(),
|
|
recurrence_days=[6],
|
|
places=2,
|
|
waiting_list_places=1,
|
|
agenda=agenda,
|
|
recurrence_end_date=now() + datetime.timedelta(days=364),
|
|
)
|
|
sunday_event.create_all_recurrences()
|
|
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug)
|
|
assert len(resp.json['data']) == 5
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug
|
|
params = {'user_external_id': 'user_id'}
|
|
# Book Monday and Thursday of first event and Sunday of second event
|
|
params['slots'] = 'foo-bar@event:0,foo-bar@event:3,foo-bar@sunday-event:6'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 156
|
|
|
|
assert Booking.objects.count() == 156
|
|
assert Booking.objects.filter(event__primary_event=event).count() == 104
|
|
assert Booking.objects.filter(event__primary_event=sunday_event).count() == 52
|
|
|
|
events = Event.objects.filter(primary_event__isnull=False)
|
|
assert events.filter(booked_places=1).count() == 156
|
|
|
|
# one recurrence is booked separately
|
|
event = Event.objects.filter(primary_event__isnull=False).first()
|
|
Booking.objects.create(event=event)
|
|
|
|
params['user_external_id'] = 'user_id_2'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 156
|
|
assert not resp.json['full_events']
|
|
assert Booking.objects.count() == 313
|
|
events = Event.objects.filter(primary_event__isnull=False)
|
|
assert events.filter(booked_places=2).count() == 156
|
|
# one booking has been put in waiting list
|
|
assert events.filter(booked_waiting_list_places=1).count() == 1
|
|
|
|
params['user_external_id'] = 'user_id_3'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
# everything goes in waiting list
|
|
assert events.filter(booked_waiting_list_places=1).count() == 156
|
|
# but an event was full
|
|
assert resp.json['booking_count'] == 155
|
|
assert len(resp.json['full_events']) == 1
|
|
assert resp.json['full_events'][0]['slug'] == event.slug
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
|
|
# events are full
|
|
params['user_external_id'] = 'user_id_4'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
|
|
# no event in range
|
|
resp = app.post_json(fillslots_url + '&date_start=2020-10-06&date_end=2020-11-06', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
|
|
params['slots'] = 'foo-bar@event:1'
|
|
resp = app.post_json(fillslots_url + '&date_start=2021-10-06&date_end=2021-11-06', params=params)
|
|
assert resp.json['booking_count'] == 4
|
|
assert Booking.objects.filter(user_external_id='user_id_4').count() == 4
|
|
|
|
resp = app.post_json(fillslots_url, params={'slots': 'foo-bar@event:0'}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
assert resp.json['errors']['user_external_id'] == ['This field is required.']
|
|
|
|
resp = app.post_json(fillslots_url, params={'user_external_id': 'a'}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'invalid payload'
|
|
assert resp.json['errors']['slots'] == ['This field is required.']
|
|
|
|
resp = app.post_json(fillslots_url, params={'user_external_id': 'a', 'slots': 'foo-bar@a:a'}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['errors']['slots'] == ['invalid slot: foo-bar@a:a']
|
|
|
|
resp = app.post_json(fillslots_url, params={'user_external_id': 'a', 'slots': 'foo-bar@a:1'}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['errors']['slots'] == ['event a of agenda foo-bar is not bookable']
|
|
|
|
resp = app.post_json(fillslots_url, params={'user_external_id': 'a', 'slots': 'foo-bar'}, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['errors']['slots'] == ['Invalid format for slot foo-bar']
|
|
|
|
|
|
def test_recurring_events_api_fillslots_waiting_list(app, user, freezer):
|
|
freezer.move_to('2021-09-06 12:00')
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now(),
|
|
recurrence_days=[0],
|
|
places=2,
|
|
waiting_list_places=2,
|
|
agenda=agenda,
|
|
recurrence_end_date=now() + datetime.timedelta(days=30),
|
|
)
|
|
event.create_all_recurrences()
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
|
|
# create bookings in waiting list
|
|
for recurrence in event.recurrences.all():
|
|
Booking.objects.create(event=recurrence, in_waiting_list=True)
|
|
events = Event.objects.filter(primary_event__isnull=False)
|
|
assert events.filter(booked_waiting_list_places=1).count() == 5
|
|
|
|
# check that new bookings are put in waiting list despite free slots on main list
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event:0'}
|
|
resp = app.post_json('/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug, params=params)
|
|
assert resp.json['booking_count'] == 5
|
|
assert events.filter(booked_waiting_list_places=2).count() == 5
|
|
|
|
|
|
def test_recurring_events_api_fillslots_change_bookings(app, user, freezer):
|
|
freezer.move_to('2021-09-06 12:00')
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now(),
|
|
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
|
|
places=1,
|
|
waiting_list_places=1,
|
|
agenda=agenda,
|
|
recurrence_end_date=now() + datetime.timedelta(days=364),
|
|
)
|
|
event.create_all_recurrences()
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug
|
|
params = {'user_external_id': 'user_id'}
|
|
# Book Monday and Thursday
|
|
params['slots'] = 'foo-bar@event:0,foo-bar@event:3'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 104
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert Booking.objects.count() == 104
|
|
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 52
|
|
assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 52
|
|
|
|
# Change booking to Monday and Tuesday
|
|
params['slots'] = 'foo-bar@event:0,foo-bar@event:1'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 52
|
|
assert resp.json['cancelled_booking_count'] == 52
|
|
assert Booking.objects.count() == 104
|
|
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 52
|
|
assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 52
|
|
|
|
# Booking again does nothing
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert Booking.objects.count() == 104
|
|
|
|
params = {'user_external_id': 'user_id_2'}
|
|
params['slots'] = 'foo-bar@event:0,foo-bar@event:3'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 104
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert Booking.objects.count() == 208
|
|
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 104
|
|
assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 52
|
|
events = Event.objects.filter(primary_event__isnull=False)
|
|
assert events.filter(booked_places=1).count() == 156
|
|
assert events.filter(booked_waiting_list_places=1).count() == 52
|
|
|
|
params['slots'] = 'foo-bar@event:1,foo-bar@event:4'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 104
|
|
assert resp.json['cancelled_booking_count'] == 104
|
|
assert Booking.objects.count() == 208
|
|
assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 104
|
|
assert Booking.objects.filter(event__start_datetime__week_day=6).count() == 52
|
|
events = Event.objects.filter(primary_event__isnull=False)
|
|
assert events.filter(booked_places=1).count() == 156
|
|
assert events.filter(booked_waiting_list_places=1).count() == 52
|
|
|
|
# passing empty slots cancels all bookings
|
|
params['slots'] = ''
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['cancelled_booking_count'] == 104
|
|
assert Booking.objects.filter(user_external_id='user_id_2').count() == 0
|
|
|
|
# only recurring events are impacted
|
|
normal_event = Event.objects.create(
|
|
start_datetime=now() + datetime.timedelta(days=1), places=2, agenda=agenda
|
|
)
|
|
Booking.objects.create(event=normal_event, user_external_id='user_id')
|
|
resp = app.post_json(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'foo-bar@event:0'})
|
|
assert resp.json['cancelled_booking_count'] == 52
|
|
assert Booking.objects.filter(user_external_id='user_id', event=normal_event).count() == 1
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_recurring_events_api_fillslots_multiple_agendas(app, user):
|
|
agenda = Agenda.objects.create(label='First Agenda', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
start, end = now(), now() + datetime.timedelta(days=30)
|
|
event_a = Event.objects.create(
|
|
label='A',
|
|
start_datetime=start,
|
|
places=2,
|
|
recurrence_end_date=end,
|
|
recurrence_days=[0, 2, 5],
|
|
agenda=agenda,
|
|
)
|
|
event_a.create_all_recurrences()
|
|
event_b = Event.objects.create(
|
|
label='B', start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1], agenda=agenda
|
|
)
|
|
event_b.create_all_recurrences()
|
|
agenda2 = Agenda.objects.create(label='Second Agenda', kind='events')
|
|
Desk.objects.create(agenda=agenda2, slug='_exceptions_holder')
|
|
event_c = Event.objects.create(
|
|
label='C',
|
|
start_datetime=start,
|
|
places=2,
|
|
recurrence_end_date=end,
|
|
recurrence_days=[2, 3],
|
|
agenda=agenda2,
|
|
)
|
|
event_c.create_all_recurrences()
|
|
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=first-agenda,second-agenda')
|
|
assert len(resp.json['data']) == 6
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s'
|
|
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@a:0,first-agenda@a:5,second-agenda@c:3'}
|
|
resp = app.post_json(fillslots_url % 'first-agenda,second-agenda', params=params)
|
|
assert resp.json['booking_count'] == 13
|
|
|
|
assert Booking.objects.count() == 13
|
|
assert Booking.objects.filter(event__primary_event=event_a).count() == 9
|
|
assert Booking.objects.filter(event__primary_event=event_b).count() == 0
|
|
assert Booking.objects.filter(event__primary_event=event_c).count() == 4
|
|
|
|
# update bookings
|
|
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@b:1'}
|
|
resp = app.post_json(fillslots_url % 'first-agenda,second-agenda', params=params)
|
|
|
|
assert resp.json['booking_count'] == 5
|
|
assert resp.json['cancelled_booking_count'] == 13
|
|
assert Booking.objects.filter(event__primary_event=event_a).count() == 0
|
|
assert Booking.objects.filter(event__primary_event=event_b).count() == 5
|
|
assert Booking.objects.filter(event__primary_event=event_c).count() == 0
|
|
|
|
# error if slot's agenda is not in querystring
|
|
resp = app.post_json(fillslots_url % 'second-agenda', params=params, status=400)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['errors']['slots'] == [
|
|
'Some events belong to agendas that are not present in querystring: first-agenda'
|
|
]
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_recurring_events_api_fillslots_multiple_agendas_queries(app, user):
|
|
for i in range(20):
|
|
agenda = Agenda.objects.create(slug=f'{i}', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
start, end = now(), now() + datetime.timedelta(days=30)
|
|
event = Event.objects.create(
|
|
start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1, 2], agenda=agenda
|
|
)
|
|
event.create_all_recurrences()
|
|
|
|
agenda_slugs = ','.join(str(i) for i in range(20))
|
|
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda_slugs)
|
|
events_to_book = [x['id'] for x in resp.json['data']]
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.post_json(
|
|
'/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda_slugs,
|
|
params={'slots': events_to_book, 'user_external_id': 'user'},
|
|
)
|
|
assert resp.json['booking_count'] == 180
|
|
assert len(ctx.captured_queries) == 16
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots(app, user):
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=1),
|
|
places=2,
|
|
waiting_list_places=1,
|
|
agenda=agenda,
|
|
)
|
|
second_event = Event.objects.create(
|
|
label='Event 2', start_datetime=now() + datetime.timedelta(days=2), places=2, agenda=agenda
|
|
)
|
|
third_event = Event.objects.create(
|
|
label='Event 3', start_datetime=now() + datetime.timedelta(days=3), places=2, agenda=agenda
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug
|
|
params = {'user_external_id': 'user_id', 'slots': 'event,event-2'}
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert len(resp.json['waiting_list_events']) == 0
|
|
|
|
events = Event.objects.all()
|
|
assert events.filter(booked_places=1).count() == 2
|
|
|
|
params['user_external_id'] = 'user_id_2'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert len(resp.json['waiting_list_events']) == 0
|
|
|
|
params['user_external_id'] = 'user_id_3'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'some events are full: Event 2'
|
|
|
|
params['slots'] = 'event'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 1
|
|
assert resp.json['waiting_list_events'][0]['slug'] == event.slug
|
|
assert Booking.objects.filter(in_waiting_list=True, event=event).count() == 1
|
|
|
|
# change bookings
|
|
params = {'user_external_id': 'user_id', 'slots': 'event-2,event-3'}
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 1
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
|
|
user_bookings = Booking.objects.filter(user_external_id='user_id')
|
|
assert {b.event.slug for b in user_bookings} == {'event-2', 'event-3'}
|
|
assert event.booking_set.count() == 2
|
|
assert second_event.booking_set.count() == 2
|
|
assert third_event.booking_set.count() == 1
|
|
|
|
# increase waiting_list_places to make "Event" bookable again
|
|
event.waiting_list_places = 2
|
|
event.save()
|
|
|
|
# specify time range so that "Event 3" is not cancelled
|
|
params['slots'] = 'event,event-2'
|
|
resp = app.post_json(fillslots_url + '?date_start=2021-09-06&date_end=2021-09-09', params=params)
|
|
assert resp.json['booking_count'] == 1
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
|
|
user_bookings = Booking.objects.filter(user_external_id='user_id')
|
|
assert {b.event.slug for b in user_bookings} == {'event', 'event-2', 'event-3'}
|
|
assert event.booking_set.count() == 3
|
|
assert second_event.booking_set.count() == 2
|
|
assert third_event.booking_set.count() == 1
|
|
|
|
# new event booking went in waiting list despite free slots on main list
|
|
assert Booking.objects.filter(in_waiting_list=True, event=event).count() == 2
|
|
|
|
# passing empty slots cancels all bookings
|
|
params['slots'] = ''
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['cancelled_booking_count'] == 3
|
|
assert Booking.objects.filter(user_external_id='user_id').count() == 0
|
|
|
|
resp = app.post('/api/agenda/foobar/events/fillslots/', status=404)
|
|
resp = app.post('/api/agenda/0/events/fillslots/', status=404)
|
|
|
|
|
|
def test_api_events_fillslots_check_delays(app, user):
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
|
|
)
|
|
Event.objects.create(
|
|
slug='event-slug',
|
|
start_datetime=localtime() + datetime.timedelta(days=5),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug
|
|
resp = app.post(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug'})
|
|
assert resp.json['err'] == 0
|
|
|
|
# test minimal_booking_delay
|
|
agenda.minimal_booking_delay = 6
|
|
agenda.save()
|
|
resp = app.post(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post(
|
|
fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug', 'bypass_delays': True}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
# test maximal_booking_delay
|
|
agenda.minimal_booking_delay = 0
|
|
agenda.maximal_booking_delay = 3
|
|
agenda.save()
|
|
resp = app.post(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug'})
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post(
|
|
fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug', 'bypass_delays': True}
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_past_event(app, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
|
)
|
|
event1 = Event.objects.create(
|
|
label='Today before now',
|
|
start_datetime=localtime(now() - datetime.timedelta(hours=1)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
event2 = Event.objects.create(
|
|
label='Today after now',
|
|
start_datetime=localtime(now() + datetime.timedelta(hours=1)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
params = {'user_external_id': 'user_id', 'slots': ','.join((event1.slug, event2.slug))}
|
|
resp = app.post_json('/api/agenda/%s/events/fillslots/' % agenda.slug, params=params)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event1.slug
|
|
|
|
params['events'] = 'future'
|
|
resp = app.post_json('/api/agenda/%s/events/fillslots/' % agenda.slug, params=params)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event1.slug
|
|
|
|
params['events'] = 'past'
|
|
resp = app.post_json('/api/agenda/%s/events/fillslots/' % agenda.slug, params=params)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'event %s is not bookable' % event2.slug
|
|
|
|
params['events'] = 'all'
|
|
resp = app.post_json('/api/agenda/%s/events/fillslots/' % agenda.slug, params=params)
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_multiple_agendas(app, user):
|
|
first_agenda = Agenda.objects.create(label='First agenda', kind='events')
|
|
Desk.objects.create(agenda=first_agenda, slug='_exceptions_holder')
|
|
first_event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
places=2,
|
|
agenda=first_agenda,
|
|
)
|
|
second_agenda = Agenda.objects.create(label='Second agenda', kind='events')
|
|
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
|
|
second_event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=6),
|
|
places=2,
|
|
agenda=second_agenda,
|
|
)
|
|
|
|
agenda_slugs = '%s,%s' % (first_agenda.slug, second_agenda.slug)
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs})
|
|
event_slugs = ','.join((resp.json['data'][0]['id'], resp.json['data'][1]['id']))
|
|
assert event_slugs == 'first-agenda@event,second-agenda@event'
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
params = {'user_external_id': 'user_id', 'slots': event_slugs}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert first_event.booking_set.count() == 1
|
|
assert second_event.booking_set.count() == 1
|
|
|
|
# booking modification
|
|
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
assert first_event.booking_set.count() == 1
|
|
assert second_event.booking_set.count() == 0
|
|
|
|
params = {'user_external_id': 'user_id_2', 'slots': event_slugs}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert first_event.booking_set.count() == 2
|
|
assert second_event.booking_set.count() == 1
|
|
|
|
params = {'user_external_id': 'user_id_3', 'slots': event_slugs}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'some events are full: Event'
|
|
|
|
# invalid agenda slugs in querystring
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?agendas=first-agenda,xxx,yyy', params=params, status=400
|
|
)
|
|
assert resp.json['errors']['agendas'][0] == 'invalid slugs: xxx, yyy'
|
|
|
|
# invalid agenda slugs in payload
|
|
params = {'user_external_id': 'user_id_3', 'slots': 'first-agenda@event,xxx@event,yyy@event'}
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params, status=400
|
|
)
|
|
assert resp.json['errors']['slots'] == [
|
|
'Some events belong to agendas that are not present in querystring: xxx, yyy'
|
|
]
|
|
|
|
# missing agendas parameter
|
|
resp = app.post_json('/api/agendas/events/fillslots/', params=params, status=400)
|
|
assert resp.json['errors']['agendas'] == ['This field is required.']
|
|
|
|
# valid agendas parameter and event slugs, but mismatch between the two
|
|
params = {'user_external_id': 'user_id_3', 'slots': event_slugs}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == [
|
|
'Some events belong to agendas that are not present in querystring: second-agenda'
|
|
]
|
|
|
|
# missing @ in slot
|
|
params['slots'] = 'first-agenda'
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == ['Invalid format for slot first-agenda']
|
|
|
|
# empty event slug
|
|
params['slots'] = 'first-agenda@'
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == ['Missing event slug in slot first-agenda@']
|
|
|
|
# empty agenda slug
|
|
params['slots'] = '@event'
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == ['Missing agenda slug in slot @event']
|
|
|
|
|
|
def test_api_events_fillslots_multiple_agendas_check_delays(app, user):
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
|
|
)
|
|
Event.objects.create(
|
|
slug='event-slug',
|
|
start_datetime=localtime() + datetime.timedelta(days=5),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
# test minimal_booking_delay
|
|
agenda.minimal_booking_delay = 6
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
# test maximal_booking_delay
|
|
agenda.minimal_booking_delay = 0
|
|
agenda.maximal_booking_delay = 3
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
|
|
|
|
def test_url_translation(app, some_data, user):
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
agenda_id = Agenda.objects.filter(label='Foo bar')[0].id
|
|
assert Booking.objects.count() == 0
|
|
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
|
|
fillslot_url = resp.json['data'][0]['api']['fillslot_url']
|
|
|
|
params = {
|
|
'backoffice_url': 'https://demarches.example.com/backoffice/foo/bar',
|
|
'cancel_callback_url': 'https://demarches.example.com/foo/bar/jump',
|
|
'form_url': 'https://demarches.example.com/foo/bar',
|
|
}
|
|
|
|
# https://demarches.example.com not in KNOWN_SERVICES, no URL translation
|
|
resp = app.post_json(fillslot_url, params=params)
|
|
booking = Booking.objects.get(pk=resp.json['booking_id'])
|
|
assert booking.backoffice_url == 'https://demarches.example.com/backoffice/foo/bar'
|
|
assert booking.cancel_callback_url == 'https://demarches.example.com/foo/bar/jump'
|
|
assert booking.form_url == 'https://demarches.example.com/foo/bar'
|
|
|
|
# http://example.org/ is in KNOWN_SERVICES, translation happens
|
|
params = {
|
|
'backoffice_url': 'http://example.org/backoffice/foo/bar',
|
|
'cancel_callback_url': 'http://example.org/foo/bar/jump',
|
|
'form_url': 'http://example.org/foo/bar',
|
|
}
|
|
|
|
resp = app.post_json(fillslot_url, params=params)
|
|
booking = Booking.objects.get(pk=resp.json['booking_id'])
|
|
assert booking.backoffice_url == 'publik://default/backoffice/foo/bar'
|
|
assert booking.cancel_callback_url == 'publik://default/foo/bar/jump'
|
|
assert booking.form_url == 'publik://default/foo/bar'
|
|
|
|
|
|
def test_fillslot_recurring_event_booking_forbidden(app, user):
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=7),
|
|
recurrence_days=[now().weekday()],
|
|
places=2,
|
|
agenda=agenda,
|
|
)
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event is recurrent'
|