chrono/tests/api/test_event.py

2220 lines
74 KiB
Python

import datetime
from unittest import mock
import pytest
from django.db import connection
from django.test.utils import CaptureQueriesContext
from chrono.agendas.models import Agenda, Booking, Event, EventsType, Subscription
from chrono.utils.timezone import localtime, make_aware, now
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,
custom_fields={
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
},
)
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 == {
'err': 0,
'id': 'event-slug',
'slug': 'event-slug',
'text': str(event),
'label': '',
'agenda_label': 'Foo bar',
'date': localtime(event.start_datetime).strftime('%Y-%m-%d'),
'datetime': localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S'),
'end_datetime': '',
'description': None,
'pricing': None,
'url': None,
'disabled': False,
'duration': None,
'checked': False,
'check_locked': False,
'invoiced': False,
'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/',
'backoffice_url': 'http://testserver/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk),
},
'places': {'available': 9, 'reserved': 1, 'total': 10, 'full': False, 'has_waiting_list': False},
}
assert 'waiting_list_total' not in resp.json['places']
events_type = EventsType.objects.create(
label='Foo',
custom_fields=[
{'varname': 'text', 'label': 'Text', 'field_type': 'text'},
{'varname': 'textarea', 'label': 'TextArea', 'field_type': 'textarea'},
{'varname': 'bool', 'label': 'Bool', 'field_type': 'bool'},
],
)
agenda.events_type = events_type
agenda.save()
resp = app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.slug))
assert resp.json == {
'err': 0,
'id': 'event-slug',
'slug': 'event-slug',
'text': str(event),
'label': '',
'agenda_label': 'Foo bar',
'date': localtime(event.start_datetime).strftime('%Y-%m-%d'),
'datetime': localtime(event.start_datetime).strftime('%Y-%m-%d %H:%M:%S'),
'end_datetime': '',
'description': None,
'pricing': None,
'url': None,
'disabled': False,
'duration': None,
'checked': False,
'check_locked': False,
'invoiced': False,
'custom_field_text': 'foo',
'custom_field_textarea': 'foo bar',
'custom_field_bool': True,
'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/',
'backoffice_url': 'http://testserver/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk),
},
'places': {'available': 9, 'reserved': 1, 'total': 10, 'full': False, 'has_waiting_list': False},
}
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)
@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
resp = app.get('/api/agenda/%s/status/%s/' % (agenda.slug, event.slug))
assert resp.json['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
# 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_event_notify_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
for i in range(8):
user_was_present = None
if i < 3:
user_was_present = True
elif i < 7:
user_was_present = False
Booking.objects.create(
event=event,
user_was_present=user_was_present,
presence_callback_url='https://example.invalid/presence/%s' % i,
absence_callback_url='https://example.invalid/absence/%s' % i,
)
app.authorization = ('Basic', ('john.doe', 'password'))
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
mock_response = mock.Mock(status_code=200)
mock_send.return_value = mock_response
app.post('/api/agenda/%s/check/%s/' % (agenda.slug, event.slug))
event.refresh_from_db()
assert event.checked is True
assert {x[0][0].url for x in mock_send.call_args_list} == {
'https://example.invalid/presence/0',
'https://example.invalid/presence/1',
'https://example.invalid/presence/2',
'https://example.invalid/absence/3',
'https://example.invalid/absence/4',
'https://example.invalid/absence/5',
'https://example.invalid/absence/6',
}
@pytest.mark.parametrize(
'days_in, days_out, err_msg',
[
(1, None, 'Expected a list of items but got type "int".'),
('2', [2], None),
([3], [3], None),
(['4'], [4], None),
([1, 2], [1, 2], None),
(['2', '3'], [2, 3], None),
('4, 5', [4, 5], None),
],
)
def test_string_or_list_serialiser(app, user, days_in, days_out, err_msg):
app.authorization = ('Basic', ('john.doe', 'password'))
agenda = Agenda(label='Foo bar')
agenda.maximal_booking_delay = 0
agenda.save()
api_url = '/api/agenda/%s/event/' % (agenda.slug)
params = {
'start_datetime': '2022-02-03 16:00',
'places': '1',
'recurrence_days': days_in,
'recurrence_end_date': '2022-02-13',
}
if not err_msg:
resp = app.post_json(api_url, params=params)
assert not resp.json['err']
assert Event.objects.get(primary_event__isnull=True).recurrence_days == days_out
else:
resp = app.post_json(api_url, params=params, status=400)
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['recurrence_days'][0] == err_msg
@pytest.mark.freeze_time('2021-11-01')
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.objects.create(label='Foo bar Meeting', kind='meetings')
api_url = '/api/agenda/%s/event/' % (meeting_agenda.slug)
resp = app.post(api_url, status=404)
assert resp.json['detail'] == 'Not found.'
agenda = Agenda.objects.create(label='Foo bar', maximal_booking_delay=0)
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_json(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_json(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
assert event.custom_fields == {}
# add an event without custom fields
events_type = EventsType.objects.create(
label='Foo',
custom_fields=[
{'varname': 'text', 'label': 'Text', 'field_type': 'text'},
{'varname': 'textarea', 'label': 'TextArea', 'field_type': 'textarea'},
{'varname': 'bool', 'label': 'Bool', 'field_type': 'bool'},
],
)
agenda.events_type = events_type
agenda.save()
params = {
'start_datetime': '2021-11-15 15:38',
'places': 10,
}
resp = app.post_json(api_url, params=params)
assert not resp.json['err']
event = Event.objects.latest('pk')
assert event.custom_fields == {
'text': '',
'textarea': '',
'bool': 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/?',
'custom_field_text': 'foo',
'custom_field_textarea': 'foo bar',
'custom_field_bool': True,
}
resp = app.post_json(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'
assert event.custom_fields == {
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
}
# add with errors in bool custom field
params = {
'start_datetime': '2021-11-15 15:38',
'places': 10,
'custom_field_bool': 'foobar',
}
resp = app.post_json(api_url, params=params, status=400)
assert resp.json['err']
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['custom_field_bool'][0] == 'Must be a valid boolean.'
# add with errors in recurrence_days list
params = {
'start_datetime': '2021-11-15 15:38',
'places': 10,
'recurrence_days': 'oups',
}
resp = app.post_json(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_json(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': '3',
'recurrence_week_interval': '2',
'description': 'A recurrent event',
}
assert Event.objects.filter(agenda=agenda).count() == 3
resp = app.post_json(api_url, params=params)
assert Event.objects.filter(agenda=agenda).count() == 29
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.description == 'A recurrent event'
assert event.recurrence_days == [3]
assert event.recurrence_week_interval == 2
assert event.recurrence_end_date is None
# some occurrences created
assert event.recurrences.count() == 25
# 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',
'custom_field_text': 'foo',
'custom_field_textarea': 'foo bar',
'custom_field_bool': True,
}
resp = app.post_json(api_url, params=params)
assert not resp.json['err']
assert resp.json['data']['id'] == 'foo-bar-event-3'
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-3')
assert Event.objects.filter(agenda=agenda).count() == 39
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 event.custom_fields == {
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
}
assert event.recurrences.count() == 9
assert sorted(
str(x.start_datetime.date()) for x in Event.objects.all() if 'foo-bar-event-3--' 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',
]
for ev in event.recurrences.all():
assert ev.custom_fields == {
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
}
app.delete('/api/agenda/%s/event/' % agenda.slug, status=405) # forbidden
@pytest.mark.freeze_time('2023-03-09')
def test_add_event_end_datetime(app, user):
agenda = Agenda.objects.create(label='Foo bar', maximal_booking_delay=0)
api_url = '/api/agenda/%s/event/' % (agenda.slug)
app.authorization = ('Basic', ('john.doe', 'password'))
# add an event
params = {'start_datetime': '2023-03-10 14:00', 'places': 10, 'duration': 30}
resp = app.post_json(api_url, params=params)
assert not resp.json['err']
assert resp.json['data']['id'] == 'foo-bar-event'
assert resp.json['data']['datetime'] == '2023-03-10 14:00:00'
assert resp.json['data']['end_datetime'] == '2023-03-10 14:30:00'
@pytest.mark.freeze_time('2021-11-01')
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.'
events_type = EventsType.objects.create(
label='Foo',
custom_fields=[
{'varname': 'text', 'label': 'Text', 'field_type': 'text'},
{'varname': 'textarea', 'label': 'TextArea', 'field_type': 'textarea'},
{'varname': 'bool', 'label': 'Bool', 'field_type': 'bool'},
],
)
agenda = Agenda.objects.create(label='Foo bar', events_type=events_type)
# 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.'
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_json(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_json(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'
assert event.custom_fields == {
'text': '',
'textarea': '',
'bool': None,
}
# update event as a recurring event
params = {
'recurrence_days': '0,3,5',
'recurrence_week_interval': 2,
'recurrence_end_date': '2021-12-27',
# with custom fields
'custom_field_text': 'foo',
'custom_field_textarea': 'foo bar',
'custom_field_bool': True,
}
resp = app.patch_json(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 event.custom_fields == {
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
}
assert [x.places for x in event.recurrences.all()] == [8] * 9
for ev in event.recurrences.all():
assert ev.custom_fields == {
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
}
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_json(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_json(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
params = {
'recurrence_week_interval': 1,
}
resp = app.patch_json(recurrence_url, params=params)
assert not resp.json['err']
# update of custom field of one of event recurrences is ignored
params = {
'custom_field_text': 'foo bar baz',
}
resp = app.patch_json(recurrence_url, params=params)
assert not resp.json['err']
recurrence.refresh_from_db()
assert recurrence.custom_fields == {
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
}
booking = Booking.objects.create(event=event.recurrences.all()[2])
# update unprotected fields
params = {
'places': 7,
}
resp = app.patch_json(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_json(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_json(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_json(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_json(api_url, params=params)
assert not resp.json['err']
event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
assert event.recurrences.count() == 5
# update just one custom field
params = {
'custom_field_textarea': 'foo bar baz',
}
resp = app.patch_json(api_url, params=params)
assert not resp.json['err']
event.refresh_from_db()
assert event.custom_fields == {
'text': 'foo',
'textarea': 'foo bar baz',
'bool': True,
}
params = {
'custom_field_bool': False,
}
resp = app.patch_json(api_url, params=params)
assert not resp.json['err']
event.refresh_from_db()
assert event.custom_fields == {
'text': 'foo',
'textarea': 'foo bar baz',
'bool': False,
}
# reset custom fields
params = {
'custom_field_text': '',
'custom_field_textarea': '',
'custom_field_bool': None,
}
resp = app.patch_json(api_url, params=params)
assert not resp.json['err']
event.refresh_from_db()
assert event.custom_fields == {
'text': '',
'textarea': '',
'bool': None,
}
def test_event_read_only_fields(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
agenda2 = Agenda.objects.create(label='Foo bar 2', kind='events')
event = Event.objects.create(
slug='event', start_datetime=now() + datetime.timedelta(days=5), places=1, agenda=agenda
)
app.authorization = ('Basic', ('john.doe', 'password'))
api_url = '/api/agenda/%s/event/' % agenda.slug
params = {
'slug': 'slug',
'agenda': agenda2.slug,
'primary_event': event.slug,
'start_datetime': now().isoformat(),
'places': 42,
}
resp = app.post(api_url, params=params)
assert resp.json['err'] == 0
new_event = Event.objects.latest('pk')
assert new_event.slug == 'foo-bar-event'
assert new_event.agenda == agenda
assert new_event.primary_event is None
api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, new_event.slug)
params = {
'slug': 'slug',
'agenda': agenda2.slug,
'primary_event': event.slug,
}
resp = app.patch(api_url, params=params)
assert resp.json['err'] == 0
new_event.refresh_from_db()
assert new_event.slug == 'foo-bar-event'
assert new_event.agenda == agenda
assert new_event.primary_event is None
@pytest.mark.freeze_time('2021-11-01 10:00')
def test_delete_event(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
event = Event.objects.create(
slug='event', start_datetime=now() + datetime.timedelta(days=5), places=1, agenda=agenda
)
# no authentication
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug), status=401)
assert Event.objects.count() == 1
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
assert not Event.objects.exists()
@pytest.mark.freeze_time('2021-11-01 10:00')
def test_delete_event_forbidden(app, user, freezer):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
event = Event.objects.create(
slug='event', start_datetime=now() + datetime.timedelta(days=5), places=1, agenda=agenda
)
booking = Booking.objects.create(event=event)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 1
assert 'This cannot be removed' in resp.json['err_desc']
freezer.move_to('2021-11-10 10:00')
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
assert not Event.objects.exists()
event = Event.objects.create(
slug='event', start_datetime=now() + datetime.timedelta(days=5), places=1, agenda=agenda
)
booking = Booking.objects.create(event=event)
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 1
booking.cancellation_datetime = now()
booking.save()
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
assert not Event.objects.exists()
@pytest.mark.freeze_time('2021-11-01 10:00')
def test_delete_recurring_event_forbidden(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
start_datetime = now() + datetime.timedelta(days=10)
event = Event.objects.create(
start_datetime=start_datetime,
places=10,
agenda=agenda,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=7),
)
event.create_all_recurrences()
event_recurrence = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
booking = Booking.objects.create(event=event_recurrence)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 1
assert 'This cannot be removed' in resp.json['err_desc']
booking.cancellation_datetime = now()
booking.save()
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
assert not Event.objects.exists()
@pytest.mark.freeze_time('2022-07-01 14:00')
def test_events(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
# missing slots
resp = app.get(
'/api/agendas/events/',
params={},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['slots'] == ['This field is required.']
agenda = Agenda.objects.create(label='Foo')
agenda2 = Agenda.objects.create(label='Bar')
start_datetime = now()
# recurring event
recurring_event = Event.objects.create(
slug='recurring-event-slug',
label='Recurring Event Label',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=8),
places=10,
agenda=agenda,
custom_fields={
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
},
)
recurring_event.create_all_recurrences()
Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=start_datetime + datetime.timedelta(days=1),
places=10,
agenda=agenda2,
checked=True,
check_locked=True,
invoiced=True,
)
# cancelled event, not returned
Event.objects.create(
slug='cancelled',
label='Cancelled',
start_datetime=start_datetime,
places=10,
agenda=agenda,
cancelled=True,
)
# unknown event in list
resp = app.get(
'/api/agendas/events/',
params={'slots': ['foo@event-slug', 'foo@recurring-event-slug--2022-07-01-1600']},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid slugs: foo@event-slug'
# cancelled event in list
resp = app.get(
'/api/agendas/events/',
params={'slots': ['bar@event-slug', 'foo@recurring-event-slug--2022-07-01-1600', 'foo@cancelled']},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid slugs: foo@cancelled'
# bad event format
resp = app.get(
'/api/agendas/events/',
params={'slots': ['bar']},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid slugs: bar'
resp = app.get(
'/api/agendas/events/',
params={'slots': ['bar@bar@bar']},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid slugs: bar@bar@bar'
# ok
resp = app.get(
'/api/agendas/events/',
params={
'slots': [
'bar@event-slug',
'foo@recurring-event-slug--2022-07-01-1600',
'foo@recurring-event-slug',
]
},
)
assert resp.json['data'] == [
{
'agenda': 'bar',
'check_locked': True,
'checked': True,
'description': None,
'duration': None,
'invoiced': True,
'label': 'Event Label',
'places': 10,
'pricing': None,
'primary_event': None,
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'slug': 'event-slug',
'start_datetime': '2022-07-02T16:00:00+02:00',
'url': None,
'waiting_list_places': 0,
},
{
'agenda': 'foo',
'check_locked': False,
'checked': False,
'description': None,
'duration': None,
'invoiced': False,
'label': 'Recurring Event Label',
'places': 10,
'pricing': None,
'primary_event': 'recurring-event-slug',
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'slug': 'recurring-event-slug--2022-07-01-1600',
'start_datetime': '2022-07-01T16:00:00+02:00',
'url': None,
'waiting_list_places': 0,
},
{
'agenda': 'foo',
'check_locked': False,
'checked': False,
'description': None,
'duration': None,
'invoiced': False,
'label': 'Recurring Event Label',
'places': 10,
'pricing': None,
'primary_event': None,
'publication_datetime': None,
'recurrence_days': [4],
'recurrence_end_date': '2022-07-09',
'recurrence_week_interval': 1,
'slug': 'recurring-event-slug',
'start_datetime': '2022-07-01T16:00:00+02:00',
'url': None,
'waiting_list_places': 0,
},
]
# result sorting ?
with CaptureQueriesContext(connection) as ctx:
resp = app.get(
'/api/agendas/events/',
params={
'slots': [
'foo@recurring-event-slug--2022-07-01-1600',
'bar@event-slug',
'foo@recurring-event-slug--2022-07-08-1600',
'foo@recurring-event-slug',
]
},
)
# there's one less query on django 3.2, the ORM has probably
# been optimized there
assert len(ctx.captured_queries) in (5, 6)
assert [(d['agenda'], d['slug']) for d in resp.json['data']] == [
('foo', 'recurring-event-slug--2022-07-01-1600'),
('bar', 'event-slug'),
('foo', 'recurring-event-slug--2022-07-08-1600'),
('foo', 'recurring-event-slug'),
]
def test_events_check_status_params(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
# missing user_external_id
resp = app.get(
'/api/agendas/events/check-status/',
params={'agendas': 'foo', 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
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.']
# missing agendas
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['This field is required.']
# unknown agenda
resp = app.get(
'/api/agendas/events/check-status/',
params={
'user_external_id': 'child:42',
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar, foo']
Agenda.objects.create(label='Foo')
resp = app.get(
'/api/agendas/events/check-status/',
params={
'user_external_id': 'child:42',
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# wrong kind
wrong_agenda = Agenda.objects.create(label='Bar')
for kind in ['meetings', 'virtual']:
wrong_agenda.kind = kind
wrong_agenda.save()
resp = app.get(
'/api/agendas/events/check-status/',
params={
'user_external_id': 'child:42',
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# missing date_start
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'agendas': 'foo', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_start'] == ['This field is required.']
# missing date_end
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'agendas': 'foo', 'date_start': '2022-05-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_end'] == ['This field is required.']
# bad date format
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'agendas': 'foo', 'date_start': 'wrong', 'date_end': 'wrong'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert 'wrong format' in resp.json['errors']['date_start'][0]
assert 'wrong format' in resp.json['errors']['date_end'][0]
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_status(app, user):
agenda = Agenda.objects.create(label='Foo')
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=now(),
places=10,
agenda=agenda,
checked=True,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
# not booked
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'not-booked',
}
assert resp.json['data'][0]['booking'] == {}
# 2 bookings found, error
booking = Booking.objects.create(event=event, user_external_id='child:42')
booking2 = Booking.objects.create(event=event, user_external_id='child:42')
Booking.objects.create(event=event, user_external_id='other')
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'error',
'error_reason': 'too-many-bookings-found',
}
assert resp.json['data'][0]['booking'] == {}
# booking cancelled
booking2.delete()
booking.cancellation_datetime = now()
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'cancelled',
}
assert list(resp.json['data'][0]['booking'].keys()) == [
'id',
'in_waiting_list',
'user_first_name',
'user_last_name',
'user_email',
'user_phone_number',
'user_was_present',
'user_absence_reason',
'user_presence_reason',
'use_color_for',
'extra_data',
'creation_datetime',
'cancellation_datetime',
'label',
]
assert resp.json['data'][0]['booking']['cancellation_datetime'] == localtime(now()).isoformat()
# booking not checked
booking.cancellation_datetime = None
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'error',
'error_reason': 'booking-not-checked',
}
assert resp.json['data'][0]['booking']['cancellation_datetime'] is None
# absence
booking.user_was_present = False
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'absence',
'check_type': None,
}
assert resp.json['data'][0]['booking']['user_was_present'] is False
assert resp.json['data'][0]['booking']['user_absence_reason'] is None
assert resp.json['data'][0]['booking']['user_presence_reason'] is None
# absence with check type
booking.user_check_type_slug = 'foo-reason'
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'absence',
'check_type': 'foo-reason',
}
assert resp.json['data'][0]['booking']['user_was_present'] is False
assert resp.json['data'][0]['booking']['user_absence_reason'] == 'foo-reason'
assert resp.json['data'][0]['booking']['user_presence_reason'] is None
# presence
booking.user_check_type_slug = None
booking.user_was_present = True
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'presence',
'check_type': None,
}
assert resp.json['data'][0]['booking']['user_was_present'] is True
assert resp.json['data'][0]['booking']['user_absence_reason'] is None
assert resp.json['data'][0]['booking']['user_presence_reason'] is None
# presence with check type
booking.user_check_type_slug = 'foo-reason'
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'presence',
'check_type': 'foo-reason',
}
assert resp.json['data'][0]['booking']['user_was_present'] is True
assert resp.json['data'][0]['booking']['user_absence_reason'] is None
assert resp.json['data'][0]['booking']['user_presence_reason'] == 'foo-reason'
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_status_events(app, user):
events_type = EventsType.objects.create(
label='Foo',
custom_fields=[
{'varname': 'text', 'label': 'Text', 'field_type': 'text'},
{'varname': 'textarea', 'label': 'TextArea', 'field_type': 'textarea'},
{'varname': 'bool', 'label': 'Bool', 'field_type': 'bool'},
],
)
agenda = Agenda.objects.create(label='Foo', events_type=events_type)
start_datetime = now()
# recurring event
recurring_event = Event.objects.create(
slug='recurring-event-slug',
label='Recurring Event Label',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=7),
places=10,
agenda=agenda,
custom_fields={
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
},
)
recurring_event.create_all_recurrences()
first_event = recurring_event.recurrences.get()
first_event.checked = True
first_event.save()
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=start_datetime - datetime.timedelta(days=1),
places=10,
agenda=agenda,
checked=True,
check_locked=True,
invoiced=True,
)
# not checked event
notchecked_event = Event.objects.create(
slug='notchecked-event-slug',
label='Not Checked Event Label',
start_datetime=start_datetime - datetime.timedelta(days=2),
places=10,
agenda=agenda,
checked=False,
)
# cancelled event, not returned
Event.objects.create(
slug='cancelled',
label='Cancelled',
start_datetime=start_datetime,
places=10,
agenda=agenda,
cancelled=True,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
booking1 = Booking.objects.create(
event=first_event,
user_external_id='child:42',
user_was_present=True,
user_check_type_slug='foo-reason',
)
booking2 = Booking.objects.create(
event=event, user_external_id='child:42', user_was_present=True, user_check_type_slug='foo-reason'
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
with CaptureQueriesContext(connection) as ctx:
resp = app.get(url, params=params)
assert len(ctx.captured_queries) == 5
assert resp.json['err'] == 0
assert resp.json['data'] == [
{
'event': {
'description': None,
'duration': None,
'label': 'Not Checked Event Label',
'slug': 'notchecked-event-slug',
'places': 10,
'pricing': None,
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'start_datetime': localtime(notchecked_event.start_datetime).isoformat(),
'url': None,
'waiting_list_places': 0,
'agenda': agenda.slug,
'primary_event': None,
'check_locked': False,
'checked': False,
'invoiced': False,
'custom_field_bool': None,
'custom_field_text': '',
'custom_field_textarea': '',
},
'check_status': {'error_reason': 'event-not-checked', 'status': 'error'},
'booking': {},
},
{
'event': {
'description': None,
'duration': None,
'label': 'Event Label',
'slug': 'event-slug',
'places': 10,
'pricing': None,
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'start_datetime': localtime(event.start_datetime).isoformat(),
'url': None,
'waiting_list_places': 0,
'agenda': agenda.slug,
'primary_event': None,
'check_locked': True,
'checked': True,
'invoiced': True,
'custom_field_bool': None,
'custom_field_text': '',
'custom_field_textarea': '',
},
'check_status': {'check_type': 'foo-reason', 'status': 'presence'},
'booking': {
'cancellation_datetime': None,
'use_color_for': None,
'creation_datetime': localtime(now()).isoformat(),
'extra_data': None,
'id': booking2.pk,
'in_waiting_list': False,
'user_absence_reason': None,
'user_email': '',
'user_first_name': '',
'user_last_name': '',
'user_phone_number': '',
'user_presence_reason': 'foo-reason',
'user_was_present': True,
'label': '',
},
},
{
'event': {
'description': None,
'duration': None,
'label': 'Recurring Event Label',
'slug': 'recurring-event-slug--2022-05-30-1600',
'places': 10,
'pricing': None,
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'start_datetime': localtime(first_event.start_datetime).isoformat(),
'url': None,
'waiting_list_places': 0,
'agenda': agenda.slug,
'primary_event': recurring_event.slug,
'check_locked': False,
'checked': True,
'invoiced': False,
'custom_field_text': 'foo',
'custom_field_textarea': 'foo bar',
'custom_field_bool': True,
},
'check_status': {'check_type': 'foo-reason', 'status': 'presence'},
'booking': {
'cancellation_datetime': None,
'use_color_for': None,
'creation_datetime': localtime(now()).isoformat(),
'extra_data': None,
'id': booking1.pk,
'in_waiting_list': False,
'user_absence_reason': None,
'user_email': '',
'user_first_name': '',
'user_last_name': '',
'user_phone_number': '',
'user_presence_reason': 'foo-reason',
'user_was_present': True,
'label': '',
},
},
]
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_status_agendas_filter(app, user):
agenda1 = Agenda.objects.create(label='Foo')
agenda2 = Agenda.objects.create(label='Foo 2')
Event.objects.create(
slug='event-1',
label='Event 1',
start_datetime=now(),
places=10,
agenda=agenda1,
)
Event.objects.create(
slug='event-2',
label='Event 2',
start_datetime=now(),
places=10,
agenda=agenda2,
)
Subscription.objects.create(
agenda=agenda1,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
Subscription.objects.create(
agenda=agenda2,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo, foo-2',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.get(url, params=params)
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['event']['slug'] == 'event-1'
assert resp.json['data'][1]['event']['slug'] == 'event-2'
params['agendas'] = 'foo'
resp = app.get(url, params=params)
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['event']['slug'] == 'event-1'
params['agendas'] = 'foo-2'
resp = app.get(url, params=params)
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['event']['slug'] == 'event-2'
@pytest.mark.parametrize(
'event_date, expected',
[
# just before first day
((2022, 4, 30, 12, 0), False),
# first day
((2022, 5, 1, 12, 0), True),
# last day
((2022, 5, 31, 12, 0), True),
# just after last day
((2022, 6, 1, 12, 0), False),
],
)
def test_events_check_status_date_filter(app, user, event_date, expected):
agenda = Agenda.objects.create(label='Foo')
Event.objects.create(
slug='event',
label='Event',
start_datetime=make_aware(datetime.datetime(*event_date)),
places=10,
agenda=agenda,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.get(url, params=params)
assert len(resp.json['data']) == int(expected)
@pytest.mark.parametrize(
'event_date, expected',
[
# just before first day
((2022, 4, 30, 12, 0), False),
# first day
((2022, 5, 1, 12, 0), True),
# last day
((2022, 5, 31, 12, 0), True),
# just after last day
((2022, 6, 1, 12, 0), False),
],
)
def test_events_check_status_subscription_filter(app, user, freezer, event_date, expected):
agenda = Agenda.objects.create(label='Foo')
Event.objects.create(
slug='event',
label='Event',
start_datetime=make_aware(datetime.datetime(*event_date)),
places=10,
agenda=agenda,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2022, month=5, day=1),
date_end=datetime.date(year=2022, month=6, day=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='other',
date_start=datetime.date(year=2022, month=4, day=1),
date_end=datetime.date(year=2022, month=7, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-04-01',
'date_end': '2022-07-01',
}
resp = app.get(url, params=params)
assert len(resp.json['data']) == int(expected)
def test_events_check_lock_params(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
# missing check_locked
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={'agendas': 'foo', 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['check_locked'] == ['This field is required.']
# missing agendas
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={'check_locked': True, 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['This field is required.']
# unknown agenda
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={
'check_locked': True,
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar, foo']
Agenda.objects.create(label='Foo')
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={
'check_locked': True,
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# wrong kind
wrong_agenda = Agenda.objects.create(label='Bar')
for kind in ['meetings', 'virtual']:
wrong_agenda.kind = kind
wrong_agenda.save()
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={
'check_locked': True,
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# missing date_start
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={'check_locked': True, 'agendas': 'foo', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_start'] == ['This field is required.']
# missing date_end
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={'check_locked': True, 'agendas': 'foo', 'date_start': '2022-05-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_end'] == ['This field is required.']
# bad date format
resp = app.post_json(
'/api/agendas/events/check-lock/',
params={'check_locked': True, 'agendas': 'foo', 'date_start': 'wrong', 'date_end': 'wrong'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert 'wrong format' in resp.json['errors']['date_start'][0]
assert 'wrong format' in resp.json['errors']['date_end'][0]
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_lock(app, user):
agenda = Agenda.objects.create(label='Foo')
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=now(),
places=10,
agenda=agenda,
checked=True,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-lock/'
params = {
'check_locked': True,
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.post_json(url, params=params)
assert resp.json['err'] == 0
event.refresh_from_db()
assert event.check_locked is True
params['check_locked'] = False
resp = app.post_json(url, params=params)
assert resp.json['err'] == 0
event.refresh_from_db()
assert event.check_locked is False
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_lock_events(app, user):
events_type = EventsType.objects.create(
label='Foo',
)
agenda = Agenda.objects.create(label='Foo', events_type=events_type)
start_datetime = now()
# recurring event
recurring_event = Event.objects.create(
slug='recurring-event-slug',
label='Recurring Event Label',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=7),
places=10,
agenda=agenda,
)
recurring_event.create_all_recurrences()
first_event = recurring_event.recurrences.get()
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=start_datetime - datetime.timedelta(days=1),
places=10,
agenda=agenda,
)
# cancelled event, not updated
cancelled_event = Event.objects.create(
slug='cancelled',
label='Cancelled',
start_datetime=start_datetime,
places=10,
agenda=agenda,
cancelled=True,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-lock/'
params = {
'check_locked': True,
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.post_json(url, params=params)
assert resp.json['err'] == 0
recurring_event.refresh_from_db()
assert recurring_event.check_locked is False
first_event.refresh_from_db()
assert first_event.check_locked is True
event.refresh_from_db()
assert event.check_locked is True
cancelled_event.refresh_from_db()
assert cancelled_event.check_locked is False
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_lock_agendas_filter(app, user):
agenda1 = Agenda.objects.create(label='Foo')
agenda2 = Agenda.objects.create(label='Foo 2')
event1 = Event.objects.create(
slug='event-1',
label='Event 1',
start_datetime=now(),
places=10,
agenda=agenda1,
)
event2 = Event.objects.create(
slug='event-2',
label='Event 2',
start_datetime=now(),
places=10,
agenda=agenda2,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-lock/'
params = {
'check_locked': True,
'agendas': 'foo, foo-2',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
app.post_json(url, params=params)
event1.refresh_from_db()
assert event1.check_locked is True
event2.refresh_from_db()
assert event2.check_locked is True
params['agendas'] = 'foo'
params['check_locked'] = False
app.post_json(url, params=params)
event1.refresh_from_db()
assert event1.check_locked is False
event2.refresh_from_db()
assert event2.check_locked is True
params['agendas'] = 'foo-2'
app.post_json(url, params=params)
event1.refresh_from_db()
assert event1.check_locked is False
event2.refresh_from_db()
assert event2.check_locked is False
@pytest.mark.parametrize(
'event_date, expected',
[
# just before first day
((2022, 4, 30, 12, 0), False),
# first day
((2022, 5, 1, 12, 0), True),
# last day
((2022, 5, 31, 12, 0), True),
# just after last day
((2022, 6, 1, 12, 0), False),
],
)
def test_events_check_lock_date_filter(app, user, event_date, expected):
agenda = Agenda.objects.create(label='Foo')
event = Event.objects.create(
slug='event',
label='Event',
start_datetime=make_aware(datetime.datetime(*event_date)),
places=10,
agenda=agenda,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-lock/'
params = {
'check_locked': True,
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
app.post_json(url, params=params)
event.refresh_from_db()
assert event.check_locked == expected
def test_events_invoiced_params(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
# missing invoiced
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={'agendas': 'foo', 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['invoiced'] == ['This field is required.']
# missing agendas
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={'invoiced': True, 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['This field is required.']
# unknown agenda
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={
'invoiced': True,
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar, foo']
Agenda.objects.create(label='Foo')
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={
'invoiced': True,
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# wrong kind
wrong_agenda = Agenda.objects.create(label='Bar')
for kind in ['meetings', 'virtual']:
wrong_agenda.kind = kind
wrong_agenda.save()
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={
'invoiced': True,
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# missing date_start
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={'invoiced': True, 'agendas': 'foo', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_start'] == ['This field is required.']
# missing date_end
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={'invoiced': True, 'agendas': 'foo', 'date_start': '2022-05-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_end'] == ['This field is required.']
# bad date format
resp = app.post_json(
'/api/agendas/events/invoiced/',
params={'invoiced': True, 'agendas': 'foo', 'date_start': 'wrong', 'date_end': 'wrong'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert 'wrong format' in resp.json['errors']['date_start'][0]
assert 'wrong format' in resp.json['errors']['date_end'][0]
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_invoiced(app, user):
agenda = Agenda.objects.create(label='Foo')
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=now(),
places=10,
agenda=agenda,
checked=True,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/invoiced/'
params = {
'invoiced': True,
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.post_json(url, params=params)
assert resp.json['err'] == 0
event.refresh_from_db()
assert event.invoiced is True
params['invoiced'] = False
resp = app.post_json(url, params=params)
assert resp.json['err'] == 0
event.refresh_from_db()
assert event.invoiced is False
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_invoiced_events(app, user):
events_type = EventsType.objects.create(
label='Foo',
)
agenda = Agenda.objects.create(label='Foo', events_type=events_type)
start_datetime = now()
# recurring event
recurring_event = Event.objects.create(
slug='recurring-event-slug',
label='Recurring Event Label',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=7),
places=10,
agenda=agenda,
)
recurring_event.create_all_recurrences()
first_event = recurring_event.recurrences.get()
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=start_datetime - datetime.timedelta(days=1),
places=10,
agenda=agenda,
)
# cancelled event, not updated
cancelled_event = Event.objects.create(
slug='cancelled',
label='Cancelled',
start_datetime=start_datetime,
places=10,
agenda=agenda,
cancelled=True,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/invoiced/'
params = {
'invoiced': True,
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.post_json(url, params=params)
assert resp.json['err'] == 0
recurring_event.refresh_from_db()
assert recurring_event.invoiced is False
first_event.refresh_from_db()
assert first_event.invoiced is True
event.refresh_from_db()
assert event.invoiced is True
cancelled_event.refresh_from_db()
assert cancelled_event.invoiced is False
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_invoiced_agendas_filter(app, user):
agenda1 = Agenda.objects.create(label='Foo')
agenda2 = Agenda.objects.create(label='Foo 2')
event1 = Event.objects.create(
slug='event-1',
label='Event 1',
start_datetime=now(),
places=10,
agenda=agenda1,
)
event2 = Event.objects.create(
slug='event-2',
label='Event 2',
start_datetime=now(),
places=10,
agenda=agenda2,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/invoiced/'
params = {
'invoiced': True,
'agendas': 'foo, foo-2',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
app.post_json(url, params=params)
event1.refresh_from_db()
assert event1.invoiced is True
event2.refresh_from_db()
assert event2.invoiced is True
params['agendas'] = 'foo'
params['invoiced'] = False
app.post_json(url, params=params)
event1.refresh_from_db()
assert event1.invoiced is False
event2.refresh_from_db()
assert event2.invoiced is True
params['agendas'] = 'foo-2'
app.post_json(url, params=params)
event1.refresh_from_db()
assert event1.invoiced is False
event2.refresh_from_db()
assert event2.invoiced is False
@pytest.mark.parametrize(
'event_date, expected',
[
# just before first day
((2022, 4, 30, 12, 0), False),
# first day
((2022, 5, 1, 12, 0), True),
# last day
((2022, 5, 31, 12, 0), True),
# just after last day
((2022, 6, 1, 12, 0), False),
],
)
def test_events_invoiced_date_filter(app, user, event_date, expected):
agenda = Agenda.objects.create(label='Foo')
event = Event.objects.create(
slug='event',
label='Event',
start_datetime=make_aware(datetime.datetime(*event_date)),
places=10,
agenda=agenda,
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/invoiced/'
params = {
'invoiced': True,
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
app.post_json(url, params=params)
event.refresh_from_db()
assert event.invoiced == expected