2026-08-18 12:03:25 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Tests\Unit\Application\Security\Usecases\CreateClient;
|
|
|
|
|
|
|
|
|
|
use Application\Security\Events\ClientSecretGenerated;
|
2026-08-18 17:31:29 +02:00
|
|
|
use Application\Security\Exceptions\ClientNameAlreadyExistsException;
|
|
|
|
|
use Application\Security\Models\Entities\Client;
|
2026-08-18 12:03:25 +02:00
|
|
|
use Application\Security\Models\ValueObjects\ClientIdentifier;
|
|
|
|
|
use Application\Security\Models\ValueObjects\ClientName;
|
|
|
|
|
use Application\Security\Models\ValueObjects\ClientSecret;
|
|
|
|
|
use Application\Security\Models\ValueObjects\Entities\ClientId;
|
|
|
|
|
use Application\Security\Ports\Services\ClientCredentialGenerator;
|
|
|
|
|
use Application\Security\Usecases\CreateClient\CreateClient;
|
|
|
|
|
use Application\Security\Usecases\CreateClient\Input;
|
|
|
|
|
use Application\Security\Usecases\CreateClient\Output;
|
|
|
|
|
use Infra\Adapters\Repositories\InMemoryClientRepository;
|
|
|
|
|
use Mockery;
|
|
|
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
|
use Shared\DDD\Event\DomainEventDispatcher;
|
|
|
|
|
|
|
|
|
|
covers(
|
|
|
|
|
CreateClient::class,
|
|
|
|
|
Input::class,
|
|
|
|
|
Output::class,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
describe('CreateClient Usecase Unit Tests', function (): void {
|
|
|
|
|
$repository = new InMemoryClientRepository();
|
|
|
|
|
|
|
|
|
|
beforeEach(function () use ($repository): void {
|
|
|
|
|
$repository->clear();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('creates and persists a new client with auto-generated id', function () use ($repository): void {
|
|
|
|
|
// GIVEN
|
|
|
|
|
$credentialGenerator = Mockery::mock(ClientCredentialGenerator::class);
|
|
|
|
|
$dispatcher = Mockery::mock(DomainEventDispatcher::class);
|
|
|
|
|
|
|
|
|
|
$usecase = new CreateClient(
|
|
|
|
|
$repository,
|
|
|
|
|
$credentialGenerator,
|
|
|
|
|
$dispatcher
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$name = new ClientName('E-commerce Gateway');
|
|
|
|
|
$input = new Input($name);
|
|
|
|
|
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('a', 32));
|
|
|
|
|
$secret = new ClientSecret('arcadia_sec_' . str_repeat('b', 86));
|
|
|
|
|
|
|
|
|
|
$credentialGenerator->expects('generateIdentifier')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($identifier);
|
|
|
|
|
|
|
|
|
|
$credentialGenerator->expects('generateSecret')
|
|
|
|
|
->once()
|
|
|
|
|
->andReturn($secret);
|
|
|
|
|
|
|
|
|
|
$dispatcher->expects('dispatch')
|
|
|
|
|
->once()
|
|
|
|
|
->withArgs(fn ($event): bool => $event instanceof ClientSecretGenerated
|
|
|
|
|
&& $event->clientIdentifier->equals($identifier));
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$result = $usecase->execute($input);
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($result)->toBeInstanceOf(Output::class)
|
|
|
|
|
->and($result->clientName)->toBe($name)
|
|
|
|
|
->and($result->clientIdentifier)->toBe($identifier)
|
|
|
|
|
->and($result->clientSecret)->toBe($secret)
|
|
|
|
|
->and($result->clientId)->toBeInstanceOf(ClientId::class);
|
|
|
|
|
|
|
|
|
|
$savedClient = $repository->find($result->clientId);
|
|
|
|
|
expect($savedClient)->not->toBeNull()
|
|
|
|
|
->and($savedClient?->name)->toBe($name)
|
|
|
|
|
->and($savedClient?->identifier)->toBe($identifier)
|
|
|
|
|
->and($savedClient?->secret)->toBe($secret)
|
|
|
|
|
->and($savedClient?->isActive)->toBeTrue();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('creates and persists a new client with explicit client id', function () use ($repository): void {
|
|
|
|
|
// GIVEN
|
|
|
|
|
$credentialGenerator = Mockery::mock(ClientCredentialGenerator::class);
|
|
|
|
|
$dispatcher = Mockery::mock(DomainEventDispatcher::class);
|
|
|
|
|
|
|
|
|
|
$usecase = new CreateClient(
|
|
|
|
|
$repository,
|
|
|
|
|
$credentialGenerator,
|
|
|
|
|
$dispatcher
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$explicitId = new ClientId(Uuid::uuid7());
|
|
|
|
|
$name = new ClientName('Mobile App Client');
|
|
|
|
|
$input = new Input($name, $explicitId);
|
|
|
|
|
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('c', 32));
|
|
|
|
|
$secret = new ClientSecret('arcadia_sec_' . str_repeat('d', 86));
|
|
|
|
|
|
|
|
|
|
$credentialGenerator->allows('generateIdentifier')
|
|
|
|
|
->andReturn($identifier);
|
|
|
|
|
|
|
|
|
|
$credentialGenerator->allows('generateSecret')
|
|
|
|
|
->andReturn($secret);
|
|
|
|
|
|
|
|
|
|
$dispatcher->allows('dispatch');
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
$result = $usecase->execute($input);
|
|
|
|
|
|
|
|
|
|
// THEN
|
|
|
|
|
expect($result)->toBeInstanceOf(Output::class)
|
|
|
|
|
->and($result->clientId)->toBe($explicitId)
|
|
|
|
|
->and($result->clientName)->toBe($name)
|
|
|
|
|
->and($result->clientIdentifier)->toBe($identifier)
|
|
|
|
|
->and($result->clientSecret)->toBe($secret);
|
|
|
|
|
|
|
|
|
|
$savedClient = $repository->find($explicitId);
|
|
|
|
|
expect($savedClient)->not->toBeNull()
|
|
|
|
|
->and($savedClient?->id)->toBe($explicitId);
|
|
|
|
|
});
|
2026-08-18 17:31:29 +02:00
|
|
|
|
|
|
|
|
it('throws ClientNameAlreadyExistsException if name already exists', function () use ($repository): void {
|
|
|
|
|
// GIVEN
|
|
|
|
|
$credentialGenerator = Mockery::mock(ClientCredentialGenerator::class);
|
|
|
|
|
$dispatcher = Mockery::mock(DomainEventDispatcher::class);
|
|
|
|
|
|
|
|
|
|
$usecase = new CreateClient(
|
|
|
|
|
$repository,
|
|
|
|
|
$credentialGenerator,
|
|
|
|
|
$dispatcher
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$name = new ClientName('Duplicate Gateway');
|
|
|
|
|
$input = new Input($name);
|
|
|
|
|
|
|
|
|
|
$client = Client::hydrate(
|
|
|
|
|
id: new ClientId(Uuid::uuid7()),
|
|
|
|
|
name: new ClientName('Duplicate Gateway'),
|
|
|
|
|
identifier: new ClientIdentifier('arcadia_id_' . str_repeat('e', 32)),
|
|
|
|
|
secret: new ClientSecret('arcadia_sec_' . str_repeat('f', 86)),
|
|
|
|
|
isActive: true,
|
|
|
|
|
createdAt: now()->toImmutable(),
|
|
|
|
|
updatedAt: now()->toImmutable(),
|
|
|
|
|
);
|
|
|
|
|
$repository->persist($client);
|
|
|
|
|
|
|
|
|
|
// WHEN
|
|
|
|
|
expect(fn () => $usecase->execute($input))
|
|
|
|
|
// THEN
|
|
|
|
|
->toThrow(ClientNameAlreadyExistsException::class);
|
|
|
|
|
});
|
2026-08-18 12:03:25 +02:00
|
|
|
});
|