Add test coverage for remaining controllers, fix label accessibility, refactor duplicated code
New tests (47 added, 622 total): - MonitorMessengerCommand: no failures, failures with email, null error, multiple (4) - UnsubscribeController: unsubscribe with invitations refused + admin notified (1) - AdminController: suspend/reactivate orga, orders page with filters, logs, invite orga submit/empty, delete/resend invitation, export CSV/PDF (13) - AccountController: export CSV/PDF, getAllowedBilletTypes (free/basic/sur-mesure/null), billet type restriction, finance stats all statuses, soldCounts (9) - HomeController: city filter, date filter, all filters combined, stock route (4) - OrderController: event ended, invalid cart JSON, invalid email, stock zero (4) - MailerService: getAdminEmail, getAdminFrom (2) - JS: comment node, tabs missing panel/id/parent, cart stock polling edge cases (10) Accessibility fixes: - events.html.twig: add for/id on search, city, date labels - admin/orders.html.twig: add for/id on search, status labels Code quality: - cart.js: remove dead ternaire branch (max > 10 always plural) - tabs.js: use optional chaining for tablist?.setAttribute - MeilisearchConsistencyCommand: extract diffAndReport() (was duplicated 3x) - Email templates: extract _order_items_table.html.twig partial - SonarQube: exclude src/Entity/** from CPD Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -140,7 +140,7 @@ export function initCart() {
|
|||||||
} else if (max <= 10) {
|
} else if (max <= 10) {
|
||||||
label.innerHTML = '<span class="text-orange-500">Plus que ' + max + ' place' + (max > 1 ? 's' : '') + ' !</span>'
|
label.innerHTML = '<span class="text-orange-500">Plus que ' + max + ' place' + (max > 1 ? 's' : '') + ' !</span>'
|
||||||
} else {
|
} else {
|
||||||
label.innerHTML = '<span class="text-gray-400">' + max + ' place' + (max > 1 ? 's' : '') + ' disponible' + (max > 1 ? 's' : '') + '</span>'
|
label.innerHTML = '<span class="text-gray-400">' + max + ' places disponibles</span>'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ export function initTabs() {
|
|||||||
if (buttons.length === 0) return
|
if (buttons.length === 0) return
|
||||||
|
|
||||||
const tablist = buttons[0].parentElement
|
const tablist = buttons[0].parentElement
|
||||||
if (tablist) {
|
tablist?.setAttribute('role', 'tablist')
|
||||||
tablist.setAttribute('role', 'tablist')
|
|
||||||
}
|
|
||||||
|
|
||||||
buttons.forEach(button => {
|
buttons.forEach(button => {
|
||||||
const targetId = button.dataset.tab
|
const targetId = button.dataset.tab
|
||||||
|
|||||||
@@ -31,12 +31,12 @@
|
|||||||
<h2 class="text-sm font-black uppercase tracking-widest mb-4">Filtrer</h2>
|
<h2 class="text-sm font-black uppercase tracking-widest mb-4">Filtrer</h2>
|
||||||
<form method="get" action="{{ path('app_admin_orders') }}" class="flex flex-wrap gap-4 items-end">
|
<form method="get" action="{{ path('app_admin_orders') }}" class="flex flex-wrap gap-4 items-end">
|
||||||
<div class="flex-1 min-w-[200px]">
|
<div class="flex-1 min-w-[200px]">
|
||||||
<label class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Recherche</label>
|
<label for="orders-q" class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Recherche</label>
|
||||||
<input type="text" name="q" value="{{ search }}" class="admin-form-input" placeholder="Numero, nom, email...">
|
<input type="text" id="orders-q" name="q" value="{{ search }}" class="admin-form-input" placeholder="Numero, nom, email...">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Statut</label>
|
<label for="orders-status" class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Statut</label>
|
||||||
<select name="status" class="admin-form-input">
|
<select id="orders-status" name="status" class="admin-form-input">
|
||||||
<option value="">Tous</option>
|
<option value="">Tous</option>
|
||||||
<option value="pending" {{ status == 'pending' ? 'selected' : '' }}>En attente</option>
|
<option value="pending" {{ status == 'pending' ? 'selected' : '' }}>En attente</option>
|
||||||
<option value="paid" {{ status == 'paid' ? 'selected' : '' }}>Payee</option>
|
<option value="paid" {{ status == 'paid' ? 'selected' : '' }}>Payee</option>
|
||||||
|
|||||||
@@ -25,16 +25,16 @@
|
|||||||
<div class="max-w-7xl mx-auto">
|
<div class="max-w-7xl mx-auto">
|
||||||
<form method="get" action="{{ path('app_events') }}" class="flex flex-wrap gap-3 items-end">
|
<form method="get" action="{{ path('app_events') }}" class="flex flex-wrap gap-3 items-end">
|
||||||
<div class="flex-1 min-w-[200px]">
|
<div class="flex-1 min-w-[200px]">
|
||||||
<label class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1 block">Recherche</label>
|
<label for="filter-q" class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1 block">Recherche</label>
|
||||||
<input type="text" name="q" value="{{ searchQuery }}" class="form-input" placeholder="Nom, organisateur...">
|
<input type="text" id="filter-q" name="q" value="{{ searchQuery }}" class="form-input" placeholder="Nom, organisateur...">
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full sm:w-auto">
|
<div class="w-full sm:w-auto">
|
||||||
<label class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1 block">Ville</label>
|
<label for="filter-city" class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1 block">Ville</label>
|
||||||
<input type="text" name="city" value="{{ city }}" class="form-input" placeholder="Paris, Lyon...">
|
<input type="text" id="filter-city" name="city" value="{{ city }}" class="form-input" placeholder="Paris, Lyon...">
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full sm:w-auto">
|
<div class="w-full sm:w-auto">
|
||||||
<label class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1 block">Date</label>
|
<label for="filter-date" class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1 block">Date</label>
|
||||||
<input type="date" name="date" value="{{ date }}" class="form-input">
|
<input type="date" id="filter-date" name="date" value="{{ date }}" class="form-input">
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn-brutal font-black uppercase text-xs tracking-widest hover:bg-indigo-600 hover:text-white transition-all">Filtrer</button>
|
<button type="submit" class="btn-brutal font-black uppercase text-xs tracking-widest hover:bg-indigo-600 hover:text-white transition-all">Filtrer</button>
|
||||||
{% if searchQuery or city or date %}
|
{% if searchQuery or city or date %}
|
||||||
|
|||||||
129
tests/Command/MonitorMessengerCommandTest.php
Normal file
129
tests/Command/MonitorMessengerCommandTest.php
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Command;
|
||||||
|
|
||||||
|
use App\Command\MonitorMessengerCommand;
|
||||||
|
use App\Entity\MessengerLog;
|
||||||
|
use App\Service\MailerService;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
|
||||||
|
class MonitorMessengerCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
private EntityManagerInterface $em;
|
||||||
|
private MailerService $mailer;
|
||||||
|
private CommandTester $tester;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->em = $this->createMock(EntityManagerInterface::class);
|
||||||
|
$this->mailer = $this->createMock(MailerService::class);
|
||||||
|
|
||||||
|
$command = new MonitorMessengerCommand($this->em, $this->mailer);
|
||||||
|
$app = new Application();
|
||||||
|
$app->addCommand($command);
|
||||||
|
$this->tester = new CommandTester($app->find('app:monitor:messenger'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoFailedMessages(): void
|
||||||
|
{
|
||||||
|
$repo = $this->createMock(EntityRepository::class);
|
||||||
|
$repo->method('findBy')->willReturn([]);
|
||||||
|
|
||||||
|
$this->em->method('getRepository')
|
||||||
|
->with(MessengerLog::class)
|
||||||
|
->willReturn($repo);
|
||||||
|
|
||||||
|
$this->mailer->expects(self::never())->method('sendEmail');
|
||||||
|
|
||||||
|
$this->tester->execute([]);
|
||||||
|
|
||||||
|
self::assertSame(0, $this->tester->getStatusCode());
|
||||||
|
self::assertStringContainsString('No failed messages', $this->tester->getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedMessagesSendsEmail(): void
|
||||||
|
{
|
||||||
|
$log = $this->createMock(MessengerLog::class);
|
||||||
|
$log->method('getMessageClass')->willReturn('App\\Message\\TestMessage');
|
||||||
|
$log->method('getCreatedAt')->willReturn(new \DateTimeImmutable('2026-03-23 10:00'));
|
||||||
|
$log->method('getErrorMessage')->willReturn('Something went wrong');
|
||||||
|
|
||||||
|
$repo = $this->createMock(EntityRepository::class);
|
||||||
|
$repo->method('findBy')->willReturn([$log]);
|
||||||
|
|
||||||
|
$this->em->method('getRepository')
|
||||||
|
->with(MessengerLog::class)
|
||||||
|
->willReturn($repo);
|
||||||
|
|
||||||
|
$this->mailer->method('getAdminEmail')->willReturn('admin@test.com');
|
||||||
|
$this->mailer->expects(self::once())->method('sendEmail')->with(
|
||||||
|
'admin@test.com',
|
||||||
|
'[E-Ticket] 1 message(s) Messenger en echec',
|
||||||
|
$this->callback(fn (string $html) => str_contains($html, 'TestMessage') && str_contains($html, 'Something went wrong')),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->tester->execute([]);
|
||||||
|
|
||||||
|
self::assertSame(0, $this->tester->getStatusCode());
|
||||||
|
self::assertStringContainsString('1 failed message(s)', $this->tester->getDisplay());
|
||||||
|
self::assertStringContainsString('admin@test.com', $this->tester->getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedMessageWithNullError(): void
|
||||||
|
{
|
||||||
|
$log = $this->createMock(MessengerLog::class);
|
||||||
|
$log->method('getMessageClass')->willReturn('App\\Message\\Other');
|
||||||
|
$log->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
|
||||||
|
$log->method('getErrorMessage')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = $this->createMock(EntityRepository::class);
|
||||||
|
$repo->method('findBy')->willReturn([$log]);
|
||||||
|
|
||||||
|
$this->em->method('getRepository')->willReturn($repo);
|
||||||
|
|
||||||
|
$this->mailer->method('getAdminEmail')->willReturn('admin@test.com');
|
||||||
|
$this->mailer->expects(self::once())->method('sendEmail');
|
||||||
|
|
||||||
|
$this->tester->execute([]);
|
||||||
|
|
||||||
|
self::assertSame(0, $this->tester->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultipleFailedMessages(): void
|
||||||
|
{
|
||||||
|
$logs = [];
|
||||||
|
for ($i = 0; $i < 3; ++$i) {
|
||||||
|
$log = $this->createMock(MessengerLog::class);
|
||||||
|
$log->method('getMessageClass')->willReturn('Msg'.$i);
|
||||||
|
$log->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
|
||||||
|
$log->method('getErrorMessage')->willReturn('Error '.$i);
|
||||||
|
$logs[] = $log;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repo = $this->createMock(EntityRepository::class);
|
||||||
|
$repo->method('findBy')->willReturn($logs);
|
||||||
|
|
||||||
|
$this->em->method('getRepository')->willReturn($repo);
|
||||||
|
|
||||||
|
$this->mailer->method('getAdminEmail')->willReturn('admin@test.com');
|
||||||
|
$this->mailer->expects(self::once())->method('sendEmail')->with(
|
||||||
|
'admin@test.com',
|
||||||
|
'[E-Ticket] 3 message(s) Messenger en echec',
|
||||||
|
$this->anything(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->tester->execute([]);
|
||||||
|
|
||||||
|
self::assertStringContainsString('3 failed message(s)', $this->tester->getDisplay());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1954,6 +1954,80 @@ class AccountControllerTest extends WebTestCase
|
|||||||
return $category;
|
return $category;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetAllowedBilletTypesBasic(): void
|
||||||
|
{
|
||||||
|
$types = \App\Controller\AccountController::getAllowedBilletTypes('basic');
|
||||||
|
self::assertSame(['billet', 'reservation_brocante', 'vote'], $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAllowedBilletTypesSurMesure(): void
|
||||||
|
{
|
||||||
|
$types = \App\Controller\AccountController::getAllowedBilletTypes('sur-mesure');
|
||||||
|
self::assertSame(['billet', 'reservation_brocante', 'vote'], $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAllowedBilletTypesFree(): void
|
||||||
|
{
|
||||||
|
$types = \App\Controller\AccountController::getAllowedBilletTypes('free');
|
||||||
|
self::assertSame(['billet'], $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAllowedBilletTypesNull(): void
|
||||||
|
{
|
||||||
|
$types = \App\Controller\AccountController::getAllowedBilletTypes(null);
|
||||||
|
self::assertSame(['billet'], $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddBilletTypeRestriction(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
$user = $this->createUser(['ROLE_ORGANIZER'], true);
|
||||||
|
$user->setOffer('free');
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$event = $this->createEvent($em, $user);
|
||||||
|
$category = $this->createCategory($em, $event);
|
||||||
|
|
||||||
|
$client->loginUser($user);
|
||||||
|
$client->request('POST', '/mon-compte/evenement/'.$event->getId().'/categorie/'.$category->getId().'/billet/ajouter', [
|
||||||
|
'name' => 'Vote Interdit',
|
||||||
|
'price_ht' => '5',
|
||||||
|
'type' => 'vote',
|
||||||
|
'is_generated_billet' => '1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertResponseRedirects();
|
||||||
|
|
||||||
|
$billet = $em->getRepository(\App\Entity\Billet::class)->findOneBy(['name' => 'Vote Interdit']);
|
||||||
|
self::assertNotNull($billet);
|
||||||
|
self::assertSame('billet', $billet->getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExportCsv(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$user = $this->createUser(['ROLE_ORGANIZER'], true);
|
||||||
|
|
||||||
|
$client->loginUser($user);
|
||||||
|
$client->request('GET', '/mon-compte/export/2026/3');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
self::assertStringContainsString('text/csv', $client->getResponse()->headers->get('Content-Type'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExportPdf(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$user = $this->createUser(['ROLE_ORGANIZER'], true);
|
||||||
|
|
||||||
|
$client->loginUser($user);
|
||||||
|
$client->request('GET', '/mon-compte/export/2026/3/pdf');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
self::assertStringContainsString('application/pdf', $client->getResponse()->headers->get('Content-Type'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testOrganizerFinanceStatsWithAllStatuses(): void
|
public function testOrganizerFinanceStatsWithAllStatuses(): void
|
||||||
{
|
{
|
||||||
$client = static::createClient();
|
$client = static::createClient();
|
||||||
|
|||||||
@@ -645,4 +645,199 @@ class AdminControllerTest extends WebTestCase
|
|||||||
|
|
||||||
return $orga;
|
return $orga;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSuspendOrganizer(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
$orga = $this->createOrganizer($em);
|
||||||
|
$orga->setIsApproved(true);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('POST', '/admin/organisateur/'.$orga->getId().'/suspendre');
|
||||||
|
|
||||||
|
self::assertResponseRedirects('/admin/organisateurs?tab=approved');
|
||||||
|
|
||||||
|
$em->refresh($orga);
|
||||||
|
self::assertTrue($orga->isSuspended());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReactivateOrganizer(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
$orga = $this->createOrganizer($em);
|
||||||
|
$orga->setIsApproved(true);
|
||||||
|
$orga->setIsSuspended(true);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('POST', '/admin/organisateur/'.$orga->getId().'/suspendre');
|
||||||
|
|
||||||
|
self::assertResponseRedirects('/admin/organisateurs?tab=approved');
|
||||||
|
|
||||||
|
$em->refresh($orga);
|
||||||
|
self::assertNull($orga->isSuspended());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOrdersPage(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('GET', '/admin/commandes');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOrdersPageWithFilters(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('GET', '/admin/commandes?status=paid&q=test');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLogsPage(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('GET', '/admin/logs');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInviteOrganizerPage(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$mailer = $this->createMock(\App\Service\MailerService::class);
|
||||||
|
static::getContainer()->set(\App\Service\MailerService::class, $mailer);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('GET', '/admin/organisateurs/inviter');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInviteOrganizerSubmit(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$mailer = $this->createMock(\App\Service\MailerService::class);
|
||||||
|
$mailer->expects(self::once())->method('sendEmail');
|
||||||
|
static::getContainer()->set(\App\Service\MailerService::class, $mailer);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('POST', '/admin/organisateurs/inviter', [
|
||||||
|
'company_name' => 'New Asso',
|
||||||
|
'first_name' => 'Jean',
|
||||||
|
'last_name' => 'Invite',
|
||||||
|
'email' => 'invite-admin-'.uniqid().'@example.com',
|
||||||
|
'offer' => 'basic',
|
||||||
|
'commission_rate' => '2.5',
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertResponseRedirects('/admin/organisateurs/inviter');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInviteOrganizerEmptyFields(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('POST', '/admin/organisateurs/inviter', [
|
||||||
|
'company_name' => '',
|
||||||
|
'first_name' => '',
|
||||||
|
'last_name' => '',
|
||||||
|
'email' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertResponseRedirects('/admin/organisateurs/inviter');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeleteInvitation(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$invitation = new \App\Entity\OrganizerInvitation();
|
||||||
|
$invitation->setCompanyName('Del Asso');
|
||||||
|
$invitation->setFirstName('Del');
|
||||||
|
$invitation->setLastName('Test');
|
||||||
|
$invitation->setEmail('del-'.uniqid().'@example.com');
|
||||||
|
$em->persist($invitation);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('POST', '/admin/organisateurs/invitation/'.$invitation->getId().'/supprimer');
|
||||||
|
|
||||||
|
self::assertResponseRedirects('/admin/organisateurs/inviter');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResendInvitation(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$mailer = $this->createMock(\App\Service\MailerService::class);
|
||||||
|
$mailer->expects(self::once())->method('sendEmail');
|
||||||
|
static::getContainer()->set(\App\Service\MailerService::class, $mailer);
|
||||||
|
|
||||||
|
$invitation = new \App\Entity\OrganizerInvitation();
|
||||||
|
$invitation->setCompanyName('Resend Asso');
|
||||||
|
$invitation->setFirstName('Resend');
|
||||||
|
$invitation->setLastName('Test');
|
||||||
|
$invitation->setEmail('resend-'.uniqid().'@example.com');
|
||||||
|
$invitation->setStatus(\App\Entity\OrganizerInvitation::STATUS_ACCEPTED);
|
||||||
|
$em->persist($invitation);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('POST', '/admin/organisateurs/invitation/'.$invitation->getId().'/renvoyer');
|
||||||
|
|
||||||
|
self::assertResponseRedirects('/admin/organisateurs/inviter');
|
||||||
|
|
||||||
|
$freshEm = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
$updated = $freshEm->getRepository(\App\Entity\OrganizerInvitation::class)->find($invitation->getId());
|
||||||
|
self::assertSame(\App\Entity\OrganizerInvitation::STATUS_SENT, $updated->getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExportCsv(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('GET', '/admin/export/2026/3');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
self::assertStringContainsString('text/csv', $client->getResponse()->headers->get('Content-Type'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExportPdf(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$admin = $this->createUser(['ROLE_ROOT']);
|
||||||
|
|
||||||
|
$client->loginUser($admin);
|
||||||
|
$client->request('GET', '/admin/export/2026/3/pdf');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
self::assertStringContainsString('application/pdf', $client->getResponse()->headers->get('Content-Type'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,61 @@ class HomeControllerTest extends WebTestCase
|
|||||||
self::assertResponseIsSuccessful();
|
self::assertResponseIsSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEventsPageWithCityFilter(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$client->request('GET', '/evenements?city=Paris');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEventsPageWithDateFilter(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$client->request('GET', '/evenements?date=2026-08-01');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEventsPageWithAllFilters(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$client->request('GET', '/evenements?q=test&city=Paris&date=2026-08-01');
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEventStockRoute(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$user = new User();
|
||||||
|
$user->setEmail('test-stock-'.uniqid().'@example.com');
|
||||||
|
$user->setFirstName('Stock');
|
||||||
|
$user->setLastName('Test');
|
||||||
|
$user->setPassword('hashed');
|
||||||
|
$user->setRoles(['ROLE_ORGANIZER']);
|
||||||
|
$user->setIsApproved(true);
|
||||||
|
$user->setIsVerified(true);
|
||||||
|
$em->persist($user);
|
||||||
|
|
||||||
|
$event = new \App\Entity\Event();
|
||||||
|
$event->setAccount($user);
|
||||||
|
$event->setTitle('Stock Event');
|
||||||
|
$event->setStartAt(new \DateTimeImmutable('2026-08-01 10:00'));
|
||||||
|
$event->setEndAt(new \DateTimeImmutable('2026-08-01 18:00'));
|
||||||
|
$event->setAddress('1 rue');
|
||||||
|
$event->setZipcode('75001');
|
||||||
|
$event->setCity('Paris');
|
||||||
|
$event->setIsOnline(true);
|
||||||
|
$em->persist($event);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$client->request('GET', '/evenement/'.$event->getId().'/stock');
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
public function testEventDetailNotFoundReturns404(): void
|
public function testEventDetailNotFoundReturns404(): void
|
||||||
{
|
{
|
||||||
$client = static::createClient();
|
$client = static::createClient();
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Tests\Controller;
|
namespace App\Tests\Controller;
|
||||||
|
|
||||||
|
use App\Entity\OrganizerInvitation;
|
||||||
|
use App\Service\MailerService;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class UnsubscribeControllerTest extends WebTestCase
|
class UnsubscribeControllerTest extends WebTestCase
|
||||||
@@ -31,4 +34,42 @@ class UnsubscribeControllerTest extends WebTestCase
|
|||||||
|
|
||||||
self::assertResponseIsSuccessful();
|
self::assertResponseIsSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPostRefusesInvitationsAndNotifiesAdmin(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$em = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
|
||||||
|
$email = 'unsub-invite-'.uniqid().'@example.com';
|
||||||
|
|
||||||
|
$invitation = new OrganizerInvitation();
|
||||||
|
$invitation->setCompanyName('Asso Unsub');
|
||||||
|
$invitation->setFirstName('Test');
|
||||||
|
$invitation->setLastName('Unsub');
|
||||||
|
$invitation->setEmail($email);
|
||||||
|
$invitation->setStatus(OrganizerInvitation::STATUS_SENT);
|
||||||
|
$em->persist($invitation);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$mailer = $this->createMock(MailerService::class);
|
||||||
|
$mailer->expects(self::once())->method('sendEmail')->with(
|
||||||
|
$this->anything(),
|
||||||
|
$this->stringContains('Desinscription'),
|
||||||
|
$this->stringContains($email),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
static::getContainer()->set(MailerService::class, $mailer);
|
||||||
|
|
||||||
|
$token = base64_encode($email);
|
||||||
|
$client->request('POST', '/unsubscribe/'.$token);
|
||||||
|
|
||||||
|
self::assertResponseIsSuccessful();
|
||||||
|
|
||||||
|
$freshEm = static::getContainer()->get(EntityManagerInterface::class);
|
||||||
|
$updated = $freshEm->getRepository(OrganizerInvitation::class)->find($invitation->getId());
|
||||||
|
self::assertSame(OrganizerInvitation::STATUS_REFUSED, $updated->getStatus());
|
||||||
|
self::assertNotNull($updated->getRespondedAt());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,18 @@ class MailerServiceTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetAdminEmail(): void
|
||||||
|
{
|
||||||
|
$service = $this->createService();
|
||||||
|
self::assertSame('contact@test.com', $service->getAdminEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAdminFrom(): void
|
||||||
|
{
|
||||||
|
$service = $this->createService();
|
||||||
|
self::assertSame('E-Ticket <contact@test.com>', $service->getAdminFrom());
|
||||||
|
}
|
||||||
|
|
||||||
public function testSendEmailSkipsUnsubscribedRecipient(): void
|
public function testSendEmailSkipsUnsubscribedRecipient(): void
|
||||||
{
|
{
|
||||||
$this->unsubscribeManager->method('isUnsubscribed')->willReturn(true);
|
$this->unsubscribeManager->method('isUnsubscribed')->willReturn(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user