Files
azionelab/backend/bookings/test_api.py

172 lines
6.6 KiB
Python

from datetime import timedelta
from django.core import mail
from django.urls import reverse
from django.utils import timezone
from rest_framework import status
from rest_framework.test import APITestCase
from bookings.models import Reservation
from bookings.services import generate_confirmation_token
from shows.models import Performance, Show, Venue
class BookingApiTests(APITestCase):
def setUp(self):
self.show = Show.objects.create(
title="Open Stage",
slug="open-stage-api",
summary="A contemporary theatre performance.",
description="Full public show description.",
is_published=True,
)
self.hidden_show = Show.objects.create(
title="Hidden Stage",
slug="hidden-stage-api",
is_published=False,
)
self.venue = Venue.objects.create(
name="AzioneLab Theatre",
slug="azionelab-theatre-api",
address="Via Example 1",
city="Rome",
)
self.performance = Performance.objects.create(
show=self.show,
venue=self.venue,
starts_at=timezone.now() + timedelta(days=7),
room_capacity=3,
)
Performance.objects.create(
show=self.hidden_show,
venue=self.venue,
starts_at=timezone.now() + timedelta(days=8),
room_capacity=5,
)
def test_show_list_returns_published_shows(self):
response = self.client.get(reverse("api-show-list"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
self.assertEqual(response.data["results"][0]["slug"], self.show.slug)
def test_show_detail_returns_public_performances(self):
response = self.client.get(reverse("api-show-detail", kwargs={"slug": self.show.slug}))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["slug"], self.show.slug)
self.assertEqual(len(response.data["performances"]), 1)
self.assertEqual(response.data["performances"][0]["id"], self.performance.id)
self.assertEqual(response.data["performances"][0]["available_seats"], 3)
def test_reservation_creation_success(self):
response = self.client.post(
reverse("api-reservation-create", kwargs={"performance_id": self.performance.id}),
{
"name": "Maria Rossi",
"email": "maria@example.com",
"phone": "+390600000000",
"party_size": 2,
"notes": "Front row if possible.",
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data["status"], Reservation.Status.PENDING)
self.assertEqual(response.data["performance"], self.performance.id)
self.assertNotIn("token", response.data)
self.assertNotIn("email", response.data)
self.assertEqual(Reservation.objects.count(), 1)
self.assertEqual(len(mail.outbox), 1)
self.assertIn("/api/reservations/confirm/?token=", mail.outbox[0].body)
def test_reservation_creation_with_insufficient_seats(self):
response = self.client.post(
reverse("api-reservation-create", kwargs={"performance_id": self.performance.id}),
{
"name": "Maria Rossi",
"email": "maria@example.com",
"party_size": 4,
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
self.assertEqual(response.data["status"], "booking_unavailable")
self.assertEqual(Reservation.objects.count(), 0)
def test_confirmation_success(self):
reservation = self.create_reservation()
_, raw_token = generate_confirmation_token(reservation)
response = self.client.post(
reverse("api-reservation-confirm"),
{"token": raw_token},
format="json",
)
reservation.refresh_from_db()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["reservation_id"], reservation.id)
self.assertEqual(response.data["status"], Reservation.Status.CONFIRMED)
self.assertEqual(response.data["party_size"], reservation.party_size)
self.assertTrue(response.data["qr_code_url"].startswith("/api/check-ins/preview/?token="))
self.assertNotIn("token", response.data)
self.assertTrue(response.data["qr_code_image"].startswith("data:image/png;base64,"))
self.assertEqual(reservation.status, Reservation.Status.CONFIRMED)
def test_confirmation_success_via_email_link_get_request(self):
reservation = self.create_reservation(email="get@example.com")
_, raw_token = generate_confirmation_token(reservation)
response = self.client.get(
reverse("api-reservation-confirm"),
{"token": raw_token},
)
reservation.refresh_from_db()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["reservation_id"], reservation.id)
self.assertEqual(reservation.status, Reservation.Status.CONFIRMED)
def test_confirmation_with_invalid_token(self):
response = self.client.post(
reverse("api-reservation-confirm"),
{"token": "invalid-token"},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(response.data["status"], "invalid_token")
def test_duplicate_confirmation_behavior(self):
reservation = self.create_reservation()
_, raw_token = generate_confirmation_token(reservation)
first_response = self.client.post(
reverse("api-reservation-confirm"),
{"token": raw_token},
format="json",
)
second_response = self.client.post(
reverse("api-reservation-confirm"),
{"token": raw_token},
format="json",
)
self.assertEqual(first_response.status_code, status.HTTP_200_OK)
self.assertEqual(second_response.status_code, status.HTTP_409_CONFLICT)
self.assertEqual(second_response.data["status"], "already_confirmed")
def create_reservation(self, **overrides):
data = {
"performance": self.performance,
"name": "Maria Rossi",
"email": "maria@example.com",
"party_size": 1,
}
data.update(overrides)
return Reservation.objects.create(**data)