generated from bisco/codex-bootstrap
feat: add email confirmation and QR generation
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core import mail
|
||||
from django.db import connection
|
||||
from django.test import TestCase
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
from django.test.utils import override_settings
|
||||
from django.utils import timezone
|
||||
|
||||
from bookings.models import Reservation, ReservationToken
|
||||
from bookings.qr import build_check_in_preview_url, generate_check_in_qr_base64
|
||||
from bookings.services import (
|
||||
AlreadyConfirmedReservation,
|
||||
ExpiredToken,
|
||||
@@ -56,6 +60,33 @@ class BookingServiceTests(TestCase):
|
||||
)
|
||||
self.assertNotIn("maria@example.com", result.raw_confirmation_token)
|
||||
|
||||
@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend")
|
||||
def test_create_pending_reservation_sends_confirmation_email(self):
|
||||
result = create_pending_reservation(
|
||||
performance_id=self.performance.id,
|
||||
name="Maria Rossi",
|
||||
email="maria@example.com",
|
||||
party_size=1,
|
||||
)
|
||||
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].to, ["maria@example.com"])
|
||||
self.assertIn(result.raw_confirmation_token, mail.outbox[0].body)
|
||||
self.assertIn("/api/reservations/confirm/?token=", mail.outbox[0].body)
|
||||
|
||||
@patch("bookings.emailing.send_mail", side_effect=RuntimeError("SMTP down"))
|
||||
def test_create_pending_reservation_logs_email_failure_without_crashing(self, mocked_send_mail):
|
||||
result = create_pending_reservation(
|
||||
performance_id=self.performance.id,
|
||||
name="Maria Rossi",
|
||||
email="maria@example.com",
|
||||
party_size=1,
|
||||
)
|
||||
|
||||
self.assertEqual(result.reservation.status, Reservation.Status.PENDING)
|
||||
self.assertEqual(Reservation.objects.count(), 1)
|
||||
mocked_send_mail.assert_called_once()
|
||||
|
||||
def test_generate_confirmation_token_returns_raw_token_once(self):
|
||||
reservation = self.create_reservation()
|
||||
|
||||
@@ -83,6 +114,38 @@ class BookingServiceTests(TestCase):
|
||||
ReservationToken.hash_token(result.raw_check_in_token),
|
||||
)
|
||||
self.assertEqual(result.available_seats, 1)
|
||||
self.assertEqual(
|
||||
result.qr_code_url,
|
||||
build_check_in_preview_url(result.raw_check_in_token),
|
||||
)
|
||||
self.assertTrue(result.qr_code_image.startswith("data:image/png;base64,"))
|
||||
|
||||
def test_qr_code_is_generated_for_confirmed_reservation(self):
|
||||
reservation = self.create_reservation(
|
||||
status=Reservation.Status.CONFIRMED,
|
||||
confirmed_at=timezone.now(),
|
||||
)
|
||||
raw_check_in_token = "opaque-check-in-token"
|
||||
|
||||
qr_code_image = generate_check_in_qr_base64(
|
||||
reservation=reservation,
|
||||
raw_check_in_token=raw_check_in_token,
|
||||
)
|
||||
|
||||
self.assertTrue(qr_code_image.startswith("data:image/png;base64,"))
|
||||
self.assertGreater(len(qr_code_image), len("data:image/png;base64,"))
|
||||
|
||||
def test_qr_code_is_not_generated_for_pending_reservation(self):
|
||||
reservation = self.create_reservation()
|
||||
|
||||
with self.assertRaisesMessage(
|
||||
ValueError,
|
||||
"QR codes are available only for confirmed reservations.",
|
||||
):
|
||||
generate_check_in_qr_base64(
|
||||
reservation=reservation,
|
||||
raw_check_in_token="opaque-check-in-token",
|
||||
)
|
||||
|
||||
def test_confirmation_fails_when_capacity_is_exhausted(self):
|
||||
Reservation.objects.create(
|
||||
|
||||
Reference in New Issue
Block a user