From 3bf36a16170c71d24f5a0d79109acd100863422d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= Date: Thu, 31 Oct 2019 09:42:22 +0100 Subject: [PATCH] api: return err_desc & reason in case of error (#24025) --- chrono/api/utils.py | 26 +++++++++ chrono/api/views.py | 74 ++++++++++++++++++------ tests/test_api.py | 124 ++++++++++++++++++++++++++++++---------- tests/test_api_utils.py | 15 +++++ 4 files changed, 190 insertions(+), 49 deletions(-) create mode 100644 chrono/api/utils.py create mode 100644 tests/test_api_utils.py diff --git a/chrono/api/utils.py b/chrono/api/utils.py new file mode 100644 index 00000000..f4746365 --- /dev/null +++ b/chrono/api/utils.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# chrono - agendas system +# Copyright (C) 2019 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from rest_framework.response import Response as DRFResponse + + +class Response(DRFResponse): + def __init__(self, data=None, *args, **kwargs): + # add reason for compatibility (https://dev.entrouvert.org/issues/24025) + if data is not None and 'err_class' in data: + data['reason'] = data['err_class'] + super(Response, self).__init__(data=data, *args, **kwargs) diff --git a/chrono/api/views.py b/chrono/api/views.py index 7465f1cf..690d9386 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -24,11 +24,14 @@ from django.shortcuts import get_object_or_404 from django.utils.dateparse import parse_date from django.utils.encoding import force_text from django.utils.timezone import now, make_aware, localtime +from django.utils.translation import gettext_noop +from django.utils.translation import ugettext_lazy as _ from rest_framework import permissions, serializers, status -from rest_framework.response import Response + from rest_framework.views import APIView +from chrono.api.utils import Response from ..agendas.models import (Agenda, Event, Booking, MeetingType, TimePeriod, Desk) from ..interval import Intervals @@ -352,7 +355,8 @@ class Fillslots(APIView): if not serializer.is_valid(): return Response({ 'err': 1, - 'reason': 'invalid payload', + 'err_class': 'invalid payload', + 'err_desc': _('invalid payload'), 'errors': serializer.errors }, status=status.HTTP_400_BAD_REQUEST) payload = serializer.validated_data @@ -362,7 +366,8 @@ class Fillslots(APIView): if not slots: return Response({ 'err': 1, - 'reason': 'slots list cannot be empty', + 'err_class': 'slots list cannot be empty', + 'err_desc': _('slots list cannot be empty'), }, status=status.HTTP_400_BAD_REQUEST) if 'count' in payload: @@ -374,7 +379,8 @@ class Fillslots(APIView): except ValueError: return Response({ 'err': 1, - 'reason': 'invalid value for count (%s)' % request.query_params['count'], + 'err_class': 'invalid value for count (%s)' % request.query_params['count'], + 'err_desc': _('invalid value for count (%s)') % request.query_params['count'], }, status=status.HTTP_400_BAD_REQUEST) else: places_count = 1 @@ -382,7 +388,8 @@ class Fillslots(APIView): if places_count <= 0: return Response({ 'err': 1, - 'reason': 'count cannot be less than or equal to zero' + 'err_class': 'count cannot be less than or equal to zero', + 'err_desc': _('count cannot be less than or equal to zero'), }, status=status.HTTP_400_BAD_REQUEST) to_cancel_booking = None @@ -393,7 +400,8 @@ class Fillslots(APIView): except (ValueError, TypeError): return Response({ 'err': 1, - 'reason': 'cancel_booking_id is not an integer' + 'err_class': 'cancel_booking_id is not an integer', + 'err_desc': _('cancel_booking_id is not an integer'), }, status=status.HTTP_400_BAD_REQUEST) if cancel_booking_id is not None: @@ -401,16 +409,20 @@ class Fillslots(APIView): try: to_cancel_booking = Booking.objects.get(pk=cancel_booking_id) if to_cancel_booking.cancellation_datetime: - cancel_error = 'cancel booking: booking already cancelled' + cancel_error = gettext_noop('cancel booking: booking already cancelled') else: to_cancel_places_count = to_cancel_booking.secondary_booking_set.count() + 1 if places_count != to_cancel_places_count: - cancel_error = 'cancel booking: count is different' + cancel_error = gettext_noop('cancel booking: count is different') except Booking.DoesNotExist: - cancel_error = 'cancel booking: booking does no exist' + cancel_error = gettext_noop('cancel booking: booking does no exist') if cancel_error: - return Response({'err': 1, 'reason': cancel_error}) + return Response({ + 'err': 1, + 'err_class': cancel_error, + 'err_desc': _(cancel_error), + }) extra_data = {} for k, v in request.data.items(): @@ -430,12 +442,14 @@ class Fillslots(APIView): except ValueError: return Response({ 'err': 1, - 'reason': 'invalid slot: %s' % slot, + 'err_class': 'invalid slot: %s' % slot, + 'err_desc': _('invalid slot: %s') % slot, }, status=status.HTTP_400_BAD_REQUEST) if meeting_type_id_ != meeting_type_id: return Response({ 'err': 1, - 'reason': 'all slots must have the same meeting type id (%s)' % meeting_type_id + 'err_class': 'all slots must have the same meeting type id (%s)' % meeting_type_id, + 'err_desc': _('all slots must have the same meeting type id (%s)') % meeting_type_id, }, status=status.HTTP_400_BAD_REQUEST) datetimes.add(make_aware(datetime.datetime.strptime(datetime_str, '%Y-%m-%d-%H%M'))) @@ -452,7 +466,11 @@ class Fillslots(APIView): available_desk = Desk.objects.get(id=available_desk_id) break else: - return Response({'err': 1, 'reason': 'no more desk available'}) + return Response({ + 'err': 1, + 'err_class': 'no more desk available', + 'err_desc': _('no more desk available'), + }) # all datetimes are free, book them in order datetimes = list(datetimes) @@ -482,10 +500,18 @@ class Fillslots(APIView): # in the waiting list. in_waiting_list = True if (event.waiting_list + places_count) > event.waiting_list_places: - return Response({'err': 1, 'reason': 'sold out'}) + return Response({ + 'err': 1, + 'err_class': 'sold out', + 'err_desc': _('sold out'), + }) else: if (event.booked_places + places_count) > event.places: - return Response({'err': 1, 'reason': 'sold out'}) + return Response({ + 'err': 1, + 'err_class': 'sold out', + 'err_desc': _('sold out') + }) with transaction.atomic(): if to_cancel_booking: @@ -578,7 +604,11 @@ class CancelBooking(APIView): def post(self, request, booking_pk=None, format=None): booking = get_object_or_404(Booking, id=booking_pk) if booking.cancellation_datetime: - response = {'err': 1, 'reason': 'already cancelled'} + response = { + 'err': 1, + 'err_class': 'already cancelled', + 'err_desc': _('already cancelled'), + } return Response(response) booking.cancel() response = {'err': 0, 'booking_id': booking.id} @@ -599,10 +629,18 @@ class AcceptBooking(APIView): def post(self, request, booking_pk=None, format=None): booking = get_object_or_404(Booking, id=booking_pk) if booking.cancellation_datetime: - response = {'err': 1, 'reason': 'booking is cancelled'} + response = { + 'err': 1, + 'err_class': 'booking is cancelled', + 'err_desc': _('booking is cancelled'), + } return Response(response) if not booking.in_waiting_list: - response = {'err': 2, 'reason': 'booking is not in waiting list'} + response = { + 'err': 2, + 'err_class': 'booking is not in waiting list', + 'err_desc': _('booking is not in waiting list'), + } return Response(response) booking.accept() response = {'err': 0, 'booking_id': booking.id} diff --git a/tests/test_api.py b/tests/test_api.py index 973ef75a..259423bb 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -316,12 +316,16 @@ def test_datetimes_api_meetings_agenda_short_time_periods(app, meetings_agenda, app.authorization = ('Basic', ('john.doe', 'password')) resp = app.post(fillslot_url) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'no more desk available' + assert resp.json['reason'] == 'no more desk available' # legacy + assert resp.json['err_class'] == 'no more desk available' + assert resp.json['err_desc'] == 'no more desk available' # booking the two slots fails too fillslots_url = '/api/agenda/%s/fillslots/' % meeting_type.agenda.slug resp = app.post(fillslots_url, params={'slots': two_slots}) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'no more desk available' + assert resp.json['reason'] == 'no more desk available' # legacy + assert resp.json['err_class'] == 'no more desk available' + assert resp.json['err_desc'] == 'no more desk available' def test_booking_api(app, some_data, user): agenda = Agenda.objects.filter(label=u'Foo bar')[0] @@ -377,7 +381,9 @@ def test_booking_api(app, some_data, user): resp = app.post_json('/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id), params={'user_name': {'foo': 'bar'}}, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'invalid payload' + assert resp.json['reason'] == 'invalid payload' # legacy + assert resp.json['err_class'] == 'invalid payload' + assert resp.json['err_desc'] == 'invalid payload' assert len(resp.json['errors']) == 1 assert 'user_name' in resp.json['errors'] @@ -541,21 +547,29 @@ def test_booking_api_fillslots(app, some_data, user): params={'slots': events_ids, 'user_name': {'foo': 'bar'}}, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'invalid payload' + assert resp.json['reason'] == 'invalid payload' # legacy + assert resp.json['err_class'] == 'invalid payload' + assert resp.json['err_desc'] == 'invalid payload' assert len(resp.json['errors']) == 1 assert 'user_name' in resp.json['errors'] # empty or missing slots resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.id, params={'slots': []}, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'slots list cannot be empty' + assert resp.json['reason'] == 'slots list cannot be empty' # legacy + assert resp.json['err_class'] == 'slots list cannot be empty' + assert resp.json['err_desc'] == 'slots list cannot be empty' resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.id, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'slots list cannot be empty' + assert resp.json['reason'] == 'slots list cannot be empty' # legacy + assert resp.json['err_class'] == 'slots list cannot be empty' + assert resp.json['err_desc'] == 'slots list cannot be empty' # invalid slots format resp = app.post_json('/api/agenda/%s/fillslots/' % agenda.id, params={'slots': 'foobar'}, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'invalid payload' + assert resp.json['reason'] == 'invalid payload' # legacy + assert resp.json['err_class'] == 'invalid payload' + assert resp.json['err_desc'] == 'invalid payload' assert len(resp.json['errors']) == 1 assert 'slots' in resp.json['errors'] @@ -592,7 +606,9 @@ def test_booking_api_meeting(app, meetings_agenda, user): # try booking the same timeslot resp2 = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id)) assert resp2.json['err'] == 1 - assert resp2.json['reason'] == 'no more desk available' + assert resp2.json['reason'] == 'no more desk available' # legacy + assert resp2.json['err_class'] == 'no more desk available' + assert resp2.json['err_desc'] == 'no more desk available' # try booking another timeslot event_id = resp.json['data'][3]['id'] @@ -622,13 +638,17 @@ def test_booking_api_meeting_fillslots(app, meetings_agenda, user): # try booking the same timeslots resp2 = app.post('/api/agenda/%s/fillslots/' % agenda_id, params={'slots': slots}) assert resp2.json['err'] == 1 - assert resp2.json['reason'] == 'no more desk available' + assert resp2.json['reason'] == 'no more desk available' # legacy + assert resp2.json['err_class'] == 'no more desk available' + assert resp2.json['err_desc'] == 'no more desk available' # try booking partially free timeslots (one free, one busy) nonfree_slots = [resp.json['data'][0]['id'], resp.json['data'][2]['id']] resp2 = app.post('/api/agenda/%s/fillslots/' % agenda_id, params={'slots': nonfree_slots}) assert resp2.json['err'] == 1 - assert resp2.json['reason'] == 'no more desk available' + assert resp2.json['reason'] == 'no more desk available' # legacy + assert resp2.json['err_class'] == 'no more desk available' + assert resp2.json['err_desc'] == 'no more desk available' # booking other free timeslots free_slots = [resp.json['data'][3]['id'], resp.json['data'][2]['id']] @@ -650,7 +670,9 @@ def test_booking_api_meeting_fillslots(app, meetings_agenda, user): params={'slots': impossible_slots}, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'all slots must have the same meeting type id (1)' + assert resp.json['reason'] == 'all slots must have the same meeting type id (1)' # legacy + assert resp.json['err_class'] == 'all slots must have the same meeting type id (1)' + assert resp.json['err_desc'] == 'all slots must have the same meeting type id (1)' def test_booking_api_meeting_across_daylight_saving_time(app, meetings_agenda, user): meetings_agenda.maximal_booking_delay = 365 @@ -765,7 +787,9 @@ def test_booking_api_with_cancel_booking(app, some_data, user): params={'cancel_booking_id': first_booking.pk} ) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'cancel booking: booking already cancelled' + assert resp.json['reason'] == 'cancel booking: booking already cancelled' # legacy + assert resp.json['err_class'] == 'cancel booking: booking already cancelled' + assert resp.json['err_desc'] == 'cancel booking: booking already cancelled' assert Booking.objects.count() == 2 # Cancelling a non existent booking returns an error @@ -774,7 +798,9 @@ def test_booking_api_with_cancel_booking(app, some_data, user): params={'cancel_booking_id': '-1'} ) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'cancel booking: booking does no exist' + assert resp.json['reason'] == 'cancel booking: booking does no exist' # legacy + assert resp.json['err_class'] == 'cancel booking: booking does no exist' + assert resp.json['err_desc'] == 'cancel booking: booking does no exist' assert Booking.objects.count() == 2 # Cancelling booking with different count than new booking @@ -791,7 +817,9 @@ def test_booking_api_with_cancel_booking(app, some_data, user): params={'cancel_booking_id': booking_id, 'count': 1} ) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'cancel booking: count is different' + assert resp.json['reason'] == 'cancel booking: count is different' # legacy + assert resp.json['err_class'] == 'cancel booking: count is different' + assert resp.json['err_desc'] == 'cancel booking: count is different' assert Booking.objects.count() == 4 # cancel_booking_id must be an integer @@ -890,7 +918,9 @@ def test_soldout(app, some_data, user): app.authorization = ('Basic', ('john.doe', 'password')) resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event.id), status=200) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' def test_status(app, some_data, user): agenda_id = Agenda.objects.filter(label=u'Foo bar')[0].id @@ -989,7 +1019,9 @@ def test_waiting_list_booking(app, some_data, user): app.authorization = ('Basic', ('john.doe', 'password')) resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event.id), status=200) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' def test_accept_booking(app, some_data, user): agenda_id = Agenda.objects.filter(label=u'Foo bar')[0].id @@ -1034,17 +1066,23 @@ def test_multiple_booking_api(app, some_data, user): app.authorization = ('Basic', ('john.doe', 'password')) resp = app.post('/api/agenda/%s/fillslot/%s/?count=NaN' % (agenda.slug, event.id), status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == "invalid value for count (NaN)" + assert resp.json['reason'] == "invalid value for count (NaN)" # legacy + assert resp.json['err_class'] == "invalid value for count (NaN)" + assert resp.json['err_desc'] == "invalid value for count (NaN)" app.authorization = ('Basic', ('john.doe', 'password')) resp = app.post('/api/agenda/%s/fillslot/%s/?count=0' % (agenda.slug, event.id), status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == "count cannot be less than or equal to zero" + assert resp.json['reason'] == "count cannot be less than or equal to zero" # legacy + assert resp.json['err_class'] == "count cannot be less than or equal to zero" + assert resp.json['err_desc'] == "count cannot be less than or equal to zero" app.authorization = ('Basic', ('john.doe', 'password')) resp = app.post('/api/agenda/%s/fillslot/%s/?count=-3' % (agenda.slug, event.id), status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == "count cannot be less than or equal to zero" + assert resp.json['reason'] == "count cannot be less than or equal to zero" # legacy + assert resp.json['err_class'] == "count cannot be less than or equal to zero" + assert resp.json['err_desc'] == "count cannot be less than or equal to zero" resp = app.post('/api/agenda/%s/fillslot/%s/?count=3' % (agenda.slug, event.id)) Booking.objects.get(id=resp.json['booking_id']) @@ -1071,7 +1109,9 @@ def test_multiple_booking_api(app, some_data, user): # check waiting list overflow resp = app.post('/api/agenda/%s/fillslot/%s/?count=5' % (agenda.slug, event.id)) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' assert Event.objects.get(id=event.id).booked_places == 2 assert Event.objects.get(id=event.id).waiting_list == 5 @@ -1088,7 +1128,9 @@ def test_multiple_booking_api(app, some_data, user): resp = app.post('/api/agenda/%s/fillslot/%s/?count=5' % (agenda.slug, event.id)) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' resp = app.post('/api/agenda/%s/fillslot/%s/?count=3' % (agenda.slug, event.id)) assert resp.json['err'] == 0 @@ -1097,7 +1139,9 @@ def test_multiple_booking_api(app, some_data, user): resp = app.post('/api/agenda/%s/fillslot/%s/?count=3' % (agenda.slug, event.id)) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' resp = app.post('/api/agenda/%s/fillslot/%s/?count=2' % (agenda.slug, event.id)) assert resp.json['err'] == 0 @@ -1115,12 +1159,16 @@ def test_multiple_booking_api_fillslots(app, some_data, user): app.authorization = ('Basic', ('john.doe', 'password')) resp = app.post('/api/agenda/%s/fillslots/?count=NaN' % agenda.slug, params={'slots': slots}, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == "invalid value for count (NaN)" + assert resp.json['reason'] == "invalid value for count (NaN)" # legacy + assert resp.json['err_class'] == "invalid value for count (NaN)" + assert resp.json['err_desc'] == "invalid value for count (NaN)" resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 'NaN'}, status=400) assert resp.json['err'] == 1 - assert resp.json['reason'] == "invalid payload" + assert resp.json['reason'] == "invalid payload" # legacy + assert resp.json['err_class'] == "invalid payload" + assert resp.json['err_desc'] == "invalid payload" assert 'count' in resp.json['errors'] # get 3 places on 2 slots @@ -1164,7 +1212,9 @@ def test_multiple_booking_api_fillslots(app, some_data, user): resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 5}) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' for event in events: assert Event.objects.get(id=event.id).booked_places == 2 assert Event.objects.get(id=event.id).waiting_list == 5 @@ -1185,7 +1235,9 @@ def test_multiple_booking_api_fillslots(app, some_data, user): resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 5}) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 3}) @@ -1197,7 +1249,9 @@ def test_multiple_booking_api_fillslots(app, some_data, user): resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': 3}) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'sold out' + assert resp.json['reason'] == 'sold out' # legacy + assert resp.json['err_class'] == 'sold out' + assert resp.json['err_desc'] == 'sold out' resp = app.post('/api/agenda/%s/fillslots/' % agenda.slug, params={'slots': slots, 'count': '2'}) @@ -1308,7 +1362,9 @@ def test_agenda_meeting_api_multiple_desk(app, meetings_agenda, user): resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id)) assert Booking.objects.count() == 2 assert resp.json['err'] == 1 - assert resp.json['reason'] == 'no more desk available' + assert resp.json['reason'] == 'no more desk available' # legacy + assert resp.json['err_class'] == 'no more desk available' + assert resp.json['err_desc'] == 'no more desk available' # cancel first booking and retry resp = app.post(cancel_url) @@ -1345,7 +1401,9 @@ def test_agenda_meeting_api_multiple_desk(app, meetings_agenda, user): # try booking the same timeslot again and fail resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda_id, event_id)) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'no more desk available' + assert resp.json['reason'] == 'no more desk available' # legacy + assert resp.json['err_class'] == 'no more desk available' + assert resp.json['err_desc'] == 'no more desk available' # fill the agenda and make sure big O is O(1) for idx, event_data in enumerate(resp2.json['data'][2:10]): @@ -1397,7 +1455,9 @@ def test_agenda_meeting_api_fillslots_multiple_desks(app, meetings_agenda, user) # try booking again: no desk available resp = app.post(fillslots_url, params={'slots': slots}) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'no more desk available' + assert resp.json['reason'] == 'no more desk available' # legacy + assert resp.json['err_class'] == 'no more desk available' + assert resp.json['err_desc'] == 'no more desk available' assert get_free_places() == start_free_places - len(slots) # cancel desk 1 booking @@ -1416,7 +1476,9 @@ def test_agenda_meeting_api_fillslots_multiple_desks(app, meetings_agenda, user) # try booking the 3 slots again: no desk available, one slot is not fully available resp = app.post(fillslots_url, params={'slots': slots}) assert resp.json['err'] == 1 - assert resp.json['reason'] == 'no more desk available' + assert resp.json['reason'] == 'no more desk available' # legacy + assert resp.json['err_class'] == 'no more desk available' + assert resp.json['err_desc'] == 'no more desk available' # cancel last signel slot booking, desk1 will be free resp = app.post(cancel_url) diff --git a/tests/test_api_utils.py b/tests/test_api_utils.py new file mode 100644 index 00000000..365fa094 --- /dev/null +++ b/tests/test_api_utils.py @@ -0,0 +1,15 @@ +import pytest + +from chrono.api.utils import Response + + +@pytest.mark.parametrize('data, expected', [ + (None, None), + ({}, {}), + ({'reason': 'foo'}, {'reason': 'foo'}), + ({'err_class': 'foo'}, {'err_class': 'foo', 'reason': 'foo'}), + ({'bar': 'foo'}, {'bar': 'foo'}), +]) +def test_response_data(data, expected): + resp = Response(data=data) + assert resp.data == expected