entityManager = $this->createMock(EntityManagerInterface::class); $this->logger = $this->createMock(LoggerInterface::class); } public function testUpdateLocalUserSyncsUserAndTeamerDataFromBusPro(): void { $user = (new User()) ->setFirstName('Old') ->setLastName('Name') ->setEmail('old@example.com') ->setBusProAddressId(1) ->setBusProPersonId(2) ; $teamer = (new Teamer()) ->setFirstName('OldTeamer') ->setLastName('OldLastname') ->setAcademicTitle('Dr.') ->setSalutation('Herr') ->setGender('M') ->setDateOfBirth(new \DateTimeImmutable('1990-01-01')) ->setAddress((new Address())->setStreet('Old Street')->setPostCode('11111')->setCity('Old City')->setCountry('DE')) ->setCommunication((new Communication())->setPhone('123')->setMobile('456')->setEmail('old@example.com')) ; $user->setTeamer($teamer); $profileResponse = $this->createProfileResponse(); $this->entityManager ->expects($this->once()) ->method('flush'); $handler = new UserDataHandler($this->entityManager, $this->logger); $handler->updateLocalUser( $user, $profileResponse, ['ROLE_TEAMER'], true, ['team' => ['selected' => true]], ['ABC'], ); $this->assertSame('New', $user->getFirstName()); $this->assertSame('Lastname', $user->getLastName()); $this->assertSame('new@example.com', $user->getEmail()); $this->assertSame(['ABC'], $user->getHotelCodes()); $this->assertTrue($user->hasRole('ROLE_TEAMER')); $this->assertSame('New', $teamer->getFirstName()); $this->assertSame('Lastname', $teamer->getLastName()); $this->assertSame('Prof.', $teamer->getAcademicTitle()); $this->assertSame('Frau', $teamer->getSalutation()); $this->assertSame('F', $teamer->getGender()); $this->assertEquals(new \DateTimeImmutable('1995-12-24'), $teamer->getDateOfBirth()); $this->assertSame('New Street 123', $teamer->getAddress()?->getStreet()); $this->assertSame('54321', $teamer->getAddress()?->getPostCode()); $this->assertSame('New City', $teamer->getAddress()?->getCity()); $this->assertSame('AT', $teamer->getAddress()?->getCountry()); $this->assertSame('999', $teamer->getCommunication()?->getPhone()); $this->assertSame('888', $teamer->getCommunication()?->getMobile()); $this->assertSame('new@example.com', $teamer->getCommunication()?->getEmail()); $this->assertSame(['team' => ['selected' => true]], $teamer->getCrmSelections()); } public function testFindLocalUserFallsBackToUniqueEmailAndRefreshesBusProIds(): void { $user = (new User()) ->setFirstName('First') ->setLastName('Last') ->setEmail('new@example.com') ->setBusProAddressId(1) ->setBusProPersonId(2) ; $profileResponse = $this->createProfileResponse(); $repository = $this->createMock(ObjectRepository::class); $repository ->expects($this->once()) ->method('findOneBy') ->with([ 'busProAddressId' => 200, 'busProPersonId' => 100, ]) ->willReturn(null); $repository ->expects($this->once()) ->method('findBy') ->with([ 'email' => 'new@example.com', ]) ->willReturn([$user]); $this->entityManager ->expects($this->once()) ->method('getRepository') ->with(User::class) ->willReturn($repository); $handler = new UserDataHandler($this->entityManager, $this->logger); $resolvedUser = $handler->findLocalUser($profileResponse); $this->assertSame($user, $resolvedUser); $this->assertSame(200, $user->getBusProAddressId()); $this->assertSame(100, $user->getBusProPersonId()); } public function testFindLocalUserSkipsEmailFallbackWhenMultipleUsersExist(): void { $userA = (new User()) ->setFirstName('A') ->setLastName('A') ->setEmail('new@example.com') ->setBusProAddressId(1) ->setBusProPersonId(2) ; $userB = (new User()) ->setFirstName('B') ->setLastName('B') ->setEmail('new@example.com') ->setBusProAddressId(3) ->setBusProPersonId(4) ; $profileResponse = $this->createProfileResponse(); $repository = $this->createMock(ObjectRepository::class); $repository ->expects($this->once()) ->method('findOneBy') ->willReturn(null); $repository ->expects($this->once()) ->method('findBy') ->willReturn([$userA, $userB]); $this->entityManager ->expects($this->once()) ->method('getRepository') ->with(User::class) ->willReturn($repository); $this->logger ->expects($this->once()) ->method('warning') ->with( 'Unable to match local user by email: multiple users found', [ 'email' => 'new@example.com', 'count' => 2, ], ); $handler = new UserDataHandler($this->entityManager, $this->logger); $resolvedUser = $handler->findLocalUser($profileResponse); $this->assertNull($resolvedUser); } public function testFindLocalUserReturnsUniqueEmailMatchWhenBusProIdsAreMissing(): void { $user = (new User()) ->setFirstName('First') ->setLastName('Last') ->setEmail('new@example.com') ->setBusProAddressId(1) ->setBusProPersonId(2) ; $profileResponse = $this->createProfileResponse(null, null); $repository = $this->createMock(ObjectRepository::class); $repository ->expects($this->once()) ->method('findOneBy') ->with([ 'busProAddressId' => null, 'busProPersonId' => null, ]) ->willReturn(null); $repository ->expects($this->once()) ->method('findBy') ->with([ 'email' => 'new@example.com', ]) ->willReturn([$user]); $this->entityManager ->expects($this->once()) ->method('getRepository') ->with(User::class) ->willReturn($repository); $this->logger ->expects($this->once()) ->method('warning') ->with( 'Skip BusPro ID refresh: profile response missing IDs', [ 'user_id' => null, 'user_email' => 'new@example.com', 'bus_pro_address_id' => null, 'bus_pro_person_id' => null, ], ); $handler = new UserDataHandler($this->entityManager, $this->logger); $resolvedUser = $handler->findLocalUser($profileResponse); $this->assertSame($user, $resolvedUser); $this->assertSame(1, $user->getBusProAddressId()); $this->assertSame(2, $user->getBusProPersonId()); } private function createProfileResponse(?int $addressId = 200, ?int $personId = 100): ProfileResponse { $address = (new BusProAddress()) ->setStreet('New Street 123') ->setPostCode('54321') ->setCity('New City') ->setCountry('AT') ; $communication = (new BusProCommunication()) ->setPhone('999') ->setMobile('888') ->setEmail('new@example.com') ; return (new ProfileResponse()) ->setAddressId($addressId) ->setPersonId($personId) ->setFirstName('New') ->setName('Lastname') ->setTitle('Prof.') ->setSalutation('Frau') ->setGender('F') ->setDateOfBirth(new \DateTimeImmutable('1995-12-24')) ->setAddress($address) ->setCommunication($communication) ; } }