From ab0c8f4f4fcb02ad5e1f9aa90421361e5abb2217 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 8 Feb 2022 10:12:13 +0100
Subject: [PATCH 2/2] misc: faster tests for api booking endpoints

---
 chrono/agendas/models.py  |   2 +-
 tests/api/test_booking.py | 174 ++++++++++++--------------------------
 2 files changed, 55 insertions(+), 121 deletions(-)

diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py
index 9a50d69..83e6caf 100644
--- a/chrono/agendas/models.py
+++ b/chrono/agendas/models.py
@@ -1962,7 +1962,7 @@ class Booking(models.Model):
             vevent.add('dtend').value = self.event.end_datetime
 
         for field in ('description', 'location', 'comment', 'url'):
-            field_value = request and request.GET.get(field) or self.extra_data.get(field)
+            field_value = request and request.GET.get(field) or (self.extra_data or {}).get(field)
             if field_value:
                 vevent.add(field).value = field_value
         ics.add(vevent)
diff --git a/tests/api/test_booking.py b/tests/api/test_booking.py
index 52cac7a..465ee5b 100644
--- a/tests/api/test_booking.py
+++ b/tests/api/test_booking.py
@@ -1,5 +1,4 @@
 import datetime
-import urllib.parse as urlparse
 from unittest import mock
 
 import pytest
@@ -24,72 +23,46 @@ pytestmark = pytest.mark.django_db
 def test_booking_ics(app, some_data, meetings_agenda, user):
     agenda = Agenda.objects.filter(label='Foo bar')[0]
     event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
-    app.authorization = ('Basic', ('john.doe', 'password'))
-    resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.id))
 
-    assert Booking.objects.count() == 1
-    assert 'ics_url' in resp.json['api']
-    assert urlparse.urlparse(resp.json['api']['cancel_url']).netloc
-    assert urlparse.urlparse(resp.json['api']['ics_url']).netloc
+    booking = Booking.objects.create(event=event)
 
     formatted_start_date = event.start_datetime.strftime('%Y%m%dT%H%M%S')
-    booking_ics = Booking.objects.get(id=resp.json['booking_id']).get_ics()
-    assert (
-        'UID:%s-%s-%s\r\n' % (event.start_datetime.isoformat(), agenda.pk, resp.json['booking_id'])
-        in booking_ics
-    )
+    booking_ics = booking.get_ics()
+    assert 'UID:%s-%s-%s\r\n' % (event.start_datetime.isoformat(), agenda.pk, booking.pk) in booking_ics
     assert 'SUMMARY:\r\n' in booking_ics
     assert 'DTSTART:%sZ\r\n' % formatted_start_date in booking_ics
     assert 'DTEND:' not in booking_ics
 
     # test with additional data
-    resp = app.post_json(
-        '/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
-        params={
-            'label': 'foo',
-            'user_name': 'bar',
-            'backoffice_url': 'http://example.net/',
-            'url': 'http://example.com/booking',
-        },
-    )
-    assert Booking.objects.count() == 2
-    booking_ics = Booking.objects.get(id=resp.json['booking_id']).get_ics()
+    booking.label = 'foo'
+    booking.user_last_name = 'bar'
+    booking.backoffice_url = 'http://example.net/'
+    booking.extra_data = {'url': 'http://example.com/booking'}
+    booking.save()
+    booking_ics = booking.get_ics()
     assert 'SUMMARY:foo\r\n' in booking_ics
     assert 'ATTENDEE:bar\r\n' in booking_ics
     assert 'URL:http://example.com/booking\r\n' in booking_ics
 
     # test with user_label in additionnal data
