test: Ajouter les tests unitaires manquants (backend et frontend)
Couverture des processors (RefreshToken, RequestPasswordReset, ResetPassword, SwitchRole, UpdateUserRoles), des query handlers (HasGradesInPeriod, HasStudentsInClass), des messaging handlers (SendActivationConfirmation, SendPasswordResetEmail), et côté frontend des modules auth, roles, monitoring, types et E2E tokens.
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Infrastructure\Messaging;
|
||||
|
||||
use App\Administration\Domain\Event\PasswordResetTokenGenerated;
|
||||
use App\Administration\Domain\Model\PasswordResetToken\PasswordResetTokenId;
|
||||
use App\Administration\Infrastructure\Messaging\SendPasswordResetEmailHandler;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Mime\Email;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* Tests for SendPasswordResetEmailHandler.
|
||||
*
|
||||
* Key invariants:
|
||||
* - Sends email to the user's address with a reset link
|
||||
* - Reset URL is constructed from appUrl + token value
|
||||
* - Uses the correct Twig template
|
||||
*/
|
||||
final class SendPasswordResetEmailHandlerTest extends TestCase
|
||||
{
|
||||
private const string APP_URL = 'https://ecole-alpha.classeo.fr';
|
||||
private const string FROM_EMAIL = 'noreply@classeo.fr';
|
||||
private const string TOKEN_VALUE = 'abc123def456';
|
||||
|
||||
private MailerInterface $mailer;
|
||||
private Environment $twig;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->mailer = $this->createMock(MailerInterface::class);
|
||||
$this->twig = $this->createMock(Environment::class);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function sendsResetEmailToUser(): void
|
||||
{
|
||||
$event = $this->createEvent('user@example.com');
|
||||
|
||||
$this->twig->method('render')->willReturn('<p>Reset your password</p>');
|
||||
|
||||
$sentEmail = null;
|
||||
$this->mailer->expects(self::once())
|
||||
->method('send')
|
||||
->with(self::callback(static function (Email $email) use (&$sentEmail): bool {
|
||||
$sentEmail = $email;
|
||||
|
||||
return true;
|
||||
}));
|
||||
|
||||
$handler = new SendPasswordResetEmailHandler(
|
||||
$this->mailer,
|
||||
$this->twig,
|
||||
self::APP_URL,
|
||||
self::FROM_EMAIL,
|
||||
);
|
||||
|
||||
($handler)($event);
|
||||
|
||||
self::assertNotNull($sentEmail);
|
||||
self::assertSame('user@example.com', $sentEmail->getTo()[0]->getAddress());
|
||||
self::assertSame(self::FROM_EMAIL, $sentEmail->getFrom()[0]->getAddress());
|
||||
self::assertStringContainsString('initialisation', $sentEmail->getSubject());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function rendersTemplateWithResetUrl(): void
|
||||
{
|
||||
$event = $this->createEvent('user@example.com');
|
||||
|
||||
$this->twig->expects(self::once())
|
||||
->method('render')
|
||||
->with(
|
||||
'emails/password_reset.html.twig',
|
||||
self::callback(static function (array $vars): bool {
|
||||
return $vars['email'] === 'user@example.com'
|
||||
&& $vars['resetUrl'] === 'https://ecole-alpha.classeo.fr/reset-password/' . self::TOKEN_VALUE;
|
||||
}),
|
||||
)
|
||||
->willReturn('<p>HTML</p>');
|
||||
|
||||
$this->mailer->method('send');
|
||||
|
||||
$handler = new SendPasswordResetEmailHandler(
|
||||
$this->mailer,
|
||||
$this->twig,
|
||||
self::APP_URL,
|
||||
self::FROM_EMAIL,
|
||||
);
|
||||
|
||||
($handler)($event);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function handlesTrailingSlashInAppUrl(): void
|
||||
{
|
||||
$event = $this->createEvent('user@example.com');
|
||||
|
||||
$this->twig->expects(self::once())
|
||||
->method('render')
|
||||
->with(
|
||||
self::anything(),
|
||||
self::callback(static function (array $vars): bool {
|
||||
// Should not have double slash
|
||||
return str_contains($vars['resetUrl'], '.fr/reset-password/')
|
||||
&& !str_contains($vars['resetUrl'], '.fr//');
|
||||
}),
|
||||
)
|
||||
->willReturn('<p>HTML</p>');
|
||||
|
||||
$this->mailer->method('send');
|
||||
|
||||
$handler = new SendPasswordResetEmailHandler(
|
||||
$this->mailer,
|
||||
$this->twig,
|
||||
'https://ecole-alpha.classeo.fr/',
|
||||
self::FROM_EMAIL,
|
||||
);
|
||||
|
||||
($handler)($event);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function includesTokenValueInResetUrl(): void
|
||||
{
|
||||
$customToken = 'custom-token-value-xyz';
|
||||
$event = new PasswordResetTokenGenerated(
|
||||
tokenId: PasswordResetTokenId::generate(),
|
||||
tokenValue: $customToken,
|
||||
userId: '550e8400-e29b-41d4-a716-446655440001',
|
||||
email: 'user@example.com',
|
||||
tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
|
||||
occurredOn: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
|
||||
$this->twig->expects(self::once())
|
||||
->method('render')
|
||||
->with(
|
||||
self::anything(),
|
||||
self::callback(static function (array $vars) use ($customToken): bool {
|
||||
return str_ends_with($vars['resetUrl'], '/reset-password/' . $customToken);
|
||||
}),
|
||||
)
|
||||
->willReturn('<p>HTML</p>');
|
||||
|
||||
$this->mailer->method('send');
|
||||
|
||||
$handler = new SendPasswordResetEmailHandler(
|
||||
$this->mailer,
|
||||
$this->twig,
|
||||
self::APP_URL,
|
||||
self::FROM_EMAIL,
|
||||
);
|
||||
|
||||
($handler)($event);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function usesDefaultFromEmail(): void
|
||||
{
|
||||
$event = $this->createEvent('user@example.com');
|
||||
|
||||
$this->twig->method('render')->willReturn('<p>HTML</p>');
|
||||
|
||||
$sentEmail = null;
|
||||
$this->mailer->expects(self::once())
|
||||
->method('send')
|
||||
->with(self::callback(static function (Email $email) use (&$sentEmail): bool {
|
||||
$sentEmail = $email;
|
||||
|
||||
return true;
|
||||
}));
|
||||
|
||||
// Use default fromEmail (constructor default)
|
||||
$handler = new SendPasswordResetEmailHandler(
|
||||
$this->mailer,
|
||||
$this->twig,
|
||||
self::APP_URL,
|
||||
);
|
||||
|
||||
($handler)($event);
|
||||
|
||||
self::assertSame('noreply@classeo.fr', $sentEmail->getFrom()[0]->getAddress());
|
||||
}
|
||||
|
||||
private function createEvent(string $email): PasswordResetTokenGenerated
|
||||
{
|
||||
return new PasswordResetTokenGenerated(
|
||||
tokenId: PasswordResetTokenId::generate(),
|
||||
tokenValue: self::TOKEN_VALUE,
|
||||
userId: '550e8400-e29b-41d4-a716-446655440001',
|
||||
email: $email,
|
||||
tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
|
||||
occurredOn: new DateTimeImmutable('2026-02-15 10:00:00'),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user