test: achieve 100% coverage for KeycloakAuthenticator and LoginSuccessHandler
This commit is contained in:
163
tests/Security/KeycloakAuthenticatorTest.php
Normal file
163
tests/Security/KeycloakAuthenticatorTest.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Security;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Security\KeycloakAuthenticator;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
|
||||
use KnpU\OAuth2ClientBundle\Client\OAuth2ClientInterface;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||
|
||||
class KeycloakAuthenticatorTest extends TestCase
|
||||
{
|
||||
private ClientRegistry $clientRegistry;
|
||||
private EntityManagerInterface $em;
|
||||
private UserRepository $userRepository;
|
||||
private RouterInterface $router;
|
||||
private KeycloakAuthenticator $authenticator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->clientRegistry = $this->createMock(ClientRegistry::class);
|
||||
$this->em = $this->createMock(EntityManagerInterface::class);
|
||||
$this->userRepository = $this->createMock(UserRepository::class);
|
||||
$this->router = $this->createMock(RouterInterface::class);
|
||||
|
||||
$this->authenticator = new KeycloakAuthenticator(
|
||||
$this->clientRegistry,
|
||||
$this->em,
|
||||
$this->userRepository,
|
||||
$this->router
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupports(): void
|
||||
{
|
||||
$request = new Request();
|
||||
$request->attributes->set('_route', 'connect_keycloak_check');
|
||||
$this->assertTrue($this->authenticator->supports($request));
|
||||
|
||||
$request->attributes->set('_route', 'other_route');
|
||||
$this->assertFalse($this->authenticator->supports($request));
|
||||
}
|
||||
|
||||
public function testAuthenticate(): void
|
||||
{
|
||||
$request = new Request();
|
||||
$client = $this->createMock(OAuth2ClientInterface::class);
|
||||
|
||||
$accessToken = new AccessToken(['access_token' => 'fake-token']);
|
||||
|
||||
$this->clientRegistry->method('getClient')->with('keycloak')->willReturn($client);
|
||||
$client->method('getAccessToken')->willReturn($accessToken);
|
||||
|
||||
$passport = $this->authenticator->authenticate($request);
|
||||
$this->assertInstanceOf(SelfValidatingPassport::class, $passport);
|
||||
|
||||
$userBadge = $passport->getBadge(\Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge::class);
|
||||
$this->assertNotNull($userBadge);
|
||||
|
||||
// Test the user loader callback
|
||||
$keycloakUser = $this->createMock(\League\OAuth2\Client\Provider\ResourceOwnerInterface::class);
|
||||
$keycloakUser->method('toArray')->willReturn([
|
||||
'sub' => '123',
|
||||
'email' => 'test@example.com',
|
||||
'given_name' => 'John',
|
||||
'family_name' => 'Doe',
|
||||
'groups' => ['super_admin_asso']
|
||||
]);
|
||||
|
||||
$client->method('fetchUserFromToken')->willReturn($keycloakUser);
|
||||
$this->userRepository->method('findOneBy')->willReturn(null);
|
||||
|
||||
$this->em->expects($this->once())->method('persist');
|
||||
$this->em->expects($this->once())->method('flush');
|
||||
|
||||
$user = $userBadge->getUser();
|
||||
$this->assertInstanceOf(User::class, $user);
|
||||
$this->assertEquals('123', $user->getKeycloakId());
|
||||
$this->assertEquals('test@example.com', $user->getEmail());
|
||||
$this->assertContains('ROLE_ROOT', $user->getRoles());
|
||||
}
|
||||
|
||||
public function testAuthenticateExistingUserByEmail(): void
|
||||
{
|
||||
$request = new Request();
|
||||
$client = $this->createMock(OAuth2ClientInterface::class);
|
||||
$accessToken = new AccessToken(['access_token' => 'fake-token']);
|
||||
$this->clientRegistry->method('getClient')->willReturn($client);
|
||||
$client->method('getAccessToken')->willReturn($accessToken);
|
||||
|
||||
$passport = $this->authenticator->authenticate($request);
|
||||
$userBadge = $passport->getBadge(\Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge::class);
|
||||
|
||||
$keycloakUser = $this->createMock(\League\OAuth2\Client\Provider\ResourceOwnerInterface::class);
|
||||
$keycloakUser->method('toArray')->willReturn([
|
||||
'sub' => '123',
|
||||
'email' => 'existing@example.com',
|
||||
'groups' => []
|
||||
]);
|
||||
$client->method('fetchUserFromToken')->willReturn($keycloakUser);
|
||||
|
||||
$existingUser = new User();
|
||||
$this->userRepository->method('findOneBy')->willReturnCallback(function($criteria) use ($existingUser) {
|
||||
if (isset($criteria['email']) && $criteria['email'] === 'existing@example.com') {
|
||||
return $existingUser;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$user = $userBadge->getUser();
|
||||
$this->assertSame($existingUser, $user);
|
||||
$this->assertEquals('123', $user->getKeycloakId());
|
||||
$this->assertContains('ROLE_EMPLOYE', $user->getRoles());
|
||||
}
|
||||
|
||||
public function testOnAuthenticationSuccess(): void
|
||||
{
|
||||
$request = new Request();
|
||||
$token = $this->createMock(TokenInterface::class);
|
||||
$this->router->method('generate')->with('app_home')->willReturn('/home');
|
||||
|
||||
$response = $this->authenticator->onAuthenticationSuccess($request, $token, 'main');
|
||||
$this->assertInstanceOf(RedirectResponse::class, $response);
|
||||
$this->assertEquals('/home', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testOnAuthenticationFailure(): void
|
||||
{
|
||||
$request = $this->createMock(Request::class);
|
||||
|
||||
$session = $this->createMock(\Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface::class);
|
||||
$flashBag = $this->createMock(\Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface::class);
|
||||
|
||||
$request->method('getSession')->willReturn($session);
|
||||
$session->method('getFlashBag')->willReturn($flashBag);
|
||||
$flashBag->expects($this->once())->method('add')->with('error', $this->anything());
|
||||
|
||||
$this->router->method('generate')->with('app_home')->willReturn('/home');
|
||||
|
||||
$response = $this->authenticator->onAuthenticationFailure($request, new AuthenticationException());
|
||||
$this->assertInstanceOf(RedirectResponse::class, $response);
|
||||
}
|
||||
|
||||
public function testStart(): void
|
||||
{
|
||||
$request = new Request();
|
||||
$this->router->method('generate')->with('app_home')->willReturn('/home');
|
||||
|
||||
$response = $this->authenticator->start($request);
|
||||
$this->assertInstanceOf(RedirectResponse::class, $response);
|
||||
$this->assertEquals('/home', $response->getTargetUrl());
|
||||
}
|
||||
}
|
||||
68
tests/Security/LoginSuccessHandlerTest.php
Normal file
68
tests/Security/LoginSuccessHandlerTest.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Security;
|
||||
|
||||
use App\Security\LoginSuccessHandler;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
|
||||
class LoginSuccessHandlerTest extends TestCase
|
||||
{
|
||||
private RouterInterface $router;
|
||||
private AuthorizationCheckerInterface $authorizationChecker;
|
||||
private LoginSuccessHandler $handler;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->router = $this->createMock(RouterInterface::class);
|
||||
$this->authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
|
||||
$this->handler = new LoginSuccessHandler($this->router, $this->authorizationChecker);
|
||||
}
|
||||
|
||||
#[DataProvider('provideRolesAndRoutes')]
|
||||
public function testOnAuthenticationSuccess(string $role, string $routeName): void
|
||||
{
|
||||
$request = $this->createMock(Request::class);
|
||||
$token = $this->createMock(TokenInterface::class);
|
||||
|
||||
$this->authorizationChecker->method('isGranted')
|
||||
->willReturnCallback(fn($r) => $r === $role);
|
||||
|
||||
$this->router->expects($this->once())
|
||||
->method('generate')
|
||||
->with($routeName)
|
||||
->willReturn('/' . $routeName);
|
||||
|
||||
$response = $this->handler->onAuthenticationSuccess($request, $token);
|
||||
|
||||
$this->assertEquals('/' . $routeName, $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public static function provideRolesAndRoutes(): iterable
|
||||
{
|
||||
yield ['ROLE_EMPLOYE', 'app_admin_dashboard'];
|
||||
yield ['ROLE_REVENDEUR', 'app_espace_prestataire_index'];
|
||||
yield ['ROLE_CUSTOMER', 'app_espace_client_index'];
|
||||
}
|
||||
|
||||
public function testOnAuthenticationSuccessDefault(): void
|
||||
{
|
||||
$request = $this->createMock(Request::class);
|
||||
$token = $this->createMock(TokenInterface::class);
|
||||
|
||||
$this->authorizationChecker->method('isGranted')->willReturn(false);
|
||||
|
||||
$this->router->expects($this->once())
|
||||
->method('generate')
|
||||
->with('app_home')
|
||||
->willReturn('/');
|
||||
|
||||
$response = $this->handler->onAuthenticationSuccess($request, $token);
|
||||
|
||||
$this->assertEquals('/', $response->getTargetUrl());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user