chrono/tests/api/test_event.py

518 lines
19 KiB
Python

import datetime
import pytest
from django.utils.timezone import localtime, now
from chrono.agendas.models import Agenda, Booking, Event
pytestmark = pytest.mark.django_db
def test_status(app, user):
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=10,
agenda=agenda,
)
agenda2 = Agenda.objects.create(label='Foo bar2', kind='events', minimal_booking_delay=0)
# other event with the same slug but in another agenda
Event.objects.create(
slug='event-slug',
start_datetime=(now() + datetime.timedelta(days=5)).replace(hour=10, minute=0),
places=5,
agenda=agenda2,
)
Booking.objects.create(event=event)
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.slug), status=401)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
assert resp.json['places']['total'] == 10
assert resp.json['places']['available'] == 9
assert resp.json['places']['reserved'] == 1
assert resp.json == {
'err': 0,
'id': 'event-slug',
'slug': 'event-slug',
'text': str(event),
'label': '',
'date': localtime(event.start_datetime).strftime('%Y-%m-%d'),
'datetime': localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S'),
'description': None,
'pricing': None,
'url': None,
'disabled': False,
'duration': None,
'api': {
'bookings_url': 'http://testserver/api/agenda/foo-bar/bookings/event-slug/',
'fillslot_url': 'http://testserver/api/agenda/foo-bar/fillslot/event-slug/',
'status_url': 'http://testserver/api/agenda/foo-bar/status/event-slug/',
'check_url': 'http://testserver/api/agenda/foo-bar/check/event-slug/',
},
'places': {'available': 9, 'reserved': 1, 'total': 10, 'full': False, 'has_waiting_list': False},
}
assert 'waiting_list_total' not in resp.json['places']
Booking(event=event, in_waiting_list=True).save()
event.waiting_list_places = 5
event.save()
resp = app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.slug))
assert resp.json['places']['waiting_list_total'] == 5
assert resp.json['places']['waiting_list_available'] == 4
assert resp.json['places']['waiting_list_reserved'] == 1
# wrong kind
for kind in ['meetings', 'virtual']:
agenda.kind = kind
agenda.save()
app.get('/api/agenda/%s/status/%s/' % (agenda.pk, event.slug), status=404)
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.slug), status=404)
@pytest.mark.freeze_time('2021-02-23')
def test_status_url(app, user):
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=10,
agenda=agenda,
)
app.authorization = ('Basic', ('john.doe', 'password'))
app.get('/api/agenda/%s/status/%s/' % (agenda.pk, event.pk))
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.slug))
app.get('/api/agenda/%s/status/%s/' % (agenda.pk, event.slug))
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.pk))
# unknown event
app.get('/api/agenda/%s/status/%s/' % (agenda.pk, 0), status=404)
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, 0), status=404)
app.get('/api/agenda/%s/status/%s/' % (agenda.pk, 'foobar'), status=404)
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, 'foobar'), status=404)
# unknown agenda
app.get('/api/agenda/%s/status/%s/' % (0, event.pk), status=404)
app.get('/api/agenda/%s/status/%s/' % (0, event.slug), status=404)
app.get('/api/agenda/%s/status/%s/' % ('foobar', event.pk), status=404)
app.get('/api/agenda/%s/status/%s/' % ('foobar', event.slug), status=404)
# recurring 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=2,
agenda=agenda,
)
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, 'recurrent--2021-02-23-1200'), status=404)
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, 'recurrent:2021-02-23-1200'), status=200)
app.get('/api/agenda/%s/status/%s/' % (agenda.slug, 'recurrent--2021-02-23-1200'), status=200)
@pytest.mark.freeze_time('2021-02-23')
def test_event_checked(app, user):
agenda = Agenda.objects.create(label='Events', kind='events')
event = Event.objects.create(
label='xyz',
start_datetime=now() - datetime.timedelta(days=1),
places=10,
agenda=agenda,
)
assert event.checked is False
app.authorization = ('Basic', ('john.doe', 'password'))
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug))
event.refresh_from_db()
assert event.checked is True
# already checked
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug))
event.refresh_from_db()
assert event.checked is True
# recurring 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=2,
agenda=agenda,
)
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, 'recurrent--2021-02-23-1200'), status=404)
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, 'recurrent:2021-02-23-1200'), status=200)
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, 'recurrent--2021-02-23-1200'), status=200)
# wrong kind
agenda.kind = 'meetings'
agenda.save()
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug), status=404)
agenda.kind = 'virtual'
agenda.save()
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug), status=404)
def test_add_event(app, user):
api_url = '/api/agenda/%s/event/' % ('999')
# no authentication
resp = app.post(api_url, status=401)
assert resp.json['detail'] == 'Authentication credentials were not provided.'
# wrong password
app.authorization = ('Basic', ('john.doe', 'wrong'))
resp = app.post(api_url, status=401)
assert resp.json['detail'] == 'Invalid username/password.'
app.authorization = ('Basic', ('john.doe', 'password'))
# missing agenda
resp = app.post(api_url, status=404)
assert resp.json['detail'] == 'Not found.'
# using meeting agenda
meeting_agenda = Agenda(label='Foo bar Meeting', kind='meetings')
meeting_agenda.save()
api_url = '/api/agenda/%s/event/' % (meeting_agenda.slug)
resp = app.post(api_url, status=404)
assert resp.json['detail'] == 'Not found.'
agenda = Agenda(label='Foo bar')
agenda.maximal_booking_delay = 0
agenda.save()
api_url = '/api/agenda/%s/event/' % (agenda.slug)
# missing fields
resp = app.post(api_url, status=400)
assert resp.json['err']
assert resp.json['errors'] == {
'start_datetime': ['This field is required.'],
'places': ['This field is required.'],
}
# add with errors in datetime parts
params = {
'start_datetime': '2021-11-15 minuit',
'places': 10,
}
resp = app.post(api_url, params=params, status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'invalid payload'
assert 'Datetime has wrong format' in resp.json['errors']['start_datetime'][0]
# add an event
params = {
'start_datetime': '2021-11-15 15:38',
'places': 10,
}
resp = app.post(api_url, params=params)
assert not resp.json['err']
assert resp.json['data']['id'] == 'foo-bar-event'
assert {'api', 'disabled', 'places'}.issubset(resp.json['data'].keys())
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.isdisjoint(
resp.json['data'].keys()
)
event = Event.objects.filter(agenda=agenda).get(slug='foo-bar-event')
assert str(event.start_datetime) == '2021-11-15 14:38:00+00:00'
assert str(event.start_datetime.tzinfo) == 'UTC'
assert event.places == 10
assert event.publication_datetime is None
# add with almost all optional managed fields
params = {
'start_datetime': '2021-11-15 15:38',
'duration': 42,
'publication_datetime': '2021-09-20 10:00',
'places': 11,
'waiting_list_places': 3,
'label': 'FOO camp',
'description': 'An event',
'pricing': 'free',
'url': 'http://example.org/foo/bar/?',
}
resp = app.post(api_url, params=params)
assert not resp.json['err']
assert resp.json['data']['id'] == 'foo-camp'
assert {'api', 'disabled', 'places'}.issubset(resp.json['data'].keys())
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.isdisjoint(
resp.json['data'].keys()
)
event = Event.objects.filter(agenda=agenda).get(slug='foo-camp')
assert event.duration == 42
assert event.waiting_list_places == 3
assert event.label == 'FOO camp'
assert event.description == 'An event'
assert event.pricing == 'free'
assert event.url == 'http://example.org/foo/bar/?'
assert str(event.publication_datetime) == '2021-09-20 08:00:00+00:00'
assert str(event.publication_datetime.tzinfo) == 'UTC'
# add with errors in recurrence_days list
params = {
'start_datetime': '2021-11-15 15:38',
'places': 10,
'recurrence_days': 'oups',
}
resp = app.post(api_url, params=params, status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['recurrence_days']['0'][0] == 'A valid integer is required.'
params = {
'start_datetime': '2021-11-15 15:38',
'places': 10,
'recurrence_days': '7',
}
resp = app.post(api_url, params=params, status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['recurrence_days']['0'][0] == 'Ensure this value is less than or equal to 6.'
# add a recurrent event
params = {
'start_datetime': '2021-11-15 15:38',
'places': 12,
'recurrence_days': '0,3,5',
'recurrence_week_interval': '2',
'description': 'A recurrent event',
}
assert Event.objects.filter(agenda=agenda).count() == 2
resp = app.post(api_url, params=params)
assert Event.objects.filter(agenda=agenda).count() == 3
assert not resp.json['err']
assert resp.json['data']['id'] == 'foo-bar-event-1'
assert {'api', 'disabled', 'places'}.isdisjoint(resp.json['data'].keys())
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.issubset(
resp.json['data'].keys()
)
event = Event.objects.filter(agenda=agenda).get(slug='foo-bar-event-1')
assert event.description == 'A recurrent event'
assert event.recurrence_days == [0, 3, 5]
assert event.recurrence_week_interval == 2
assert event.recurrence_end_date is None
# add a recurrent event with end recurrence date creates 9 recurrences
params = {
'start_datetime': '2021-11-15 15:38',
'places': 13,
'recurrence_days': '0,3,5', # Monday, Tuesday, Saturday
'recurrence_week_interval': '2',
'recurrence_end_date': '2021-12-27',
'description': 'A recurrent event having recurrences',
}
resp = app.post(api_url, params=params)
assert not resp.json['err']
assert resp.json['data']['id'] == 'foo-bar-event-2'
assert {'api', 'disabled', 'places'}.isdisjoint(resp.json['data'].keys())
assert {'recurrence_days', 'recurrence_week_interval', 'recurrence_end_date'}.issubset(
resp.json['data'].keys()
)
event = Event.objects.filter(agenda=agenda).get(slug='foo-bar-event-2')
assert Event.objects.filter(agenda=agenda).count() == 13
assert event.description == 'A recurrent event having recurrences'
assert event.recurrence_days == [0, 3, 5]
assert event.recurrence_week_interval == 2
assert event.recurrence_end_date == datetime.date(2021, 12, 27)
assert sorted(
str(x.start_datetime.date()) for x in Event.objects.all() if 'foo-bar-event-2--' in x.slug
) == [
'2021-11-15',
'2021-11-18',
'2021-11-20',
'2021-11-29',
'2021-12-02',
'2021-12-04',
'2021-12-13',
'2021-12-16',
'2021-12-18',
]
def test_update_event(app, user):
api_url = '/api/agenda/%s/event/%s/' % ('nop', 'nop')
# no authentication
resp = app.patch(api_url, status=401)
assert resp.json['detail'] == 'Authentication credentials were not provided.'
# wrong password
app.authorization = ('Basic', ('john.doe', 'wrong'))
resp = app.patch(api_url, status=401)
assert resp.json['detail'] == 'Invalid username/password.'
app.authorization = ('Basic', ('john.doe', 'password'))
# missing agenda
resp = app.patch(api_url, status=404)
assert resp.json['detail'] == 'Not found.'
meeting_agenda = Agenda.objects.create(label='Foo bar Meeting', kind='meetings')
# using meeting agenda
api_url = '/api/agenda/%s/event/%s/' % (meeting_agenda.slug, 'nop')
resp = app.patch(api_url, status=404)
assert resp.json['detail'] == 'Not found.'
agenda = Agenda.objects.create(label='Foo bar')
# missing event
api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, 'nop')
resp = app.patch(api_url, status=404)
assert resp.json['detail'] == 'Not found.'
# missing recurring event
api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, 'nop:2021-11-15-1538')
resp = app.patch(api_url, status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'unknown recurring event slug: nop'
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, waiting_list_places=5)
api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, event.slug)
# update with errors in datetime parts
params = {
'start_datetime': '2021-11-15 minuit',
'recurrence_days': '7, 8',
}
resp = app.patch(api_url, params=params, status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'invalid payload'
assert 'Datetime has wrong format' in resp.json['errors']['start_datetime'][0]
assert resp.json['errors']['recurrence_days']['0'][0] == 'Ensure this value is less than or equal to 6.'
# update with almost all optional managed fields
params = {
'start_datetime': '2021-11-15 15:38',
'duration': 42,
'publication_datetime': '2021-09-20 12:00',
'places': 8,
'waiting_list_places': 3,
'label': 'FOO camp',
'description': 'An event',
'pricing': 'free',
'url': 'http://example.org/foo/bar/?',
}
resp = app.patch(api_url, params=params)
assert not resp.json['err']
event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
assert event.duration == 42
assert event.places == 8
assert event.waiting_list_places == 3
assert event.label == 'FOO camp'
assert event.description == 'An event'
assert event.pricing == 'free'
assert event.url == 'http://example.org/foo/bar/?'
assert str(event.publication_datetime) == '2021-09-20 10:00:00+00:00'
assert str(event.publication_datetime.tzinfo) == 'UTC'
# update event as a recurring event
params = {
'recurrence_days': '0,3,5',
'recurrence_week_interval': 2,
'recurrence_end_date': '2021-12-27',
}
resp = app.patch(api_url, params=params)
assert not resp.json['err']
event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
assert event.recurrences.count() == 9
assert [x.places for x in event.recurrences.all()] == [8] * 9
recurrence_slug = event.recurrences.first().slug
assert recurrence_slug == 'foo-bar-event--2021-11-15-1538'
recurrence_url = '/api/agenda/%s/event/%s/' % (agenda.slug, 'foo-bar-event:2021-11-15-1538')
# update unprotected fields of one of the event recurrencies
params = {
'start_datetime': '2021-11-14 14:00',
'duration': 43,
'places': 9,
'waiting_list_places': 4,
'label': 'BAR camp',
'description': 'An occurence of an event recurrence',
'pricing': '5€',
'url': 'http://example.org/bar/bar/',
}
resp = app.patch(recurrence_url, params=params)
assert not resp.json['err']
recurrence = Event.objects.filter(agenda=agenda).get(slug=recurrence_slug)
assert recurrence.duration == 43
assert recurrence.places == 9
assert recurrence.waiting_list_places == 4
assert recurrence.label == 'BAR camp'
assert recurrence.description == 'An occurence of an event recurrence'
assert recurrence.pricing == '5€'
assert recurrence.url == 'http://example.org/bar/bar/'
# try to update protected fields of one of the event recurrencies
params = {
'publication_datetime': '2021-11-15 12:00',
}
resp = app.patch(recurrence_url, params=params, status=400)
assert resp.json['err']
assert 'cannot be modified' in resp.json['err_desc']
# update protected fields of one of the event recurrencies providing same value
assert 'cannot be modified' in resp.json['err_desc']
params = {
'recurrence_week_interval': 1,
}
resp = app.patch(recurrence_url, params=params)
assert not resp.json['err']
booking = Booking.objects.create(event=event.recurrences.all()[2])
# update unprotected fields
params = {
'places': 7,
}
resp = app.patch(api_url, params=params)
assert not resp.json['err']
event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
assert [x.places for x in event.recurrences.all()] == [7] * 9
# try to update recurring event protected fields
params = {
'recurrence_days': '1,2',
}
resp = app.patch(api_url, params=params, status=400)
assert resp.json['err']
assert 'cannot be modified' in resp.json['err_desc']
assert booking.event.start_datetime.date() == datetime.date(2021, 11, 20)
# try to reduce recurrence end date before booked event
params = {
'recurrence_end_date': '2021-11-20',
}
resp = app.patch(api_url, params=params, status=400)
assert resp.json['err']
assert 'bookings exist after this date.' in resp.json['err_desc']
# reduce recurrence end date after booked event
params = {
'recurrence_end_date': '2021-11-21',
}
resp = app.patch(api_url, params=params)
assert not resp.json['err']
event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
assert event.recurrences.count() == 3
booking.cancel()
# update no more protected fields
params = {
'recurrence_days': '1,2,3,4,5',
'recurrence_week_interval': 1,
}
resp = app.patch(api_url, params=params)
assert not resp.json['err']
event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
assert event.recurrences.count() == 5