-    resp = app.post_json(
-        '/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
-        params={
-            'label': 'foo',
-            'user_first_name': 'foo',
-            'user_last_name': 'bar',
-            'backoffice_url': 'http://example.net/',
-            'url': 'http://example.com/booking',
-            'user_display_label': 'your booking',
-        },
-    )
-    assert Booking.objects.count() == 3
-    booking_ics = Booking.objects.get(id=resp.json['booking_id']).get_ics()
+    booking.user_first_name = 'foo'
+    booking.user_last_name = 'bar'
+    booking.user_display_label = 'your booking'
+    booking.save()
+    booking_ics = booking.get_ics()
     assert 'SUMMARY:your booking\r\n' in booking_ics
     assert 'ATTENDEE:foo bar\r\n' in booking_ics
     assert 'URL:http://example.com/booking\r\n' in booking_ics
 
     # extra data stored in extra_data field
-    resp = app.post_json(
-        '/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
-        params={
-            'label': 'l',
-            'backoffice_url': '',
-            'location': 'bar',
-            'comment': 'booking comment',
-            'description': 'booking description',
-        },
-    )
-    assert Booking.objects.count() == 4
-    booking_id = resp.json['booking_id']
-    booking = Booking.objects.get(id=booking_id)
+    booking.label = 'l'
+    booking.extra_data = {
+        'backoffice_url': '',
+        'location': 'bar',
+        'comment': 'booking comment',
+        'description': 'booking description',
+    }
+    booking.save()
     booking_ics = booking.get_ics()
     assert 'COMMENT:booking comment\r\n' in booking_ics
     assert 'LOCATION:bar\r\n' in booking_ics
@@ -97,10 +70,10 @@ def test_booking_ics(app, some_data, meetings_agenda, user):
 
     # unauthenticated
     app.authorization = None
-    app.get('/api/booking/%s/ics/' % resp.json['booking_id'], status=401)
+    app.get('/api/booking/%s/ics/' % booking.pk, status=401)
 
     app.authorization = ('Basic', ('john.doe', 'password'))
-    resp = app.get('/api/booking/%s/ics/' % resp.json['booking_id'])
+    resp = app.get('/api/booking/%s/ics/' % booking.pk)
     assert resp.headers['Content-Type'] == 'text/calendar'
 
     params = {
@@ -109,19 +82,17 @@ def test_booking_ics(app, some_data, meetings_agenda, user):
         'comment': 'custom comment',
         'url': 'http://example.com/custom',
     }
-    resp = app.get('/api/booking/%s/ics/' % booking_id, params=params)
+    resp = app.get('/api/booking/%s/ics/' % booking.pk, params=params)
     assert 'DESCRIPTION:custom booking description\r\n' in resp.text
     assert 'LOCATION:custom booking location\r\n' in resp.text
     assert 'COMMENT:custom comment\r\n' in resp.text
     assert 'URL:http://example.com/custom\r\n' in resp.text
 
-    meetings_agenda_id = Agenda.objects.filter(label='Foo bar Meeting')[0].id
     meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
     resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
     event = resp.json['data'][2]
-    resp = app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda_id, event['id']))
-    assert Booking.objects.count() == 5
-    assert 'ics_url' in resp.json['api']
+    resp = app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda.pk, event['id']))
+    assert Booking.objects.count() == 2
     booking = Booking.objects.get(id=resp.json['booking_id'])
     booking_ics = booking.get_ics()
     start = booking.event.start_datetime.strftime('%Y%m%dT%H%M%S')
@@ -685,71 +656,15 @@ def test_booking_patch_api_extra_data(app, user):
     assert booking.extra_data == {'foo': None, 'foooo': 'baz'}  # not changed
 
 
-def test_booking_cancellation_api(app, some_data, user):
-    agenda = Agenda.objects.filter(label='Foo bar')[0]
-    event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
-    resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id), status=401)
-
-    app.authorization = ('Basic', ('john.doe', 'password'))
-    resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id))
-    booking_id = resp.json['booking_id']
-    assert Booking.objects.count() == 1
-    resp = app.delete('/api/booking/%s/' % booking_id)
-    assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 1
-
-
-def test_booking_cancellation_post_api(app, some_data, user):
-    agenda = Agenda.objects.filter(label='Foo bar')[0]
-    event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
-    resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id), status=401)
+def test_booking_cancellation_api(app, user):
+    agenda = Agenda.objects.create(kind='events')
+    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
+    booking = Booking.objects.create(event=event)
 
     app.authorization = ('Basic', ('john.doe', 'password'))
