apiClient = $this->createMock(ApiClient::class); $this->crypt = $this->createMock(Crypt::class); $this->logger = $this->createMock(LoggerInterface::class); $this->service = new ParticipantDataPrefiller( $this->apiClient, $this->crypt, $this->logger ); } public function testPrepopulateApplicantWithCompletePersonalData(): void { $user = $this->createUser('test@example.com', 'encrypted_password'); $applicant = new ParticipantDto(); $applicant->index = 0; $personalData = $this->createCompletePersonalData(); $this->crypt->expects($this->once()) ->method('decrypt') ->with('encrypted_password') ->willReturn('decrypted_password'); $this->apiClient->expects($this->once()) ->method('getPersonalData') ->with('test@example.com', 'decrypted_password') ->willReturn($personalData); $result = $this->service->prefillApplicantFromUser($user, $applicant); // BPN API IDs for linking to existing records $this->assertSame(12345, $result->addressId); $this->assertSame(67890, $result->personId); $this->assertSame('John', $result->firstName); $this->assertSame('Doe', $result->lastName); $this->assertSame('Dr.', $result->title); $this->assertSame('M', $result->gender); $this->assertSame('DE', $result->nationality); $this->assertSame('test@example.com', $result->email); $this->assertSame('+49123456789', $result->mobile); $this->assertSame('180', $result->height); $this->assertSame('75', $result->weight); $this->assertSame('42', $result->shoeSize); $this->assertSame('No smoking room please', $result->remarksRoom); $this->assertSame('AB-CD-1234', $result->licensePlate); $this->assertNotNull($result->dateOfBirth); $this->assertSame('1990-05-15', $result->dateOfBirth->format('Y-m-d')); $this->assertNotNull($result->address); $this->assertSame('Main Street 123', $result->address->street); $this->assertSame('12345', $result->address->postCode); $this->assertSame('Berlin', $result->address->city); $this->assertSame('DE', $result->address->country); $this->assertSame('Mitte', $result->address->district); } public function testPrepopulateApplicantWithPartialPersonalData(): void { $user = $this->createUser('test@example.com', 'encrypted_password'); $applicant = new ParticipantDto(); $applicant->index = 0; $personalData = $this->createPartialPersonalData(); $this->crypt->expects($this->once()) ->method('decrypt') ->willReturn('decrypted_password'); $this->apiClient->expects($this->once()) ->method('getPersonalData') ->willReturn($personalData); $result = $this->service->prefillApplicantFromUser($user, $applicant); // Required fields should be populated $this->assertSame('Jane', $result->firstName); $this->assertSame('Smith', $result->lastName); $this->assertSame('jane@example.com', $result->email); // Optional fields should be null (except nationality which defaults to 'D') $this->assertNull($result->title); $this->assertNull($result->gender); $this->assertSame('D', $result->nationality); $this->assertNull($result->mobile); $this->assertNull($result->height); $this->assertNull($result->weight); $this->assertNull($result->shoeSize); $this->assertNull($result->remarksRoom); $this->assertNull($result->licensePlate); } public function testPrepopulateApplicantWithApiNotificationError(): void { $user = $this->createUser('test@example.com', 'encrypted_password'); $applicant = new ParticipantDto(); $applicant->index = 0; $notification = new Notification(401, 'Authentication failed'); $this->crypt->expects($this->once()) ->method('decrypt') ->willReturn('decrypted_password'); $this->apiClient->expects($this->once()) ->method('getPersonalData') ->willReturn($notification); $this->logger->expects($this->once()) ->method('warning') ->with('Failed to fetch personal data for prepopulation', [ 'email' => 'test@example.com', 'code' => 401, 'message' => 'Authentication failed', ]); $this->logger->expects($this->never()) ->method('info'); $result = $this->service->prefillApplicantFromUser($user, $applicant); // Should return unchanged applicant $this->assertNull($result->firstName); $this->assertNull($result->lastName); $this->assertNull($result->email); } public function testPrepopulateApplicantWithDecryptionException(): void { $user = $this->createUser('test@example.com', 'encrypted_password'); $applicant = new ParticipantDto(); $applicant->index = 0; $this->crypt->expects($this->once()) ->method('decrypt') ->willThrowException(new \RuntimeException('Decryption failed')); $this->apiClient->expects($this->never()) ->method('getPersonalData'); $this->logger->expects($this->once()) ->method('error') ->with('Exception during applicant prepopulation', [ 'error' => 'Decryption failed', 'email' => 'test@example.com', ]); $result = $this->service->prefillApplicantFromUser($user, $applicant); // Should return unchanged applicant $this->assertNull($result->firstName); $this->assertNull($result->lastName); $this->assertNull($result->email); } public function testPrepopulateApplicantWithApiException(): void { $user = $this->createUser('test@example.com', 'encrypted_password'); $applicant = new ParticipantDto(); $applicant->index = 0; $this->crypt->expects($this->once()) ->method('decrypt') ->willReturn('decrypted_password'); $this->apiClient->expects($this->once()) ->method('getPersonalData') ->willThrowException(new \RuntimeException('API connection failed')); $this->logger->expects($this->once()) ->method('error') ->with('Exception during applicant prepopulation', [ 'error' => 'API connection failed', 'email' => 'test@example.com', ]); $result = $this->service->prefillApplicantFromUser($user, $applicant); // Should return unchanged applicant $this->assertNull($result->firstName); $this->assertNull($result->lastName); $this->assertNull($result->email); } public function testPrepopulatePreservesBookingSpecificFields(): void { $user = $this->createUser('test@example.com', 'encrypted_password'); $applicant = new ParticipantDto(); $applicant->index = 0; $applicant->assignedRoomId = 42; $applicant->bulkInsuranceBooking = true; $personalData = $this->createCompletePersonalData(); $this->crypt->expects($this->once()) ->method('decrypt') ->willReturn('decrypted_password'); $this->apiClient->expects($this->once()) ->method('getPersonalData') ->willReturn($personalData); $result = $this->service->prefillApplicantFromUser($user, $applicant); // Personal data should be updated $this->assertSame('John', $result->firstName); $this->assertSame('Doe', $result->lastName); // Booking-specific fields should be preserved $this->assertSame(42, $result->assignedRoomId); $this->assertTrue($result->bulkInsuranceBooking); } public function testShouldPrepopulateApplicantOnlyWhenFresh(): void { $fresh = new ParticipantDto(); $fresh->firstName = ''; $filled = new ParticipantDto(); $filled->firstName = 'Already set'; $this->assertTrue($this->service->shouldPrefillApplicant($fresh)); $this->assertFalse($this->service->shouldPrefillApplicant($filled)); } public function testDummyTokenMatchesOnlyInCreateMode(): void { $participant = new ParticipantDto(); $participant->lastName = ParticipantDataPrefiller::TOKEN; $this->assertTrue($this->service->isDummyDataFillRequested($participant, BookingDto::MODE_CREATE)); $this->assertFalse($this->service->isDummyDataFillRequested($participant, BookingDto::MODE_EDIT)); } public function testFillDummyParticipantSetsGeneratedData(): void { CarbonImmutable::setTestNow(CarbonImmutable::create(2026, 1, 15, 14, 30)); $participant = new ParticipantDto(); $this->service->fillDummyParticipant($participant, 0); $this->assertSame('Vorname 1', $participant->firstName); $this->assertSame('Muster 1 14:30', $participant->lastName); $this->assertSame('0171/111111', $participant->mobile); $this->assertSame('teilnehmer.in1@example.com', $participant->email); $this->assertSame('2006-01-15', $participant->dateOfBirth?->format('Y-m-d')); $this->assertSame('Musterstr. 123', $participant->address?->street); CarbonImmutable::setTestNow(); } private function createUser(string $email, string $encryptedPassword): User { $user = new User($email); $user->setPassword($encryptedPassword); return $user; } private function createCompletePersonalData(): PersonalData { $personalData = new PersonalData(); $personalData->addressId = 12345; $personalData->personId = 67890; $personalData->firstName = 'John'; $personalData->name = 'Doe'; $personalData->title = 'Dr.'; $personalData->gender = 'M'; $personalData->nationality = 'DE'; $personalData->dateOfBirth = new \DateTimeImmutable('1990-05-15'); $personalData->height = '180'; $personalData->weight = '75'; $personalData->shoeSize = '42'; $personalData->remarksRoom = 'No smoking room please'; $personalData->licensePlate = 'AB-CD-1234'; $personalData->communication = new Communication(); $personalData->communication->email = 'test@example.com'; $personalData->communication->mobile = '+49123456789'; $personalData->address = new Address(); $personalData->address->street = 'Main Street 123'; $personalData->address->postCode = '12345'; $personalData->address->city = 'Berlin'; $personalData->address->country = 'DE'; $personalData->address->district = 'Mitte'; return $personalData; } private function createPartialPersonalData(): PersonalData { $personalData = new PersonalData(); $personalData->firstName = 'Jane'; $personalData->name = 'Smith'; $personalData->dateOfBirth = new \DateTimeImmutable('1995-03-20'); $personalData->communication = new Communication(); $personalData->communication->email = 'jane@example.com'; $personalData->address = new Address(); $personalData->address->street = 'Second Street 45'; $personalData->address->postCode = '54321'; $personalData->address->city = 'Munich'; $personalData->address->country = 'DE'; return $personalData; } }