from datetime import datetime # Test doubles implement the same interfaces as production code class FakeBookingRepository(BookingRepository): """In-memory repository for testing - no HTTP, no database.""" def __init__(self): self._bookings: dict[str, Booking] = {} def find_by_id(self, booking_id: BookingId) -> Booking | None: return self._bookings.get(booking_id.value) def save(self, booking: Booking) -> None: self._bookings[booking.id.value] = booking class FakePaymentAdapter(PaymentAdapter): """Predictable payment adapter for testing - no Stripe SDK.""" def __init__(self, should_succeed: bool = True): self._should_succeed = should_succeed self.charges: list[PaymentRequest] = [] def charge(self, request: PaymentRequest) -> PaymentResult: self.charges.append(request) return PaymentResult( transaction_id="fake-txn-123", success=self._should_succeed, ) def test_given_valid_booking_command_when_creating_booking_then_charges_payment_and_persists(): """ Test Doc: - Why: Verifies the core booking creation flow orchestrates payment and persistence correctly - Contract: create_booking charges payment via adapter, then saves confirmed booking via repository - Usage Notes: Inject FakeBookingRepository and FakePaymentAdapter; command requires user_id, departure, origin, destination - Quality Contribution: Critical path - booking creation is the primary user journey - Worked Example: SYD→MEL booking for user-1 on 2025-06-15 → payment charged, booking saved with same ID """ # Arrange - construct fakes and inject into real service fake_repo = FakeBookingRepository() fake_payments = FakePaymentAdapter(should_succeed=True) service = BookingApplicationServiceImpl( bookings=fake_repo, payments=fake_payments, ) command = CreateBookingCommand( user_id="user-1", departure=datetime(2025, 6, 15, 10, 0), origin="SYD", destination="MEL", ) # Act booking = service.create_booking(command) # Assert - payment was charged with correct booking assert len(fake_payments.charges) == 1 assert fake_payments.charges[0].booking_id == booking.id # Assert - booking was persisted saved = fake_repo.find_by_id(booking.id) assert saved is not None assert saved.user_id == "user-1"