-    resp = app.post(
-        '/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
-        params={
-            'cancel_callback_url': 'http://example.net/jump/trigger/',
-        },
-    )
-    booking_id = resp.json['booking_id']
-    assert Booking.objects.count() == 1
-    assert urlparse.urlparse(resp.json['api']['cancel_url']).path == '/api/booking/%s/cancel/' % booking_id
-    with mock.patch('chrono.agendas.models.requests.post') as mocked_post:
-        resp = app.post('/api/booking/%s/cancel/' % booking_id)
-        assert not mocked_post.called
+    app.delete('/api/booking/%s/' % booking.pk)
     assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 1
 
-    # cancel an object that doesn't exist
-    resp = app.post('/api/booking/%s/cancel/' % 0, status=404)
-
-    # cancel an event that was already cancelled
-    resp = app.post('/api/booking/%s/cancel/' % booking_id, status=200)
-    assert resp.json['err'] == 1
-
-
-def test_booking_cancellation_post_meeting_api(app, meetings_agenda, user):
-    agenda_id = Agenda.objects.filter(label='Foo bar Meeting')[0].id
-    meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
-    resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
-    nb_events = len(resp.json['data'])
-    event_id = resp.json['data'][2]['id']
-
-    app.authorization = ('Basic', ('john.doe', 'password'))
-    resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
-    assert Booking.objects.count() == 1
-
-    booking_id = resp.json['booking_id']
-    assert Booking.objects.count() == 1
-    resp = app.post('/api/booking/%s/cancel/' % booking_id)
-
-    resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
-    assert len(resp.json['data']) == nb_events
-
-    # book the same time slot
-    resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
-    assert resp.json['err'] == 0
-    resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
-    assert len([x for x in resp.json['data'] if not x.get('disabled')]) == nb_events - 1
-
 
 def test_bookings(app, user):
     agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
@@ -839,9 +754,9 @@ def test_bookings(app, user):
     )
 
 
-def test_cancel_booking(app, some_data, user):
-    agenda_id = Agenda.objects.filter(label='Foo bar')[0].pk
-    event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
+def test_cancel_booking(app, user):
+    agenda = Agenda.objects.create(kind='events')
+    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
     primary = Booking.objects.create(event=event)
     secondary = Booking.objects.create(event=event, primary_booking=primary)
     app.authorization = ('Basic', ('john.doe', 'password'))
@@ -859,6 +774,25 @@ def test_cancel_booking(app, some_data, user):
     assert primary.cancellation_datetime is not None
     assert secondary.cancellation_datetime is not None
 
+    # cancel an object that doesn't exist
+    resp = app.post('/api/booking/%s/cancel/' % 0, status=404)
+
+    # cancel an event that was already cancelled
+    resp = app.post('/api/booking/%s/cancel/' % primary.pk, status=200)
+    assert resp.json['err'] == 1
+
+
+def test_cancel_booking_no_trigger(app, user):
+    agenda = Agenda.objects.create(kind='events')
+    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
+    booking = Booking.objects.create(event=event, cancel_callback_url='http://example.net/jump/trigger/')
+
+    app.authorization = ('Basic', ('john.doe', 'password'))
+    with mock.patch('chrono.agendas.models.requests.post') as mocked_post:
+        app.post('/api/booking/%s/cancel/' % booking.pk)
+        assert not mocked_post.called
+    assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 1
+
 
 def test_accept_booking(app, user):
     agenda = Agenda.objects.create(kind='events')
-- 
2.34.1

