misc: split api tests (#54461)
This commit is contained in:
parent
d51cd7c14a
commit
31bc67fcde
|
@ -0,0 +1,100 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils.timezone import localtime, make_aware, now
|
||||
|
||||
from chrono.agendas.models import Agenda, Desk, Event, MeetingType, TimePeriod, VirtualMember
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user():
|
||||
User = get_user_model()
|
||||
user = User.objects.create(
|
||||
username='john.doe', first_name=u'John', last_name=u'Doe', email='john.doe@example.net'
|
||||
)
|
||||
user.set_password('password')
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture(params=['Europe/Brussels', 'Asia/Kolkata', 'Brazil/East'])
|
||||
def time_zone(request, settings):
|
||||
settings.TIME_ZONE = request.param
|
||||
|
||||
|
||||
# 2017-05-20 -> saturday
|
||||
@pytest.fixture(
|
||||
params=[
|
||||
datetime.datetime(year=2017, month=5, day=20, hour=1, minute=12),
|
||||
datetime.datetime(year=2017, month=5, day=20, hour=11, minute=42),
|
||||
datetime.datetime(year=2017, month=5, day=20, hour=23, minute=17),
|
||||
]
|
||||
)
|
||||
def mock_now(request, freezer, time_zone):
|
||||
aware_datetime = make_aware(request.param)
|
||||
freezer.move_to(aware_datetime)
|
||||
return aware_datetime
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def some_data(mock_now):
|
||||
agenda = Agenda(label=u'Foo bar')
|
||||
agenda.save()
|
||||
first_date = localtime(now()).replace(hour=17, minute=0, second=0, microsecond=0)
|
||||
first_date += datetime.timedelta(days=1)
|
||||
for i in range(3):
|
||||
event = Event(start_datetime=first_date + datetime.timedelta(days=i), places=20, agenda=agenda)
|
||||
event.save()
|
||||
|
||||
agenda2 = Agenda(label=u'Foo bar 2')
|
||||
agenda2.save()
|
||||
first_date = localtime(now()).replace(hour=20, minute=0, second=0, microsecond=0)
|
||||
first_date += datetime.timedelta(days=1)
|
||||
for i in range(2):
|
||||
event = Event(start_datetime=first_date + datetime.timedelta(days=i), places=20, agenda=agenda2)
|
||||
event.save()
|
||||
|
||||
# a date in the past
|
||||
event = Event(start_datetime=first_date - datetime.timedelta(days=10), places=10, agenda=agenda)
|
||||
event.save()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def meetings_agenda(mock_now):
|
||||
agenda = Agenda(
|
||||
label=u'Foo bar Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=56
|
||||
)
|
||||
agenda.save()
|
||||
meeting_type = MeetingType(agenda=agenda, label='Blah', duration=30)
|
||||
meeting_type.save()
|
||||
|
||||
test_1st_weekday = (localtime(now()).weekday() + 2) % 7
|
||||
test_2nd_weekday = (localtime(now()).weekday() + 3) % 7
|
||||
|
||||
default_desk, created = Desk.objects.get_or_create(agenda=agenda, label='Desk 1')
|
||||
|
||||
time_period = TimePeriod(
|
||||
weekday=test_1st_weekday,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=default_desk,
|
||||
)
|
||||
time_period.save()
|
||||
time_period = TimePeriod(
|
||||
weekday=test_2nd_weekday,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(17, 0),
|
||||
desk=default_desk,
|
||||
)
|
||||
time_period.save()
|
||||
return agenda
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def virtual_meetings_agenda(meetings_agenda):
|
||||
agenda = Agenda.objects.create(label='Virtual Agenda', kind='virtual')
|
||||
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meetings_agenda)
|
||||
return agenda
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,629 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import urllib.parse as urlparse
|
||||
|
||||
import pytest
|
||||
from django.test import override_settings
|
||||
from django.utils.timezone import localtime, make_aware, now
|
||||
|
||||
from chrono.agendas.models import Agenda, Booking, Event, TimePeriodException
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_datetimes_api(app, some_data):
|
||||
agenda = Agenda.objects.filter(label=u'Foo bar')[0]
|
||||
|
||||
def check_bookability(data):
|
||||
for event in data:
|
||||
assert Event.objects.get(slug=event['id']).in_bookable_period()
|
||||
for event in agenda.event_set.all():
|
||||
if not event.in_bookable_period():
|
||||
assert event.slug not in [x['id'] for x in data]
|
||||
|
||||
resp = app.get('/api/agenda/xxx/datetimes/', status=404)
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert 'data' in resp.json
|
||||
assert len(resp.json['data']) == 3
|
||||
check_bookability(resp.json['data'])
|
||||
assert app.get('/api/agenda/%s/datetimes/' % agenda.id).json == resp.json
|
||||
|
||||
agenda.minimal_booking_delay = 5
|
||||
agenda.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 0
|
||||
check_bookability(resp.json['data'])
|
||||
|
||||
agenda.minimal_booking_delay = 2
|
||||
agenda.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 2
|
||||
check_bookability(resp.json['data'])
|
||||
|
||||
agenda.minimal_booking_delay = 0
|
||||
agenda.maximal_booking_delay = 3
|
||||
agenda.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 2
|
||||
check_bookability(resp.json['data'])
|
||||
assert resp.json['data'][0]['description'] is None
|
||||
|
||||
agenda.event_set.update(publication_date=localtime(now()).date() + datetime.timedelta(days=1))
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 0
|
||||
check_bookability(resp.json['data'])
|
||||
|
||||
agenda.event_set.update(publication_date=localtime(now()).date())
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 2
|
||||
check_bookability(resp.json['data'])
|
||||
|
||||
# add description, URL and pricing to events
|
||||
for i, event in enumerate(agenda.event_set.all()):
|
||||
event.description = 'Description %s' % i
|
||||
event.url = 'https://www.example.net/%s' % i
|
||||
event.pricing = '%s €' % i
|
||||
event.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['description']
|
||||
|
||||
|
||||
def test_datetimes_api_wrong_kind(app):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
|
||||
app.get('/api/agenda/%s/datetimes/' % agenda.slug, status=404)
|
||||
|
||||
|
||||
def test_datetime_api_fr(app):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
|
||||
Event.objects.create(
|
||||
slug='event-slug',
|
||||
start_datetime=localtime((now() + datetime.timedelta(days=5))).replace(hour=17, minute=0),
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
with override_settings(LANGUAGE_CODE='fr-fr'):
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
# no seconds, hh:mm in 24-hour formats
|
||||
assert resp.json['data'][0]['text'].endswith(' 17:00')
|
||||
assert resp.json['data'][0]['datetime'].endswith(' 17:00:00')
|
||||
assert 'data' in resp.json
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2021-05-06 14:00')
|
||||
def test_datetime_api_label(app):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
|
||||
event = Event.objects.create(
|
||||
label='Hello world',
|
||||
slug='event-slug',
|
||||
start_datetime=(now() + datetime.timedelta(days=1)),
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert 'Hello world' == resp.json['data'][0]['text']
|
||||
|
||||
agenda.event_display_template = '{{ event.label }} - {{ event.start_datetime }}'
|
||||
agenda.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['text'] == 'Hello world - May 7, 2021, 4 p.m.'
|
||||
|
||||
Booking.objects.create(event=event)
|
||||
agenda.event_display_template = (
|
||||
'{{ event.label }} ({{ event.remaining_places }}/{{ event.places }} places remaining)'
|
||||
)
|
||||
agenda.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['text'] == 'Hello world (4/5 places remaining)'
|
||||
|
||||
|
||||
def test_datetime_api_urls(app):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
|
||||
event = Event.objects.create(
|
||||
slug='event-slug',
|
||||
start_datetime=(now() + datetime.timedelta(days=5)).replace(hour=10, minute=0),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
for datum in resp.json['data']:
|
||||
assert urlparse.urlparse(datum['api']['bookings_url']).path == '/api/agenda/%s/bookings/%s/' % (
|
||||
agenda.slug,
|
||||
event.slug,
|
||||
)
|
||||
assert urlparse.urlparse(datum['api']['fillslot_url']).path == '/api/agenda/%s/fillslot/%s/' % (
|
||||
agenda.slug,
|
||||
event.slug,
|
||||
)
|
||||
assert urlparse.urlparse(datum['api']['status_url']).path == '/api/agenda/%s/status/%s/' % (
|
||||
agenda.slug,
|
||||
event.slug,
|
||||
)
|
||||
|
||||
|
||||
def test_datetime_api_min_places(app):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(start_datetime=now() + datetime.timedelta(days=7), places=5, agenda=agenda)
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert not resp.json['data'][0]['disabled']
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=5' % agenda.slug)
|
||||
assert not resp.json['data'][0]['disabled']
|
||||
|
||||
Booking.objects.create(event=event)
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=5' % agenda.slug)
|
||||
assert resp.json['data'][0]['disabled']
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=wrong' % agenda.slug, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/?min_places=' % agenda.slug, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2021-02-23')
|
||||
def test_datetimes_api_exclude_slots(app):
|
||||
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')
|
||||
cancelled = Booking.objects.create(event=event, user_external_id='35')
|
||||
cancelled.cancel()
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '35'})
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
assert resp.json['meta']['first_bookable_slot'] is None
|
||||
assert resp.json['meta']['no_bookable_datetimes'] is True
|
||||
|
||||
event.delete()
|
||||
|
||||
# recurrent event
|
||||
event = Event.objects.create(
|
||||
slug='recurrent',
|
||||
start_datetime=localtime().replace(hour=12, minute=0),
|
||||
repeat='weekly',
|
||||
places=2,
|
||||
agenda=agenda,
|
||||
)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['id'] == 'recurrent:2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['id'] == 'recurrent:2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
|
||||
first_recurrence = event.get_or_create_event_recurrence(event.start_datetime)
|
||||
Booking.objects.create(event=first_recurrence, user_external_id='42')
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is False
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is False
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
|
||||
Booking.objects.create(event=first_recurrence)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is True
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'exclude_user_external_id': '42'})
|
||||
assert resp.json['data'][0]['id'] == 'recurrent--2021-02-23-1200'
|
||||
assert resp.json['data'][0]['places']['full'] is True
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
|
||||
|
||||
def test_datetimes_api_hide_disabled(app):
|
||||
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=now() + datetime.timedelta(days=3),
|
||||
places=1,
|
||||
agenda=agenda,
|
||||
)
|
||||
Booking.objects.create(event=event)
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['id'] == 'event-slug'
|
||||
assert resp.json['data'][0]['disabled'] is True
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params={'hide_disabled': True})
|
||||
assert resp.json['data'] == []
|
||||
|
||||
|
||||
def test_waiting_list_datetimes(app, some_data, user):
|
||||
agenda_id = Agenda.objects.filter(label=u'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()
|
||||
|
||||
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 i in range(event.places):
|
||||
Booking(event=event).save()
|
||||
|
||||
# all places are booked but all the dates are still displayed as there is a
|
||||
# waiting list.
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
|
||||
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == 3
|
||||
|
||||
# fill the waiting list
|
||||
for i in range(event.waiting_list_places):
|
||||
Booking(event=event, in_waiting_list=True).save()
|
||||
|
||||
# the event datetime should no longer be returned
|
||||
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')]
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2017-05-20')
|
||||
def test_agenda_api_date_range(app):
|
||||
# test range limitation
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
|
||||
first_datetime = localtime(now()).replace(hour=20, minute=0, second=0, microsecond=0)
|
||||
first_datetime += datetime.timedelta(days=1)
|
||||
for i in range(2):
|
||||
event = Event.objects.create(
|
||||
start_datetime=first_datetime + datetime.timedelta(days=i), places=20, agenda=agenda
|
||||
)
|
||||
base_datetime = agenda.event_set.last().start_datetime
|
||||
base_datetime = base_datetime + datetime.timedelta(days=1)
|
||||
|
||||
for idx in range(7, 10):
|
||||
if idx == 7:
|
||||
day_events = ['9:00', '10:00', '11:00']
|
||||
elif idx == 8:
|
||||
day_events = ['13:00', '14:00']
|
||||
else:
|
||||
day_events = ['8:00']
|
||||
day = base_datetime.date() + datetime.timedelta(days=idx)
|
||||
for event in day_events:
|
||||
event_dt = datetime.datetime.combine(day, datetime.datetime.strptime(event, '%H:%M').time())
|
||||
Event.objects.create(agenda=agenda, start_datetime=make_aware(event_dt), places=2)
|
||||
|
||||
for value in ['foo', '2017-05-42']:
|
||||
params = {'date_start': value}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
|
||||
for value in ['foo', '2017-05-42']:
|
||||
params = {'date_end': value}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
|
||||
params = {'date_start': base_datetime.date().isoformat()}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 6
|
||||
date_endtime = base_datetime + datetime.timedelta(days=7)
|
||||
params = {'date_end': date_endtime.date().isoformat()}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-21 20:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-22 20:00:00'
|
||||
|
||||
params = {
|
||||
'date_start': base_datetime.date() + datetime.timedelta(days=8),
|
||||
'date_end': base_datetime.date() + datetime.timedelta(days=10),
|
||||
}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-31 13:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-06-01 08:00:00'
|
||||
|
||||
# with minimal booking delay changed
|
||||
agenda.minimal_booking_delay = 3
|
||||
agenda.save()
|
||||
params = {'date_start': '2017-05-21'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 6
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-06-01 08:00:00'
|
||||
|
||||
# with maximal booking delay changed
|
||||
agenda.maximal_booking_delay = 11
|
||||
agenda.save()
|
||||
params = {'date_end': '2017-06-01'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
# with time
|
||||
params = {'date_start': '2017-05-21 foo'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_start format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
|
||||
for start in ['2017-05-30 09:00', '2017-05-30 09:00:00']:
|
||||
params = {'date_start': start}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
for start in ['2017-05-30 09:01', '2017-05-30 09:00:01', '2017-05-30 09:00:01+02:00']:
|
||||
params = {'date_start': start}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 10:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
params = {'date_end': '2017-06-01 foo'}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
assert resp.json['err_desc'] == 'date_end format must be YYYY-MM-DD or YYYY-MM-DD HH:MM'
|
||||
|
||||
for end in ['2017-05-30 11:01', '2017-05-30 11:00:01']:
|
||||
params = {'date_end': end}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 11:00:00'
|
||||
|
||||
for end in ['2017-05-30 11:00', '2017-05-30 11:00:00', '2017-05-30 11:00:00+02:00']:
|
||||
params = {'date_end': end}
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug, params=params)
|
||||
assert len(resp.json['data']) == 2
|
||||
assert resp.json['data'][0]['datetime'] == '2017-05-30 09:00:00'
|
||||
assert resp.json['data'][-1]['datetime'] == '2017-05-30 10:00:00'
|
||||
|
||||
|
||||
def test_datetimes_api_meta(app, freezer):
|
||||
# 2017-05-20 -> saturday
|
||||
freezer.move_to(make_aware(datetime.datetime(year=2017, month=5, day=20, hour=1, minute=12)))
|
||||
|
||||
agenda = Agenda.objects.create(label=u'Foo bar')
|
||||
first_date = localtime(now()).replace(hour=17, minute=0, second=0, microsecond=0)
|
||||
first_date += datetime.timedelta(days=1)
|
||||
for i in range(3):
|
||||
event = Event(start_datetime=first_date + datetime.timedelta(days=i), places=20, agenda=agenda)
|
||||
event.save()
|
||||
# a date in the past
|
||||
event = Event(start_datetime=first_date - datetime.timedelta(days=10), places=10, agenda=agenda)
|
||||
event.save()
|
||||
|
||||
events = Event.objects.filter(agenda_id=agenda.id).exclude(start_datetime__lt=now())
|
||||
assert len(events) == 3
|
||||
api_url = '/api/agenda/%s/datetimes/' % agenda.slug
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 3,
|
||||
'first_bookable_slot': resp.json['data'][0],
|
||||
}
|
||||
|
||||
def simulate_booking(event, nb_places):
|
||||
for i in range(nb_places):
|
||||
Booking(event=event, user_external_id='42').save()
|
||||
|
||||
simulate_booking(events[0], 10)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 3,
|
||||
'first_bookable_slot': resp.json['data'][0],
|
||||
}
|
||||
resp = app.get(api_url, params={'exclude_user_external_id': '42'})
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 2,
|
||||
'first_bookable_slot': resp.json['data'][1],
|
||||
}
|
||||
|
||||
resp = app.get(api_url + '?min_places=11')
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 2,
|
||||
'first_bookable_slot': resp.json['data'][1],
|
||||
}
|
||||
|
||||
simulate_booking(events[0], 10)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': False,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 2,
|
||||
'first_bookable_slot': resp.json['data'][1],
|
||||
}
|
||||
|
||||
simulate_booking(events[1], 20)
|
||||
simulate_booking(events[2], 20)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['meta'] == {
|
||||
'no_bookable_datetimes': True,
|
||||
'bookable_datetimes_number_total': 3,
|
||||
'bookable_datetimes_number_available': 0,
|
||||
'first_bookable_slot': None,
|
||||
}
|
||||
|
||||
# recurring event
|
||||
Event.objects.all().delete()
|
||||
Event.objects.create(
|
||||
slug='abc', label='Test', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda
|
||||
)
|
||||
resp = app.get(api_url)
|
||||
assert resp.json['meta']['first_bookable_slot']['text'] == 'Test (May 27, 2017, 1:12 a.m.)'
|
||||
|
||||
|
||||
def test_recurring_events_api(app, user, freezer):
|
||||
freezer.move_to('2021-01-12 12:05') # Tuesday
|
||||
agenda = Agenda.objects.create(
|
||||
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
|
||||
)
|
||||
base_event = Event.objects.create(
|
||||
slug='abc', label='Test', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda
|
||||
)
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
data = resp.json['data']
|
||||
assert len(data) == 4
|
||||
assert data[0]['id'] == 'abc:2021-01-19-1305'
|
||||
assert data[0]['datetime'] == '2021-01-19 13:05:00'
|
||||
assert data[0]['text'] == 'Test (Jan. 19, 2021, 1:05 p.m.)'
|
||||
assert data[3]['id'] == 'abc:2021-02-09-1305'
|
||||
assert Event.objects.count() == 1
|
||||
|
||||
fillslot_url = data[0]['api']['fillslot_url']
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
# book first event
|
||||
resp = app.post(fillslot_url)
|
||||
assert resp.json['err'] == 0
|
||||
assert Event.objects.count() == 2
|
||||
event = Booking.objects.get(pk=resp.json['booking_id']).event
|
||||
assert event.slug == 'abc--2021-01-19-1305'
|
||||
|
||||
# first event is now a real event in datetimes
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
data = resp.json['data']
|
||||
assert len(data) == 4
|
||||
assert data[0]['id'] == event.slug
|
||||
new_fillslot_url = data[0]['api']['fillslot_url']
|
||||
|
||||
# booking again with both old and new urls works
|
||||
resp = app.post(fillslot_url)
|
||||
assert resp.json['err'] == 0
|
||||
resp = app.post(new_fillslot_url)
|
||||
assert resp.json['err'] == 0
|
||||
assert Event.objects.count() == 2
|
||||
assert event.booking_set.count() == 3
|
||||
|
||||
# status and bookings api also create a real event
|
||||
status_url = data[1]['api']['status_url']
|
||||
resp = app.get(status_url)
|
||||
assert resp.json['places']['total'] == 5
|
||||
assert Event.objects.count() == 3
|
||||
|
||||
bookings_url = data[2]['api']['bookings_url']
|
||||
resp = app.get(bookings_url, params={'user_external_id': '42'})
|
||||
assert resp.json['data'] == []
|
||||
assert Event.objects.count() == 4
|
||||
|
||||
# cancelled recurrences do not appear
|
||||
event.cancel()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['id'] == 'abc--2021-01-26-1305'
|
||||
|
||||
# publication date is accounted for
|
||||
Event.objects.filter(primary_event=base_event).delete()
|
||||
base_event.publication_date = now().replace(day=27)
|
||||
base_event.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 0
|
||||
|
||||
# events follow agenda display template
|
||||
Event.objects.all().update(publication_date=None)
|
||||
agenda.event_display_template = '{{ event.label }} - {{ event.start_datetime }}'
|
||||
agenda.save()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert resp.json['data'][0]['text'] == 'Test - Jan. 19, 2021, 1:05 p.m.'
|
||||
|
||||
|
||||
def test_recurring_events_api_various_times(app, user, mock_now):
|
||||
agenda = Agenda.objects.create(
|
||||
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
||||
)
|
||||
event = Event.objects.create(
|
||||
slug='abc', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda
|
||||
)
|
||||
event.refresh_from_db()
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 5
|
||||
fillslot_url = resp.json['data'][0]['api']['fillslot_url']
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
resp = app.post(fillslot_url)
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
new_event = Booking.objects.get(pk=resp.json['booking_id']).event
|
||||
assert event.start_datetime == new_event.start_datetime
|
||||
|
||||
|
||||
def test_datetimes_dst(app, freezer):
|
||||
agenda = Agenda.objects.create(
|
||||
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=10
|
||||
)
|
||||
event = Event.objects.create(
|
||||
slug='event-slug',
|
||||
start_datetime=make_aware(datetime.datetime(2021, 3, 28, 12, 30)),
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
|
||||
freezer.move_to('Fri, 18 Mar 2021 23:59:39 +0100')
|
||||
|
||||
# maximal_booking_delay is 10days so last bookable event is on
|
||||
# 2021-03-28T00:00, so event should not be bookable
|
||||
assert not event.in_bookable_period()
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 0
|
||||
|
||||
|
||||
def test_recurring_events_api_exceptions(app, user, freezer):
|
||||
freezer.move_to('2021-01-12 12:05') # Tuesday
|
||||
agenda = Agenda.objects.create(
|
||||
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
|
||||
)
|
||||
Event.objects.create(slug='abc', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda)
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
data = resp.json['data']
|
||||
assert len(data) == 4
|
||||
assert data[0]['datetime'] == '2021-01-19 13:05:00'
|
||||
|
||||
TimePeriodException.objects.create(
|
||||
desk=agenda.desk_set.get(),
|
||||
start_datetime=datetime.date(year=2021, month=1, day=18),
|
||||
end_datetime=datetime.date(year=2021, month=1, day=20),
|
||||
)
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
assert len(resp.json['data']) == 3
|
||||
assert resp.json['data'][0]['datetime'] == '2021-01-26 13:05:00'
|
||||
|
||||
# try to book excluded event
|
||||
fillslot_url = data[0]['api']['fillslot_url']
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
resp = app.post(fillslot_url, status=400)
|
||||
assert resp.json['err'] == 1
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import pytest
|
||||
from django.utils.timezone import now
|
||||
|
||||
from chrono.agendas.models import Agenda, Booking, Category, Event
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_statistics_list(app, user):
|
||||
Category.objects.create(label='Category A')
|
||||
Category.objects.create(label='Category B')
|
||||
|
||||
# unauthorized
|
||||
app.get('/api/statistics/', status=401)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
resp = app.get('/api/statistics/')
|
||||
category_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'category'][0]
|
||||
assert len(category_filter['options']) == 3
|
||||
|
||||
|
||||
def test_statistics_bookings(app, user, freezer):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(start_datetime=now(), places=5, agenda=agenda)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
resp = app.get('/api/statistics/')
|
||||
url = [x for x in resp.json['data'] if x['id'] == 'bookings_count'][0]['url']
|
||||
|
||||
resp = app.get(url)
|
||||
assert len(resp.json['data']['series']) == 0
|
||||
|
||||
freezer.move_to('2020-10-10')
|
||||
for _ in range(10):
|
||||
Booking.objects.create(event=event)
|
||||
freezer.move_to('2020-10-15')
|
||||
Booking.objects.create(event=event)
|
||||
|
||||
resp = app.get(url + '?time_interval=day')
|
||||
assert resp.json['data'] == {
|
||||
'x_labels': ['2020-10-10', '2020-10-15'],
|
||||
'series': [{'label': 'Bookings Count', 'data': [10, 1]}],
|
||||
}
|
||||
|
||||
# period filter
|
||||
resp = app.get(url + '?start=2020-10-14&end=2020-10-16')
|
||||
assert resp.json['data'] == {
|
||||
'x_labels': ['2020-10-15'],
|
||||
'series': [{'label': 'Bookings Count', 'data': [1]}],
|
||||
}
|
||||
|
||||
category = Category.objects.create(label='Category A', slug='category-a')
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events', category=category)
|
||||
event = Event.objects.create(start_datetime=now(), places=5, agenda=agenda)
|
||||
freezer.move_to('2020-10-25')
|
||||
Booking.objects.create(event=event)
|
||||
|
||||
# category filter
|
||||
resp = app.get(url + '?category=category-a')
|
||||
assert resp.json['data'] == {
|
||||
'x_labels': ['2020-10-25'],
|
||||
'series': [{'label': 'Bookings Count', 'data': [1]}],
|
||||
}
|
||||
|
||||
# invalid time_interval
|
||||
resp = app.get(url + '?time_interval=month', status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert 'time_interval' in resp.json['errors']
|
||||
|
||||
# absence/presence
|
||||
for i in range(10):
|
||||
Booking.objects.create(event=event, user_was_present=bool(i % 2))
|
||||
|
||||
freezer.move_to('2020-11-01')
|
||||
Booking.objects.create(event=event, user_was_present=True)
|
||||
|
||||
resp = app.get(url)
|
||||
assert resp.json['data'] == {
|
||||
'x_labels': ['2020-10-10', '2020-10-15', '2020-10-25', '2020-11-01'],
|
||||
'series': [
|
||||
{'label': 'Unknown', 'data': [10, 1, 1, None]},
|
||||
{'label': 'Present', 'data': [None, None, 5, 1]},
|
||||
{'label': 'Absent', 'data': [None, None, 5, None]},
|
||||
],
|
||||
}
|
Loading…
Reference in New Issue