""" Tests that booking status updates succeed even when the email service fails. This covers the fix: email send is wrapped in try/except so SMTP failure does not block the booking status change. """ from unittest.mock import AsyncMock, patch from tests.conftest import SAMPLE_BOOKING, BOOKING_ID, ADMIN_ID async def test_confirm_booking_succeeds_when_email_fails(admin_client, mock_db): """Admin confirming a booking must succeed even if the email service raises.""" mock_db.fetchrow = AsyncMock(side_effect=[ SAMPLE_BOOKING, # UPDATE bookings RETURNING * {"email": "client@test.com"}, # SELECT email FROM profiles {"date": "2026-06-01", "start_time": "10:00:00"}, # SELECT from time_slots SAMPLE_BOOKING, # get_booking at the end ]) mock_db.execute = AsyncMock(return_value="INSERT 1") with patch("app.services.email_service.send_booking_confirmed", new_callable=AsyncMock, side_effect=Exception("SMTP unavailable")): r = await admin_client.patch( f"/api/v1/admin/bookings/{BOOKING_ID}", json={"status": "confirmed"}, ) # Must succeed despite the email failure assert r.status_code == 200 assert r.json()["success"] is True async def test_cancel_booking_succeeds_when_email_fails(admin_client, mock_db): """Same resilience for cancellation emails.""" pending = {**SAMPLE_BOOKING, "status": "pending"} mock_db.fetchrow = AsyncMock(side_effect=[ pending, {"email": "client@test.com"}, {"date": "2026-06-01", "start_time": "10:00:00"}, pending, ]) mock_db.execute = AsyncMock(return_value="INSERT 1") with patch("app.services.email_service.send_booking_cancelled", new_callable=AsyncMock, side_effect=Exception("SMTP unavailable")): r = await admin_client.patch( f"/api/v1/admin/bookings/{BOOKING_ID}", json={"status": "cancelled"}, ) assert r.status_code == 200 assert r.json()["success"] is True async def test_confirm_booking_with_no_email_address(admin_client, mock_db): """Booking with no guest_email and no user_id must still update successfully.""" no_email_booking = {**SAMPLE_BOOKING, "guest_email": None, "user_id": None} mock_db.fetchrow = AsyncMock(side_effect=[ no_email_booking, # UPDATE RETURNING * no_email_booking, # get_booking at the end ]) mock_db.execute = AsyncMock(return_value="INSERT 1") r = await admin_client.patch( f"/api/v1/admin/bookings/{BOOKING_ID}", json={"status": "confirmed"}, ) assert r.status_code == 200 async def test_confirm_booking_not_found(admin_client, mock_db): mock_db.fetchrow = AsyncMock(return_value=None) r = await admin_client.patch( f"/api/v1/admin/bookings/00000000-0000-0000-0000-000000000000", json={"status": "confirmed"}, ) assert r.status_code == 404 assert r.json()["error"]["code"] == "BOOKING_NOT_FOUND"