- Add /conformite page: PSD2/3DS/Stripe, SonarQube badges, CI/CD, security - Create SonarBadgeController proxy to serve SonarQube badges without exposing token - Store SonarQube badge token in ansible/vault.yml instead of env files - Add Meilisearch coverage tests: search with results, search error, sync, delete - Fix MeilisearchService delete catch block with comment - Fix ESLint: use globalThis.confirm instead of window.confirm - Fix accessibility: add for/id attributes to buyer creation form labels - Add conformite link to site footer - Add SonarBadgeControllerTest and LegalControllerTest for /conformite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
435 lines
14 KiB
PHP
435 lines
14 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Controller;
|
|
|
|
use App\Entity\User;
|
|
use App\Service\MailerService;
|
|
use App\Service\MeilisearchService;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
|
|
|
class AdminControllerTest extends WebTestCase
|
|
{
|
|
public function testDashboardRedirectsWhenNotAuthenticated(): void
|
|
{
|
|
$client = static::createClient();
|
|
$client->request('GET', '/admin');
|
|
|
|
self::assertResponseRedirects();
|
|
}
|
|
|
|
public function testDashboardDeniedForNonRoot(): void
|
|
{
|
|
$client = static::createClient();
|
|
$user = $this->createUser();
|
|
|
|
$client->loginUser($user);
|
|
$client->request('GET', '/admin');
|
|
|
|
self::assertResponseStatusCodeSame(403);
|
|
}
|
|
|
|
public function testDashboardReturnsSuccessForRoot(): void
|
|
{
|
|
$client = static::createClient();
|
|
$user = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$client->loginUser($user);
|
|
$client->request('GET', '/admin');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testUsersPageReturnsSuccessForRoot(): void
|
|
{
|
|
$client = static::createClient();
|
|
$user = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$client->loginUser($user);
|
|
$client->request('GET', '/admin/utilisateurs');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testUsersPageDeniedForNonRoot(): void
|
|
{
|
|
$client = static::createClient();
|
|
$user = $this->createUser();
|
|
|
|
$client->loginUser($user);
|
|
$client->request('GET', '/admin/utilisateurs');
|
|
|
|
self::assertResponseStatusCodeSame(403);
|
|
}
|
|
|
|
public function testBuyersPageReturnsSuccessForRoot(): void
|
|
{
|
|
$client = static::createClient();
|
|
$user = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$client->loginUser($user);
|
|
$client->request('GET', '/admin/acheteurs');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testBuyersSearchWithQuery(): void
|
|
{
|
|
$client = static::createClient();
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$meilisearch = $this->createMock(MeilisearchService::class);
|
|
$meilisearch->expects(self::once())->method('search')->willReturn([
|
|
'hits' => [],
|
|
'estimatedTotalHits' => 0,
|
|
]);
|
|
static::getContainer()->set(MeilisearchService::class, $meilisearch);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('GET', '/admin/acheteurs?q=test');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testSyncMeilisearchWithBuyers(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$buyer = new User();
|
|
$buyer->setEmail('test-sync-'.uniqid().'@example.com');
|
|
$buyer->setFirstName('Sync');
|
|
$buyer->setLastName('Test');
|
|
$buyer->setPassword('$2y$13$hashed');
|
|
$buyer->setIsVerified(true);
|
|
$em->persist($buyer);
|
|
$em->flush();
|
|
|
|
$meilisearch = $this->createMock(MeilisearchService::class);
|
|
$meilisearch->expects(self::once())->method('createIndexIfNotExists');
|
|
$meilisearch->expects(self::once())->method('addDocuments');
|
|
static::getContainer()->set(MeilisearchService::class, $meilisearch);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/sync-meilisearch');
|
|
|
|
self::assertResponseRedirects('/admin');
|
|
}
|
|
|
|
public function testBuyersSearchWithMeilisearchError(): void
|
|
{
|
|
$client = static::createClient();
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$meilisearch = $this->createMock(MeilisearchService::class);
|
|
$meilisearch->method('search')->willThrowException(new \RuntimeException('Meilisearch down'));
|
|
static::getContainer()->set(MeilisearchService::class, $meilisearch);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('GET', '/admin/acheteurs?q=test');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testBuyersSearchWithResults(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$buyer = new User();
|
|
$buyer->setEmail('test-search-hit-'.uniqid().'@example.com');
|
|
$buyer->setFirstName('Found');
|
|
$buyer->setLastName('User');
|
|
$buyer->setPassword('$2y$13$hashed');
|
|
$buyer->setIsVerified(true);
|
|
$em->persist($buyer);
|
|
$em->flush();
|
|
|
|
$meilisearch = $this->createMock(MeilisearchService::class);
|
|
$meilisearch->method('search')->willReturn([
|
|
'hits' => [['id' => $buyer->getId()]],
|
|
'estimatedTotalHits' => 1,
|
|
]);
|
|
static::getContainer()->set(MeilisearchService::class, $meilisearch);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('GET', '/admin/acheteurs?q=Found');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testCreateBuyerWithValidData(): void
|
|
{
|
|
$client = static::createClient();
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$mailer = $this->createMock(MailerService::class);
|
|
$mailer->expects(self::once())->method('sendEmail');
|
|
static::getContainer()->set(MailerService::class, $mailer);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/acheteurs/creer', [
|
|
'first_name' => 'Nouveau',
|
|
'last_name' => 'Acheteur',
|
|
'email' => 'new-buyer-'.uniqid().'@example.com',
|
|
]);
|
|
|
|
self::assertResponseRedirects('/admin/acheteurs');
|
|
}
|
|
|
|
public function testCreateBuyerWithDuplicateEmail(): void
|
|
{
|
|
$client = static::createClient();
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/acheteurs/creer', [
|
|
'first_name' => 'Dup',
|
|
'last_name' => 'Test',
|
|
'email' => $admin->getEmail(),
|
|
]);
|
|
|
|
self::assertResponseRedirects('/admin/acheteurs');
|
|
}
|
|
|
|
public function testResendVerificationEmail(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$buyer = new User();
|
|
$buyer->setEmail('test-buyer-'.uniqid().'@example.com');
|
|
$buyer->setFirstName('Buyer');
|
|
$buyer->setLastName('Test');
|
|
$buyer->setPassword('$2y$13$hashed');
|
|
$em->persist($buyer);
|
|
$em->flush();
|
|
|
|
$mailer = $this->createMock(MailerService::class);
|
|
$mailer->expects(self::once())->method('sendEmail');
|
|
static::getContainer()->set(MailerService::class, $mailer);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/acheteur/'.$buyer->getId().'/renvoyer-verification');
|
|
|
|
self::assertResponseRedirects('/admin/acheteurs');
|
|
|
|
$em->refresh($buyer);
|
|
self::assertNotNull($buyer->getEmailVerificationToken());
|
|
}
|
|
|
|
public function testResetPasswordSendsEmail(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$buyer = new User();
|
|
$buyer->setEmail('test-reset-admin-'.uniqid().'@example.com');
|
|
$buyer->setFirstName('Reset');
|
|
$buyer->setLastName('Test');
|
|
$buyer->setPassword('$2y$13$hashed');
|
|
$buyer->setIsVerified(true);
|
|
$em->persist($buyer);
|
|
$em->flush();
|
|
|
|
$mailer = $this->createMock(MailerService::class);
|
|
$mailer->expects(self::once())->method('sendEmail');
|
|
static::getContainer()->set(MailerService::class, $mailer);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/acheteur/'.$buyer->getId().'/reset-password');
|
|
|
|
self::assertResponseRedirects('/admin/acheteurs');
|
|
}
|
|
|
|
public function testDeleteBuyer(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$buyer = new User();
|
|
$buyer->setEmail('test-delete-'.uniqid().'@example.com');
|
|
$buyer->setFirstName('Delete');
|
|
$buyer->setLastName('Test');
|
|
$buyer->setPassword('$2y$13$hashed');
|
|
$em->persist($buyer);
|
|
$em->flush();
|
|
$buyerId = $buyer->getId();
|
|
|
|
$meilisearch = $this->createMock(MeilisearchService::class);
|
|
$meilisearch->expects(self::once())->method('deleteDocument')->with('buyers', $buyerId);
|
|
static::getContainer()->set(MeilisearchService::class, $meilisearch);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/acheteur/'.$buyerId.'/supprimer');
|
|
|
|
self::assertResponseRedirects('/admin/acheteurs');
|
|
|
|
$deleted = $em->getRepository(User::class)->find($buyerId);
|
|
self::assertNull($deleted);
|
|
}
|
|
|
|
public function testDeleteBuyerMeilisearchFailureDoesNotBlock(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$buyer = new User();
|
|
$buyer->setEmail('test-delete-fail-'.uniqid().'@example.com');
|
|
$buyer->setFirstName('DeleteFail');
|
|
$buyer->setLastName('Test');
|
|
$buyer->setPassword('$2y$13$hashed');
|
|
$em->persist($buyer);
|
|
$em->flush();
|
|
$buyerId = $buyer->getId();
|
|
|
|
$meilisearch = $this->createMock(MeilisearchService::class);
|
|
$meilisearch->method('deleteDocument')->willThrowException(new \RuntimeException('Meilisearch down'));
|
|
static::getContainer()->set(MeilisearchService::class, $meilisearch);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/acheteur/'.$buyerId.'/supprimer');
|
|
|
|
self::assertResponseRedirects('/admin/acheteurs');
|
|
}
|
|
|
|
public function testForceVerification(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$buyer = new User();
|
|
$buyer->setEmail('test-force-'.uniqid().'@example.com');
|
|
$buyer->setFirstName('Force');
|
|
$buyer->setLastName('Test');
|
|
$buyer->setPassword('$2y$13$hashed');
|
|
$em->persist($buyer);
|
|
$em->flush();
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/acheteur/'.$buyer->getId().'/forcer-verification');
|
|
|
|
self::assertResponseRedirects('/admin/acheteurs');
|
|
|
|
$em->refresh($buyer);
|
|
self::assertTrue($buyer->isVerified());
|
|
self::assertNotNull($buyer->getEmailVerifiedAt());
|
|
self::assertNull($buyer->getEmailVerificationToken());
|
|
}
|
|
|
|
public function testOrganizersPagePendingTab(): void
|
|
{
|
|
$client = static::createClient();
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('GET', '/admin/organisateurs');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testOrganizersPageApprovedTab(): void
|
|
{
|
|
$client = static::createClient();
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('GET', '/admin/organisateurs?tab=approved');
|
|
|
|
self::assertResponseIsSuccessful();
|
|
}
|
|
|
|
public function testApproveOrganizer(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
$orga = $this->createOrganizer($em);
|
|
|
|
$mailer = $this->createMock(MailerService::class);
|
|
$mailer->expects(self::once())->method('sendEmail');
|
|
static::getContainer()->set(MailerService::class, $mailer);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/organisateur/'.$orga->getId().'/approuver');
|
|
|
|
self::assertResponseRedirects('/admin/organisateurs');
|
|
|
|
$em->refresh($orga);
|
|
self::assertTrue($orga->isApproved());
|
|
}
|
|
|
|
public function testRejectOrganizer(): void
|
|
{
|
|
$client = static::createClient();
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$admin = $this->createUser(['ROLE_ROOT']);
|
|
$orga = $this->createOrganizer($em);
|
|
$orgaId = $orga->getId();
|
|
|
|
$mailer = $this->createMock(MailerService::class);
|
|
$mailer->expects(self::once())->method('sendEmail');
|
|
static::getContainer()->set(MailerService::class, $mailer);
|
|
|
|
$client->loginUser($admin);
|
|
$client->request('POST', '/admin/organisateur/'.$orgaId.'/refuser');
|
|
|
|
self::assertResponseRedirects('/admin/organisateurs');
|
|
|
|
$deleted = $em->getRepository(User::class)->find($orgaId);
|
|
self::assertNull($deleted);
|
|
}
|
|
|
|
/**
|
|
* @param list<string> $roles
|
|
*/
|
|
private function createUser(array $roles = []): User
|
|
{
|
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
|
|
|
$user = new User();
|
|
$user->setEmail('test-admin-'.uniqid().'@example.com');
|
|
$user->setFirstName('Admin');
|
|
$user->setLastName('User');
|
|
$user->setPassword('$2y$13$hashed');
|
|
$user->setRoles($roles);
|
|
|
|
$em->persist($user);
|
|
$em->flush();
|
|
|
|
return $user;
|
|
}
|
|
|
|
private function createOrganizer(EntityManagerInterface $em): User
|
|
{
|
|
$orga = new User();
|
|
$orga->setEmail('test-orga-'.uniqid().'@example.com');
|
|
$orga->setFirstName('Orga');
|
|
$orga->setLastName('Test');
|
|
$orga->setPassword('$2y$13$hashed');
|
|
$orga->setRoles(['ROLE_ORGANIZER']);
|
|
$orga->setIsVerified(true);
|
|
$orga->setCompanyName('Mon Asso');
|
|
$orga->setSiret('12345678901234');
|
|
|
|
$em->persist($orga);
|
|
$em->flush();
|
|
|
|
return $orga;
|
|
}
|
|
}
|