1 |
1 |
import datetime
|
2 |
|
import urllib.parse as urlparse
|
3 |
2 |
from unittest import mock
|
4 |
3 |
|
5 |
4 |
import pytest
|
... | ... | |
24 |
23 |
def test_booking_ics(app, some_data, meetings_agenda, user):
|
25 |
24 |
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
26 |
25 |
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
27 |
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
28 |
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.id))
|
29 |
26 |
|
30 |
|
assert Booking.objects.count() == 1
|
31 |
|
assert 'ics_url' in resp.json['api']
|
32 |
|
assert urlparse.urlparse(resp.json['api']['cancel_url']).netloc
|
33 |
|
assert urlparse.urlparse(resp.json['api']['ics_url']).netloc
|
|
27 |
booking = Booking.objects.create(event=event)
|
34 |
28 |
|
35 |
29 |
formatted_start_date = event.start_datetime.strftime('%Y%m%dT%H%M%S')
|
36 |
|
booking_ics = Booking.objects.get(id=resp.json['booking_id']).get_ics()
|
37 |
|
assert (
|
38 |
|
'UID:%s-%s-%s\r\n' % (event.start_datetime.isoformat(), agenda.pk, resp.json['booking_id'])
|
39 |
|
in booking_ics
|
40 |
|
)
|
|
30 |
booking_ics = booking.get_ics()
|
|
31 |
assert 'UID:%s-%s-%s\r\n' % (event.start_datetime.isoformat(), agenda.pk, booking.pk) in booking_ics
|
41 |
32 |
assert 'SUMMARY:\r\n' in booking_ics
|
42 |
33 |
assert 'DTSTART:%sZ\r\n' % formatted_start_date in booking_ics
|
43 |
34 |
assert 'DTEND:' not in booking_ics
|
44 |
35 |
|
45 |
36 |
# test with additional data
|
46 |
|
resp = app.post_json(
|
47 |
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
48 |
|
params={
|
49 |
|
'label': 'foo',
|
50 |
|
'user_name': 'bar',
|
51 |
|
'backoffice_url': 'http://example.net/',
|
52 |
|
'url': 'http://example.com/booking',
|
53 |
|
},
|
54 |
|
)
|
55 |
|
assert Booking.objects.count() == 2
|
56 |
|
booking_ics = Booking.objects.get(id=resp.json['booking_id']).get_ics()
|
|
37 |
booking.label = 'foo'
|
|
38 |
booking.user_last_name = 'bar'
|
|
39 |
booking.backoffice_url = 'http://example.net/'
|
|
40 |
booking.extra_data = {'url': 'http://example.com/booking'}
|
|
41 |
booking.save()
|
|
42 |
booking_ics = booking.get_ics()
|
57 |
43 |
assert 'SUMMARY:foo\r\n' in booking_ics
|
58 |
44 |
assert 'ATTENDEE:bar\r\n' in booking_ics
|
59 |
45 |
assert 'URL:http://example.com/booking\r\n' in booking_ics
|
60 |
46 |
|
61 |
47 |
# test with user_label in additionnal data
|
62 |
|
resp = app.post_json(
|
63 |
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
64 |
|
params={
|
65 |
|
'label': 'foo',
|
66 |
|
'user_first_name': 'foo',
|
67 |
|
'user_last_name': 'bar',
|
68 |
|
'backoffice_url': 'http://example.net/',
|
69 |
|
'url': 'http://example.com/booking',
|
70 |
|
'user_display_label': 'your booking',
|
71 |
|
},
|
72 |
|
)
|
73 |
|
assert Booking.objects.count() == 3
|
74 |
|
booking_ics = Booking.objects.get(id=resp.json['booking_id']).get_ics()
|
|
48 |
booking.user_first_name = 'foo'
|
|
49 |
booking.user_last_name = 'bar'
|
|
50 |
booking.user_display_label = 'your booking'
|
|
51 |
booking.save()
|
|
52 |
booking_ics = booking.get_ics()
|
75 |
53 |
assert 'SUMMARY:your booking\r\n' in booking_ics
|
76 |
54 |
assert 'ATTENDEE:foo bar\r\n' in booking_ics
|
77 |
55 |
assert 'URL:http://example.com/booking\r\n' in booking_ics
|
78 |
56 |
|
79 |
57 |
# extra data stored in extra_data field
|
80 |
|
resp = app.post_json(
|
81 |
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
82 |
|
params={
|
83 |
|
'label': 'l',
|
84 |
|
'backoffice_url': '',
|
85 |
|
'location': 'bar',
|
86 |
|
'comment': 'booking comment',
|
87 |
|
'description': 'booking description',
|
88 |
|
},
|
89 |
|
)
|
90 |
|
assert Booking.objects.count() == 4
|
91 |
|
booking_id = resp.json['booking_id']
|
92 |
|
booking = Booking.objects.get(id=booking_id)
|
|
58 |
booking.label = 'l'
|
|
59 |
booking.extra_data = {
|
|
60 |
'backoffice_url': '',
|
|
61 |
'location': 'bar',
|
|
62 |
'comment': 'booking comment',
|
|
63 |
'description': 'booking description',
|
|
64 |
}
|
|
65 |
booking.save()
|
93 |
66 |
booking_ics = booking.get_ics()
|
94 |
67 |
assert 'COMMENT:booking comment\r\n' in booking_ics
|
95 |
68 |
assert 'LOCATION:bar\r\n' in booking_ics
|
... | ... | |
97 |
70 |
|
98 |
71 |
# unauthenticated
|
99 |
72 |
app.authorization = None
|
100 |
|
app.get('/api/booking/%s/ics/' % resp.json['booking_id'], status=401)
|
|
73 |
app.get('/api/booking/%s/ics/' % booking.pk, status=401)
|
101 |
74 |
|
102 |
75 |
app.authorization = ('Basic', ('john.doe', 'password'))
|
103 |
|
resp = app.get('/api/booking/%s/ics/' % resp.json['booking_id'])
|
|
76 |
resp = app.get('/api/booking/%s/ics/' % booking.pk)
|
104 |
77 |
assert resp.headers['Content-Type'] == 'text/calendar'
|
105 |
78 |
|
106 |
79 |
params = {
|
... | ... | |
109 |
82 |
'comment': 'custom comment',
|
110 |
83 |
'url': 'http://example.com/custom',
|
111 |
84 |
}
|
112 |
|
resp = app.get('/api/booking/%s/ics/' % booking_id, params=params)
|
|
85 |
resp = app.get('/api/booking/%s/ics/' % booking.pk, params=params)
|
113 |
86 |
assert 'DESCRIPTION:custom booking description\r\n' in resp.text
|
114 |
87 |
assert 'LOCATION:custom booking location\r\n' in resp.text
|
115 |
88 |
assert 'COMMENT:custom comment\r\n' in resp.text
|
116 |
89 |
assert 'URL:http://example.com/custom\r\n' in resp.text
|
117 |
90 |
|
118 |
|
meetings_agenda_id = Agenda.objects.filter(label='Foo bar Meeting')[0].id
|
119 |
91 |
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
120 |
92 |
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
121 |
93 |
event = resp.json['data'][2]
|
122 |
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda_id, event['id']))
|
123 |
|
assert Booking.objects.count() == 5
|
124 |
|
assert 'ics_url' in resp.json['api']
|
|
94 |
resp = app.post('/api/agenda/%s/fillslot/%s/' % (meetings_agenda.pk, event['id']))
|
|
95 |
assert Booking.objects.count() == 2
|
125 |
96 |
booking = Booking.objects.get(id=resp.json['booking_id'])
|
126 |
97 |
booking_ics = booking.get_ics()
|
127 |
98 |
start = booking.event.start_datetime.strftime('%Y%m%dT%H%M%S')
|
... | ... | |
693 |
664 |
assert booking.extra_data == {'foo': None, 'foooo': 'baz'} # not changed
|
694 |
665 |
|
695 |
666 |
|
696 |
|
def test_booking_cancellation_api(app, some_data, user):
|
697 |
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
698 |
|
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
699 |
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id), status=401)
|
700 |
|
|
701 |
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
702 |
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id))
|
703 |
|
booking_id = resp.json['booking_id']
|
704 |
|
assert Booking.objects.count() == 1
|
705 |
|
resp = app.delete('/api/booking/%s/' % booking_id)
|
706 |
|
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 1
|
707 |
|
|
708 |
|
|
709 |
|
def test_booking_cancellation_post_api(app, some_data, user):
|
710 |
|
agenda = Agenda.objects.filter(label='Foo bar')[0]
|
711 |
|
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
712 |
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id), status=401)
|
|
667 |
def test_booking_cancellation_api(app, user):
|
|
668 |
agenda = Agenda.objects.create(kind='events')
|
|
669 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
|
|
670 |
booking = Booking.objects.create(event=event)
|
713 |
671 |
|
714 |
672 |
app.authorization = ('Basic', ('john.doe', 'password'))
|
715 |
|
resp = app.post(
|
716 |
|
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
717 |
|
params={
|
718 |
|
'cancel_callback_url': 'http://example.net/jump/trigger/',
|
719 |
|
},
|
720 |
|
)
|
721 |
|
booking_id = resp.json['booking_id']
|
722 |
|
assert Booking.objects.count() == 1
|
723 |
|
assert urlparse.urlparse(resp.json['api']['cancel_url']).path == '/api/booking/%s/cancel/' % booking_id
|
724 |
|
with mock.patch('chrono.agendas.models.requests.post') as mocked_post:
|
725 |
|
resp = app.post('/api/booking/%s/cancel/' % booking_id)
|
726 |
|
assert not mocked_post.called
|
|
673 |
app.delete('/api/booking/%s/' % booking.pk)
|
727 |
674 |
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 1
|
728 |
675 |
|
729 |
|
# cancel an object that doesn't exist
|
730 |
|
resp = app.post('/api/booking/%s/cancel/' % 0, status=404)
|
731 |
|
|
732 |
|
# cancel an event that was already cancelled
|
733 |
|
resp = app.post('/api/booking/%s/cancel/' % booking_id, status=200)
|
734 |
|
assert resp.json['err'] == 1
|
735 |
|
|
736 |
|
|
737 |
|
def test_booking_cancellation_post_meeting_api(app, meetings_agenda, user):
|
738 |
|
agenda_id = Agenda.objects.filter(label='Foo bar Meeting')[0].id
|
739 |
|
meeting_type = MeetingType.objects.get(agenda=meetings_agenda)
|
740 |
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
741 |
|
nb_events = len(resp.json['data'])
|
742 |
|
event_id = resp.json['data'][2]['id']
|
743 |
|
|
744 |
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
745 |
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
746 |
|
assert Booking.objects.count() == 1
|
747 |
|
|
748 |
|
booking_id = resp.json['booking_id']
|
749 |
|
assert Booking.objects.count() == 1
|
750 |
|
resp = app.post('/api/booking/%s/cancel/' % booking_id)
|
751 |
|
|
752 |
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
753 |
|
assert len(resp.json['data']) == nb_events
|
754 |
|
|
755 |
|
# book the same time slot
|
756 |
|
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id))
|
757 |
|
assert resp.json['err'] == 0
|
758 |
|
resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
759 |
|
assert len([x for x in resp.json['data'] if not x.get('disabled')]) == nb_events - 1
|
760 |
|
|
761 |
676 |
|
762 |
677 |
def test_bookings(app, user):
|
763 |
678 |
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
|
... | ... | |
847 |
762 |
)
|
848 |
763 |
|
849 |
764 |
|
850 |
|
def test_cancel_booking(app, some_data, user):
|
851 |
|
agenda_id = Agenda.objects.filter(label='Foo bar')[0].pk
|
852 |
|
event = Event.objects.filter(agenda_id=agenda_id).exclude(start_datetime__lt=now())[0]
|
|
765 |
def test_cancel_booking(app, user):
|
|
766 |
agenda = Agenda.objects.create(kind='events')
|
|
767 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
|
853 |
768 |
primary = Booking.objects.create(event=event)
|
854 |
769 |
secondary = Booking.objects.create(event=event, primary_booking=primary)
|
855 |
770 |
app.authorization = ('Basic', ('john.doe', 'password'))
|
... | ... | |
867 |
782 |
assert primary.cancellation_datetime is not None
|
868 |
783 |
assert secondary.cancellation_datetime is not None
|
869 |
784 |
|
|
785 |
# cancel an object that doesn't exist
|
|
786 |
resp = app.post('/api/booking/%s/cancel/' % 0, status=404)
|
|
787 |
|
|
788 |
# cancel an event that was already cancelled
|
|
789 |
resp = app.post('/api/booking/%s/cancel/' % primary.pk, status=200)
|
|
790 |
assert resp.json['err'] == 1
|
|
791 |
|
|
792 |
|
|
793 |
def test_cancel_booking_no_trigger(app, user):
|
|
794 |
agenda = Agenda.objects.create(kind='events')
|
|
795 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
|
|
796 |
booking = Booking.objects.create(event=event, cancel_callback_url='http://example.net/jump/trigger/')
|
|
797 |
|
|
798 |
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
799 |
with mock.patch('chrono.agendas.models.requests.post') as mocked_post:
|
|
800 |
app.post('/api/booking/%s/cancel/' % booking.pk)
|
|
801 |
assert not mocked_post.called
|
|
802 |
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 1
|
|
803 |
|
870 |
804 |
|
871 |
805 |
def test_accept_booking(app, user):
|
872 |
806 |
agenda = Agenda.objects.create(kind='events')
|
873 |
|
-
|