129 lines
4.5 KiB
PHP
129 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\MessageHandler;
|
|
|
|
use App\Email\Mailer;
|
|
use App\Entity\User;
|
|
use App\Message\RoleNominationMessage;
|
|
use App\MessageHandler\RoleNominationHandler;
|
|
use App\Repository\UserRepository;
|
|
use App\Security\Role;
|
|
use App\Service\RoleApprovalUrlGenerator;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
|
|
/**
|
|
* The notification goes to the people who can act on it, and to nobody else: an account merely
|
|
* nominated for ROLE_ADMIN must not be told about other people's nominations.
|
|
*/
|
|
class RoleNominationHandlerTest extends TestCase
|
|
{
|
|
/** @var array<string, mixed>|null */
|
|
private ?array $sentOptions = null;
|
|
|
|
/** @var array<string, mixed>|null */
|
|
private ?array $sentContext = null;
|
|
|
|
private ?string $generatedRoute = null;
|
|
|
|
private ?int $generatedReferenceType = null;
|
|
|
|
public function testMailsEveryAdministrator(): void
|
|
{
|
|
$handler = $this->handler(
|
|
$this->user(7, '[email protected]'),
|
|
[
|
|
$this->user(1, '[email protected]', [Role::ADMIN]),
|
|
$this->user(2, '[email protected]', [Role::ADMIN]),
|
|
],
|
|
);
|
|
|
|
$handler(new RoleNominationMessage(7, [Role::ADMIN]));
|
|
|
|
self::assertSame(['[email protected]', '[email protected]'], $this->sentOptions['to']);
|
|
self::assertSame('email/role_nomination.html.twig', $this->sentOptions['template']);
|
|
// Labelled for a human, not the raw role string.
|
|
self::assertSame(['Administration'], $this->sentContext['roles']);
|
|
// The filtered user list, not app_admin_user_permissions: that route renders a bare htmx
|
|
// fragment, which a mail client following the link would show unstyled.
|
|
self::assertSame('app_admin_user', $this->generatedRoute);
|
|
// Absolute, because a worker has no request to borrow a host from.
|
|
self::assertSame(UrlGeneratorInterface::ABSOLUTE_URL, $this->generatedReferenceType);
|
|
self::assertSame('https://my.ep-reisen.de/generated', $this->sentContext['approvalUrl']);
|
|
}
|
|
|
|
public function testAMissingAccountIsANoOp(): void
|
|
{
|
|
$handler = $this->handler(null, [$this->user(1, '[email protected]', [Role::ADMIN])]);
|
|
|
|
$handler(new RoleNominationMessage(7, [Role::ADMIN]));
|
|
|
|
self::assertNull($this->sentOptions);
|
|
}
|
|
|
|
public function testWithoutAnAdministratorNothingIsSent(): void
|
|
{
|
|
$handler = $this->handler($this->user(7, '[email protected]'), []);
|
|
|
|
$handler(new RoleNominationMessage(7, [Role::ADMIN]));
|
|
|
|
self::assertNull($this->sentOptions);
|
|
}
|
|
|
|
/**
|
|
* @param string[] $roles
|
|
*/
|
|
private function user(int $id, string $email, array $roles = []): User
|
|
{
|
|
$user = (new User($email))->setRoles($roles);
|
|
|
|
// The id is generated by Doctrine and has no setter, but the notification links to it.
|
|
$property = new \ReflectionProperty(User::class, 'id');
|
|
$property->setValue($user, $id);
|
|
|
|
return $user;
|
|
}
|
|
|
|
/**
|
|
* @param User[] $administrators
|
|
*/
|
|
private function handler(?User $nominee, array $administrators): RoleNominationHandler
|
|
{
|
|
$userRepository = $this->createStub(UserRepository::class);
|
|
$userRepository->method('find')->willReturn($nominee);
|
|
$userRepository->method('findAdministrators')->willReturn($administrators);
|
|
|
|
$mailer = $this->createStub(Mailer::class);
|
|
$mailer
|
|
->method('createAndSendEmail')
|
|
->willReturnCallback(function (array $context, array $options): void {
|
|
$this->sentContext = $context;
|
|
$this->sentOptions = $options;
|
|
})
|
|
;
|
|
|
|
// Records what was asked of the router rather than imitating its output: what matters is
|
|
// which route the mail points at and that it is absolute, not how a query string is escaped.
|
|
$urlGenerator = $this->createStub(UrlGeneratorInterface::class);
|
|
$urlGenerator
|
|
->method('generate')
|
|
->willReturnCallback(function (string $route, array $parameters, int $referenceType): string {
|
|
$this->generatedRoute = $route;
|
|
$this->generatedReferenceType = $referenceType;
|
|
|
|
return 'https://my.ep-reisen.de/generated';
|
|
})
|
|
;
|
|
|
|
return new RoleNominationHandler(
|
|
$userRepository,
|
|
$mailer,
|
|
new RoleApprovalUrlGenerator($urlGenerator),
|
|
$this->createStub(LoggerInterface::class),
|
|
);
|
|
}
|
|
}
|