Skip to content

Commit

Permalink
Ajout du handler pour créer un token (#1159)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmarchois authored Jan 22, 2025
1 parent fc32eab commit 97f7ab1
Show file tree
Hide file tree
Showing 8 changed files with 292 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/Application/User/Command/CreateTokenCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Application\User\Command;

use App\Application\CommandInterface;

final readonly class CreateTokenCommand implements CommandInterface
{
public function __construct(
public string $email,
public string $type,
) {
}
}
49 changes: 49 additions & 0 deletions src/Application/User/Command/CreateTokenCommandHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace App\Application\User\Command;

use App\Application\DateUtilsInterface;
use App\Application\IdFactoryInterface;
use App\Domain\User\Exception\UserNotFoundException;
use App\Domain\User\Repository\TokenRepositoryInterface;
use App\Domain\User\Repository\UserRepositoryInterface;
use App\Domain\User\Token;
use App\Domain\User\TokenGenerator;
use App\Domain\User\User;

final readonly class CreateTokenCommandHandler
{
public function __construct(
private IdFactoryInterface $idFactory,
private UserRepositoryInterface $userRepository,
private TokenRepositoryInterface $tokenRepository,
private DateUtilsInterface $dateUtils,
private TokenGenerator $tokenGenerator,
) {
}

public function __invoke(CreateTokenCommand $command): string
{
$user = $this->userRepository->findOneByEmail($command->email);
if (!$user instanceof User) {
throw new UserNotFoundException();
}

$expirationDate = $this->dateUtils->getNow()->modify('+1 day');
$token = $this->tokenGenerator->generate();

$this->tokenRepository->add(
new Token(
uuid: $this->idFactory->make(),
token: $token,
type: $command->type,
user: $user,
expirationDate: $expirationDate,
),
);

return $token;
}
}
9 changes: 9 additions & 0 deletions src/Domain/User/Exception/UserNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace App\Domain\User\Exception;

final class UserNotFoundException extends \Exception
{
}
16 changes: 16 additions & 0 deletions src/Domain/User/Repository/TokenRepositoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Domain\User\Repository;

use App\Domain\User\Token;

interface TokenRepositoryInterface
{
public function add(Token $token): Token;

public function remove(Token $token): void;

public function findOneByTokenAndType(string $token, string $type): ?Token;
}
13 changes: 13 additions & 0 deletions src/Domain/User/TokenGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace App\Domain\User;

final class TokenGenerator
{
public function generate(): string
{
return rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace App\Infrastructure\Persistence\Doctrine\Repository\User;

use App\Domain\User\Repository\TokenRepositoryInterface;
use App\Domain\User\Token;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

final class TokenRepository extends ServiceEntityRepository implements TokenRepositoryInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Token::class);
}

public function add(Token $token): Token
{
$this->getEntityManager()->persist($token);

return $token;
}

public function remove(Token $token): void
{
$this->getEntityManager()->remove($token);
}

public function findOneByTokenAndType(string $token, string $type): ?Token
{
return $this->createQueryBuilder('t')
->where('t.token = :token')
->andWhere('t.type = :type')
->setParameters([
'token' => $token,
'type' => $type,
])
->setMaxResults(1)
->getQuery()
->getOneOrNullResult()
;
}
}
125 changes: 125 additions & 0 deletions tests/Unit/Application/User/Command/CreateTokenCommandHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

declare(strict_types=1);

namespace App\Tests\Unit\Application\User\Command;

use App\Application\DateUtilsInterface;
use App\Application\IdFactoryInterface;
use App\Application\User\Command\CreateTokenCommand;
use App\Application\User\Command\CreateTokenCommandHandler;
use App\Domain\User\Enum\TokenTypeEnum;
use App\Domain\User\Exception\UserNotFoundException;
use App\Domain\User\Repository\TokenRepositoryInterface;
use App\Domain\User\Repository\UserRepositoryInterface;
use App\Domain\User\Token;
use App\Domain\User\TokenGenerator;
use App\Domain\User\User;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

final class CreateTokenCommandHandlerTest extends TestCase
{
private MockObject $idFactory;
private MockObject $userRepository;
private MockObject $tokenRepository;
private MockObject $dateUtils;
private MockObject $tokenGenerator;

public function setUp(): void
{
$this->idFactory = $this->createMock(IdFactoryInterface::class);
$this->userRepository = $this->createMock(UserRepositoryInterface::class);
$this->tokenRepository = $this->createMock(TokenRepositoryInterface::class);
$this->dateUtils = $this->createMock(DateUtilsInterface::class);
$this->tokenGenerator = $this->createMock(TokenGenerator::class);
}

public function testCreateToken(): void
{
$this->tokenGenerator
->expects(self::once())
->method('generate')
->willReturn('myToken');

$expirationDate = new \DateTimeImmutable('2023-08-31 08:00:00');
$this->dateUtils
->expects(self::once())
->method('getNow')
->willReturn($expirationDate);

$user = $this->createMock(User::class);
$this->userRepository
->expects(self::once())
->method('findOneByEmail')
->with('[email protected]')
->willReturn($user);

$this->idFactory
->expects(self::once())
->method('make')
->willReturn('ed81607d-476c-4e52-a234-90fddf3ba550');

$this->tokenRepository
->expects(self::once())
->method('add')
->with(
new Token(
uuid: 'ed81607d-476c-4e52-a234-90fddf3ba550',
token: 'myToken',
type: TokenTypeEnum::FORGOT_PASSWORD->value,
user: $user,
expirationDate: new \DateTime('2023-09-01 08:00:00'),
),
);

$handler = new CreateTokenCommandHandler(
$this->idFactory,
$this->userRepository,
$this->tokenRepository,
$this->dateUtils,
$this->tokenGenerator,
);
$this->assertSame(
'myToken',
($handler)(new CreateTokenCommand('[email protected]', TokenTypeEnum::FORGOT_PASSWORD->value)),
);
}

public function testUserNotFound(): void
{
$this->expectException(UserNotFoundException::class);

$this->tokenGenerator
->expects(self::never())
->method('generate');

$this->dateUtils
->expects(self::never())
->method('getNow');

$this->userRepository
->expects(self::once())
->method('findOneByEmail')
->with('[email protected]')
->willReturn(null);

$this->idFactory
->expects(self::never())
->method('make');

$this->tokenRepository
->expects(self::never())
->method('add');

$handler = new CreateTokenCommandHandler(
$this->idFactory,
$this->userRepository,
$this->tokenRepository,
$this->dateUtils,
$this->tokenGenerator,
);

($handler)(new CreateTokenCommand('[email protected]', TokenTypeEnum::FORGOT_PASSWORD->value));
}
}
19 changes: 19 additions & 0 deletions tests/Unit/Domain/User/TokenGeneratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace App\Tests\Unit\Domain\User;

use App\Domain\User\TokenGenerator;
use PHPUnit\Framework\TestCase;

final class TokenGeneratorTest extends TestCase
{
public function testGenerate(): void
{
$tokenGenerator = new TokenGenerator();

$this->assertNotEmpty($tokenGenerator->generate());
$this->assertSame(43, \strlen($tokenGenerator->generate()));
}
}

0 comments on commit 97f7ab1

Please sign in to comment.