diff --git a/backend/bookings/test_api.py b/backend/bookings/test_api.py index edec06a..aa22fca 100644 --- a/backend/bookings/test_api.py +++ b/backend/bookings/test_api.py @@ -1,12 +1,14 @@ from datetime import timedelta from unittest.mock import patch +from django.contrib.auth import get_user_model +from django.core.cache import cache from django.core import mail from django.urls import reverse from django.test.utils import override_settings from django.utils import timezone from rest_framework import status -from rest_framework.test import APITestCase +from rest_framework.test import APIClient, APITestCase from bookings.models import Reservation from bookings.services import generate_confirmation_token @@ -16,6 +18,7 @@ from shows.models import Performance, Show, Venue class BookingApiTests(APITestCase): def setUp(self): + cache.clear() self.show = Show.objects.create( title="Open Stage", slug="open-stage-api", @@ -90,6 +93,48 @@ class BookingApiTests(APITestCase): mail.outbox[0].body, ) + @override_settings(SITE_BASE_URL="https://tickets.azionelab.example") + def test_reservation_creation_allows_anonymous_post_without_csrf(self): + csrf_client = APIClient(enforce_csrf_checks=True) + + with self.captureOnCommitCallbacks(execute=True): + response = csrf_client.post( + reverse("api-reservation-create", kwargs={"performance_id": self.performance.id}), + { + "name": "Maria Rossi", + "email": "maria@example.com", + "party_size": 2, + }, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data["status"], Reservation.Status.PENDING) + + @override_settings(SITE_BASE_URL="https://tickets.azionelab.example") + def test_reservation_creation_ignores_session_csrf_for_public_endpoint(self): + csrf_client = APIClient(enforce_csrf_checks=True) + user = get_user_model().objects.create_user( + username="box-office", + email="staff@example.com", + password="test-pass-123", + ) + csrf_client.force_login(user) + + with self.captureOnCommitCallbacks(execute=True): + response = csrf_client.post( + reverse("api-reservation-create", kwargs={"performance_id": self.performance.id}), + { + "name": "Maria Rossi", + "email": "maria@example.com", + "party_size": 2, + }, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data["status"], Reservation.Status.PENDING) + def test_reservation_creation_schedules_email_after_commit(self): with self.captureOnCommitCallbacks(execute=False) as callbacks: response = self.client.post( diff --git a/backend/bookings/views.py b/backend/bookings/views.py index c7e4e1c..a616b52 100644 --- a/backend/bookings/views.py +++ b/backend/bookings/views.py @@ -1,6 +1,7 @@ from django.shortcuts import get_object_or_404 from rest_framework import status -from rest_framework.decorators import api_view, throttle_classes +from rest_framework.decorators import api_view, authentication_classes, permission_classes, throttle_classes +from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.throttling import AnonRateThrottle @@ -35,6 +36,8 @@ class ReservationConfirmThrottle(AnonRateThrottle): @api_view(["POST"]) +@authentication_classes([]) +@permission_classes([AllowAny]) @throttle_classes([ReservationCreateThrottle]) def create_reservation(request, performance_id): get_object_or_404(Performance, pk=performance_id, show__is_published=True)