diff --git a/composer.json b/composer.json index b6eb4788..6d793412 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ }, "autoload-dev": { "psr-4": { - "EzSystems\\EzRecommendationClient\\Tests\\": "tests/lib/" + "EzSystems\\EzRecommendationClient\\Tests\\": "tests/lib/", + "Ibexa\\Tests\\Personalization\\": "tests/lib/" } }, "scripts": { diff --git a/src/lib/Event/Listener/LoginListener.php b/src/lib/Event/Listener/LoginListener.php index 27d0c5ae..12c8a426 100644 --- a/src/lib/Event/Listener/LoginListener.php +++ b/src/lib/Event/Listener/LoginListener.php @@ -64,7 +64,7 @@ public function __construct( public function onSecurityInteractiveLogin(InteractiveLoginEvent $event): void { - if (!$event->getRequest()->get('is_rest_request')) { + if ($event->getRequest()->get('is_rest_request')) { return; } diff --git a/tests/lib/Event/Listener/LoginListenerTest.php b/tests/lib/Event/Listener/LoginListenerTest.php new file mode 100644 index 00000000..50f1feef --- /dev/null +++ b/tests/lib/Event/Listener/LoginListenerTest.php @@ -0,0 +1,223 @@ +authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $this->session = $this->createMock(SessionInterface::class); + $this->client = $this->createMock(EzRecommendationClientInterface::class); + $this->guzzleClient = $this->createMock(Client::class); + $this->userService = $this->createMock(UserService::class); + $this->configResolver = $this->createMock(ConfigResolverInterface::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->token = $this->createMock(TokenInterface::class); + $this->loginListener = new LoginListener( + $this->authorizationChecker, + $this->session, + $this->client, + $this->userService, + $this->configResolver, + $this->logger + ); + } + + public function testDoNotStartSessionWhenRestRequest(): void + { + $this->assertSessionNotStarted(true); + } + + public function testDoNotStartSessionWhenUserIsNotAuthenticated(): void + { + $this->configureAuthorizationCheckerToReturnIsUserAuthenticated(false, false); + $this->assertSessionNotStarted(false); + } + + public function testStartRecommendationSession(): void + { + $this->configureAuthorizationCheckerToReturnIsUserAuthenticated(true, true); + $this->configureSession(self::SESSION_ID); + $this->client + ->expects(self::once()) + ->method('getHttpClient') + ->willReturn($this->guzzleClient); + + $this->configureConfigResolverToReturnEndpointParameters(); + + $this->guzzleClient + ->expects(self::once()) + ->method('__call') + ->with('get', [ + $this->getEndpointUri(self::ENDPOINT_URL, self::CUSTOMER_ID, self::SESSION_ID), + ]) + ->willReturn($this->createMock(ResponseInterface::class)); + + $event = $this->createTestEvent(false, true); + $this->loginListener->onSecurityInteractiveLogin($event); + $request = $event->getRequest(); + + self::assertTrue($request->getSession()->isStarted()); + self::assertEquals( + self::SESSION_ID, + $request->cookies->get(self::RECOMMENDATION_SESSION_KEY) + ); + } + + private function assertSessionNotStarted(bool $isRestRequest): void + { + $event = $this->createTestEvent($isRestRequest, false); + $this->loginListener->onSecurityInteractiveLogin($event); + $session = $event->getRequest()->getSession(); + + self::assertFalse($session->isStarted()); + } + + private function createTestEvent(bool $isRestRequest, bool $isSessionStarted): InteractiveLoginEvent + { + return $this->createInteractiveLoginEvent( + $this->createRequest($isRestRequest, $this->createSession($isSessionStarted)), + $this->token + ); + } + + private function createInteractiveLoginEvent(Request $request, TokenInterface $token): InteractiveLoginEvent + { + return new InteractiveLoginEvent($request, $token); + } + + private function createRequest(bool $isRestRequest, SessionInterface $session): Request + { + $request = new Request(); + $request->attributes->set('is_rest_request', $isRestRequest); + $request->setSession($session); + + return $request; + } + + private function createSession(bool $isStarted): SessionInterface + { + $testSession = $this->createMock(SessionInterface::class); + $testSession + ->expects(self::once()) + ->method('isStarted') + ->willReturn($isStarted); + + return $testSession; + } + + private function configureAuthorizationCheckerToReturnIsUserAuthenticated( + bool $isAuthenticatedFully, + bool $isAuthenticatedRemembered + ): void { + $this->authorizationChecker + ->expects(self::atLeastOnce()) + ->method('isGranted') + ->withConsecutive( + ['IS_AUTHENTICATED_FULLY'], + ['IS_AUTHENTICATED_REMEMBERED'], + ) + ->willReturnOnConsecutiveCalls( + $isAuthenticatedFully, + $isAuthenticatedRemembered + ); + } + + private function configureSession(string $sessionId): void + { + $this->session + ->expects(self::once()) + ->method('isStarted') + ->willReturn(false); + + $this->session + ->expects(self::once()) + ->method('start') + ->willReturn(true); + + $this->session + ->expects(self::once()) + ->method('getId') + ->willReturn($sessionId); + } + + private function configureConfigResolverToReturnEndpointParameters(): void + { + $this->configResolver + ->expects(self::atLeastOnce()) + ->method('getParameter') + ->withConsecutive( + ['authentication.customer_id', self::CONFIG_NAMESPACE], + ['api.event_tracking.endpoint', self::CONFIG_NAMESPACE], + ) + ->willReturnOnConsecutiveCalls( + self::CUSTOMER_ID, + self::ENDPOINT_URL + ); + } + + private function getEndpointUri( + string $endpointUrl, + int $customerId, + string $sessionId + ): string { + return sprintf($endpointUrl . '/api/%d/login/%s/', $customerId, $sessionId); + } +}