from datetime import timedelta from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.db import IntegrityError from django.test import TestCase from django.utils import timezone from bookings.models import Reservation, ReservationToken from checkins.models import CheckIn from shows.models import Performance, Show, Venue class DomainModelTests(TestCase): def setUp(self): self.show = Show.objects.create( title="Open Stage", slug="open-stage", is_published=True, ) self.venue = Venue.objects.create( name="AzioneLab Theatre", slug="azionelab-theatre", 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=10, manually_occupied_seats=2, additional_seats=3, ) def create_reservation(self, **overrides): data = { "performance": self.performance, "name": "Maria Rossi", "email": "maria@example.com", "party_size": 2, } data.update(overrides) return Reservation.objects.create(**data) def test_available_seats_count_only_confirmed_reservations(self): self.create_reservation(party_size=4) self.create_reservation( party_size=3, status=Reservation.Status.CONFIRMED, confirmed_at=timezone.now(), ) self.assertEqual(self.performance.configured_capacity, 11) self.assertEqual(self.performance.confirmed_seats(), 3) self.assertEqual(self.performance.available_seats(), 8) def test_reservation_lifecycle_pending_to_confirmed_with_token(self): reservation = self.create_reservation() token, raw_token = ReservationToken.create_token( reservation=reservation, purpose=ReservationToken.Purpose.CONFIRMATION, expires_at=timezone.now() + timedelta(hours=2), ) reservation.confirm_with_token(raw_token) reservation.refresh_from_db() token.refresh_from_db() self.assertEqual(reservation.status, Reservation.Status.CONFIRMED) self.assertIsNotNone(reservation.confirmed_at) self.assertIsNotNone(token.used_at) def test_only_pending_reservations_can_be_confirmed(self): reservation = self.create_reservation(status=Reservation.Status.CANCELLED) token, raw_token = ReservationToken.create_token( reservation=reservation, purpose=ReservationToken.Purpose.CONFIRMATION, expires_at=timezone.now() + timedelta(hours=2), ) with self.assertRaises(ValidationError): reservation.confirm_with_token(raw_token) token.refresh_from_db() self.assertIsNone(token.used_at) def test_capacity_configuration_rejects_impossible_manual_occupancy(self): performance = Performance( show=self.show, venue=self.venue, starts_at=timezone.now() + timedelta(days=8), room_capacity=5, additional_seats=1, manually_occupied_seats=7, ) with self.assertRaises(ValidationError): performance.full_clean() def test_check_in_requires_confirmed_reservation(self): reservation = self.create_reservation() user = get_user_model().objects.create_user(username="staff", password="test") check_in = CheckIn(reservation=reservation, checked_in_by=user) with self.assertRaises(ValidationError): check_in.full_clean() def test_reservation_cannot_be_checked_in_twice(self): reservation = self.create_reservation( status=Reservation.Status.CONFIRMED, confirmed_at=timezone.now(), ) user = get_user_model().objects.create_user(username="staff", password="test") CheckIn.objects.create(reservation=reservation, checked_in_by=user) with self.assertRaises((IntegrityError, ValidationError)): CheckIn.objects.create(reservation=reservation, checked_in_by=user)