feat(console): add CreateClient command and enforce unique slug for ApiClient
This commit is contained in:
parent
0a5603330a
commit
d3786aaed6
16 changed files with 157 additions and 12 deletions
|
|
@ -6,6 +6,7 @@ namespace Database\Factories;
|
|||
|
||||
use Application\Security\Ports\Services\ClientCredentialGenerator;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use Infra\Database\Models\ApiClient;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
|
|
@ -24,9 +25,12 @@ final class ApiClientFactory extends Factory
|
|||
{
|
||||
$clientIdentifierGenerator = resolve(ClientCredentialGenerator::class);
|
||||
|
||||
$name = fake()->company();
|
||||
|
||||
return [
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'name' => fake()->company(),
|
||||
'name' => $name,
|
||||
'slug' => Str::slug($name),
|
||||
'identifier' => $clientIdentifierGenerator->generateIdentifier()->value,
|
||||
'secret' => $clientIdentifierGenerator->generateSecret()->value,
|
||||
'is_active' => true,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ return new class () extends Migration
|
|||
$table->uuid('id')->primary();
|
||||
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('identifier')->unique();
|
||||
$table->text('secret');
|
||||
$table->boolean('is_active');
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ namespace Database\Seeders;
|
|||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Str;
|
||||
use Infra\Database\Models\ApiClient;
|
||||
|
||||
final class ApiClientSeeder extends Seeder
|
||||
|
|
@ -20,6 +21,7 @@ final class ApiClientSeeder extends Seeder
|
|||
[
|
||||
'id' => $devClient['id'],
|
||||
'name' => $devClient['name'],
|
||||
'slug' => Str::slug($devClient['name']),
|
||||
'secret' => $devClient['secret'],
|
||||
'is_active' => true,
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\Security\Exceptions;
|
||||
|
||||
final class ClientNameAlreadyExistsException extends \DomainException
|
||||
{
|
||||
public static function forName(string $name): self
|
||||
{
|
||||
return new self("A client with the name '{$name}' (or similar) already exists.");
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ namespace Application\Security\Ports\Repositories;
|
|||
|
||||
use Application\Security\Models\Entities\Client;
|
||||
use Application\Security\Models\ValueObjects\ClientIdentifier;
|
||||
use Application\Security\Models\ValueObjects\ClientName;
|
||||
use Application\Security\Models\ValueObjects\Entities\ClientId;
|
||||
use Shared\DDD\Port\Repository;
|
||||
use Shared\Paginated;
|
||||
|
|
@ -22,6 +23,8 @@ interface ClientRepository extends Repository
|
|||
|
||||
public function delete(Client $client): void;
|
||||
|
||||
public function existsByName(ClientName $name): bool;
|
||||
|
||||
public function findByIdentifier(ClientIdentifier $identifier): ?Client;
|
||||
|
||||
/** @return Paginated<Client> */
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Application\Security\Usecases\CreateClient;
|
||||
|
||||
use Application\Security\Events\ClientSecretGenerated;
|
||||
use Application\Security\Exceptions\ClientNameAlreadyExistsException;
|
||||
use Application\Security\Models\Entities\Client;
|
||||
use Application\Security\Models\ValueObjects\Entities\ClientId;
|
||||
use Application\Security\Ports\Repositories\ClientRepository;
|
||||
|
|
@ -30,6 +31,10 @@ final readonly class CreateClient implements Usecase
|
|||
{
|
||||
Assert::isInstanceOf($input, Input::class);
|
||||
|
||||
if ($this->clientRepository->existsByName($input->name)) {
|
||||
throw ClientNameAlreadyExistsException::forName($input->name->value);
|
||||
}
|
||||
|
||||
$clientId = $input->clientId ?? new ClientId(Uuid::uuid7());
|
||||
$identifier = $this->credentialGenerator->generateIdentifier();
|
||||
$secret = $this->credentialGenerator->generateSecret();
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ namespace Infra\Adapters\Repositories;
|
|||
|
||||
use Application\Security\Models\Entities\Client;
|
||||
use Application\Security\Models\ValueObjects\ClientIdentifier;
|
||||
use Application\Security\Models\ValueObjects\ClientName;
|
||||
use Application\Security\Models\ValueObjects\Entities\ClientId;
|
||||
use Application\Security\Ports\Repositories\ClientRepository;
|
||||
use Illuminate\Support\Str;
|
||||
use Infra\Database\Models\ApiClient;
|
||||
use Shared\Paginated;
|
||||
use Shared\Pagination;
|
||||
|
|
@ -21,6 +23,7 @@ final class ApiClientRepository implements ClientRepository
|
|||
['id' => $client->id->value],
|
||||
[
|
||||
'name' => $client->name,
|
||||
'slug' => Str::slug($client->name->value),
|
||||
'identifier' => $client->identifier,
|
||||
'secret' => $client->secret,
|
||||
'is_active' => $client->isActive,
|
||||
|
|
@ -28,6 +31,14 @@ final class ApiClientRepository implements ClientRepository
|
|||
);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function existsByName(ClientName $name): bool
|
||||
{
|
||||
return ApiClient::query()
|
||||
->where('slug', Str::slug($name->value))
|
||||
->exists();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function findByIdentifier(ClientIdentifier $identifier): ?Client
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ namespace Infra\Adapters\Repositories;
|
|||
|
||||
use Application\Security\Models\Entities\Client;
|
||||
use Application\Security\Models\ValueObjects\ClientIdentifier;
|
||||
use Application\Security\Models\ValueObjects\ClientName;
|
||||
use Application\Security\Models\ValueObjects\Entities\ClientId;
|
||||
use Application\Security\Ports\Repositories\ClientRepository;
|
||||
use Illuminate\Support\Str;
|
||||
use Shared\Paginated;
|
||||
use Shared\Pagination;
|
||||
|
||||
|
|
@ -60,6 +62,20 @@ final class InMemoryClientRepository implements ClientRepository
|
|||
return null;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function existsByName(ClientName $name): bool
|
||||
{
|
||||
$slug = Str::slug($name->value);
|
||||
|
||||
foreach ($this->clients as $client) {
|
||||
if (Str::slug($client->name->value) === $slug) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function findAll(Pagination $pagination): Paginated
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use Infra\Casts\UuidCast;
|
|||
/**
|
||||
* @property \Ramsey\Uuid\UuidInterface $id
|
||||
* @property \Application\Security\Models\ValueObjects\ClientName $name
|
||||
* @property string $slug
|
||||
* @property \Application\Security\Models\ValueObjects\ClientIdentifier $identifier
|
||||
* @property \Application\Security\Models\ValueObjects\ClientSecret $secret
|
||||
* @property bool $is_active
|
||||
|
|
@ -49,6 +50,7 @@ final class ApiClient extends Authenticatable
|
|||
protected $fillable = [
|
||||
'id',
|
||||
'name',
|
||||
'slug',
|
||||
'identifier',
|
||||
'secret',
|
||||
'is_active',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace Presentation\Console\Commands;
|
||||
|
||||
use Application\Security\Exceptions\ClientNameAlreadyExistsException;
|
||||
use Application\Security\Models\ValueObjects\ClientName;
|
||||
use Application\Security\Ports\Repositories\ClientRepository;
|
||||
use Application\Security\Usecases\CreateClient\CreateClient;
|
||||
use Application\Security\Usecases\CreateClient\Input;
|
||||
use Illuminate\Console\Command;
|
||||
|
|
@ -25,6 +27,12 @@ final class CreateClientCommand extends Command
|
|||
*/
|
||||
protected $description = 'Create a new API client (interactive wizard)';
|
||||
|
||||
public function __construct(
|
||||
private readonly ClientRepository $clientRepository,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle(
|
||||
CreateClient $usecase,
|
||||
UsecaseExecutor $executor,
|
||||
|
|
@ -71,8 +79,14 @@ final class CreateClientCommand extends Command
|
|||
}
|
||||
|
||||
try {
|
||||
return new ClientName($nameInput);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$clientName = new ClientName($nameInput);
|
||||
|
||||
if ($this->clientRepository->existsByName($clientName)) {
|
||||
throw ClientNameAlreadyExistsException::forName($clientName->value);
|
||||
}
|
||||
|
||||
return $clientName;
|
||||
} catch (\InvalidArgumentException | ClientNameAlreadyExistsException $e) {
|
||||
$this->error($e->getMessage());
|
||||
|
||||
if ($attempt < $maxAttempts) {
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ describe('VerifyHmacSignature Middleware Feature Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'name' => 'Inactive Client',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => false,
|
||||
|
|
@ -230,6 +231,7 @@ describe('VerifyHmacSignature Middleware Feature Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'name' => 'Active Client',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => true,
|
||||
|
|
@ -267,6 +269,7 @@ describe('VerifyHmacSignature Middleware Feature Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'name' => 'Active Client',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => true,
|
||||
|
|
@ -303,6 +306,7 @@ describe('VerifyHmacSignature Middleware Feature Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'name' => 'Valid API Client',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => true,
|
||||
|
|
@ -336,6 +340,7 @@ describe('VerifyHmacSignature Middleware Feature Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'name' => 'Valid API Client',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => true,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ describe('CreateClientCommand', function (): void {
|
|||
$this->artisan('client:create')
|
||||
|
||||
// THEN
|
||||
->expectsQuestion('Client name (e.g. "Production Gateway")', $clientName)
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', $clientName)
|
||||
->expectsConfirmation('Create this API client?', 'yes')
|
||||
->expectsOutputToContain("API client '{$clientName}' created successfully.")
|
||||
->expectsOutputToContain('Store the secret securely')
|
||||
|
|
@ -42,7 +42,7 @@ describe('CreateClientCommand', function (): void {
|
|||
$this->artisan('client:create')
|
||||
|
||||
// THEN
|
||||
->expectsQuestion('Client name (e.g. "Production Gateway")', $clientName)
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', $clientName)
|
||||
->expectsConfirmation('Create this API client?', 'no')
|
||||
->expectsOutput('Client creation cancelled.')
|
||||
->assertSuccessful();
|
||||
|
|
@ -55,9 +55,9 @@ describe('CreateClientCommand', function (): void {
|
|||
// GIVEN
|
||||
/** @var \Tests\TestCase $this */
|
||||
$this->artisan('client:create')
|
||||
->expectsQuestion('Client name (e.g. "Production Gateway")', '')
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', '')
|
||||
->expectsOutput('Client name cannot be empty.')
|
||||
->expectsQuestion('Client name (e.g. "Production Gateway")', 'Valid Gateway')
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', 'Valid Gateway')
|
||||
|
||||
// WHEN
|
||||
->expectsConfirmation('Create this API client?', 'yes')
|
||||
|
|
@ -78,15 +78,36 @@ describe('CreateClientCommand', function (): void {
|
|||
$this->artisan('client:create')
|
||||
|
||||
// WHEN
|
||||
->expectsQuestion('Client name (e.g. "Production Gateway")', '')
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', '')
|
||||
->expectsOutput('Client name cannot be empty.')
|
||||
->expectsQuestion('Client name (e.g. "Production Gateway")', '')
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', '')
|
||||
->expectsOutput('Client name cannot be empty.')
|
||||
->expectsQuestion('Client name (e.g. "Production Gateway")', '')
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', '')
|
||||
->expectsOutput('Client name cannot be empty.')
|
||||
->expectsOutput('Maximum number of attempts reached. Aborting.')
|
||||
|
||||
// THEN
|
||||
->assertFailed();
|
||||
});
|
||||
|
||||
it('retries when the client name already exists', function (): void {
|
||||
// GIVEN
|
||||
/** @var \Tests\TestCase $this */
|
||||
ApiClient::factory()->create([
|
||||
'name' => 'Existing Gateway',
|
||||
'slug' => 'existing-gateway',
|
||||
]);
|
||||
|
||||
// WHEN
|
||||
$this->artisan('client:create')
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', 'Existing Gateway')
|
||||
->expectsOutput("A client with the name 'Existing Gateway' (or similar) already exists.")
|
||||
->expectsQuestion('Client name (e.g. "Ones Branding website")', 'New Gateway')
|
||||
->expectsConfirmation('Create this API client?', 'yes')
|
||||
|
||||
// THEN
|
||||
->expectsOutputToContain("API client 'New Gateway' created successfully.")
|
||||
->assertSuccessful();
|
||||
|
||||
$client = ApiClient::query()->where('name', 'New Gateway')->first();
|
||||
expect($client)->not->toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ describe('ListClientsCommand', function (): void {
|
|||
$client1 = ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Production Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('a', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('b', 86),
|
||||
'is_active' => true,
|
||||
|
|
@ -39,6 +40,7 @@ describe('ListClientsCommand', function (): void {
|
|||
$client2 = ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Backup Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('c', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('d', 86),
|
||||
'is_active' => false,
|
||||
|
|
@ -79,6 +81,7 @@ describe('ListClientsCommand', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => "Client {$i}",
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat((string) $i, 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat((string) $i, 86),
|
||||
'is_active' => true,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ describe('ToggleClientStatusCommand', function (): void {
|
|||
$client = ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Production Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('a', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('b', 86),
|
||||
'is_active' => true,
|
||||
|
|
@ -63,6 +64,7 @@ describe('ToggleClientStatusCommand', function (): void {
|
|||
$client = ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Backup Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('c', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('d', 86),
|
||||
'is_active' => false,
|
||||
|
|
@ -84,6 +86,7 @@ describe('ToggleClientStatusCommand', function (): void {
|
|||
$client = ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Special Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('e', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('f', 86),
|
||||
'is_active' => false,
|
||||
|
|
@ -105,6 +108,7 @@ describe('ToggleClientStatusCommand', function (): void {
|
|||
$client = ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Special Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('g', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('h', 86),
|
||||
'is_active' => true,
|
||||
|
|
@ -127,6 +131,7 @@ describe('ToggleClientStatusCommand', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Some Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => $identifier,
|
||||
'secret' => 'arcadia_sec_' . str_repeat('z', 86),
|
||||
'is_active' => true,
|
||||
|
|
@ -147,6 +152,7 @@ describe('ToggleClientStatusCommand', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Production Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('a', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('b', 86),
|
||||
'is_active' => true,
|
||||
|
|
@ -155,6 +161,7 @@ describe('ToggleClientStatusCommand', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => (string) Uuid::uuid7(),
|
||||
'name' => 'Backup Gateway',
|
||||
'slug' => 'test-' . uniqid(),
|
||||
'identifier' => 'arcadia_id_' . str_repeat('c', 32),
|
||||
'secret' => 'arcadia_sec_' . str_repeat('d', 86),
|
||||
'is_active' => false,
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ describe('ApiClientRepository Integration Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => $rawId,
|
||||
'name' => 'Initial Client',
|
||||
'slug' => 'initial-client',
|
||||
'identifier' => $identifier,
|
||||
'secret' => $oldSecret,
|
||||
'is_active' => true,
|
||||
|
|
@ -103,6 +104,7 @@ describe('ApiClientRepository Integration Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => $rawId,
|
||||
'name' => 'Existing Client',
|
||||
'slug' => 'existing-client',
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => true,
|
||||
|
|
@ -130,6 +132,7 @@ describe('ApiClientRepository Integration Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => $rawId,
|
||||
'name' => 'Searchable Client',
|
||||
'slug' => 'searchable-client',
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => true,
|
||||
|
|
@ -157,6 +160,7 @@ describe('ApiClientRepository Integration Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => $rawId,
|
||||
'name' => 'Client to Delete',
|
||||
'slug' => 'client-to-delete',
|
||||
'identifier' => $identifier,
|
||||
'secret' => $secret,
|
||||
'is_active' => true,
|
||||
|
|
@ -189,6 +193,7 @@ describe('ApiClientRepository Integration Tests', function (): void {
|
|||
ApiClient::query()->create([
|
||||
'id' => Uuid::uuid7()->toString(),
|
||||
'name' => "Client $i",
|
||||
'slug' => "client-$i",
|
||||
'identifier' => 'arcadia_id_' . str_pad((string) $i, 32, '0', STR_PAD_LEFT),
|
||||
'secret' => 'arcadia_sec_' . str_pad((string) $i, 86, '0', STR_PAD_LEFT),
|
||||
'is_active' => true,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Application\Security\Usecases\CreateClient;
|
||||
|
||||
use Application\Security\Events\ClientSecretGenerated;
|
||||
use Application\Security\Exceptions\ClientNameAlreadyExistsException;
|
||||
use Application\Security\Models\Entities\Client;
|
||||
use Application\Security\Models\ValueObjects\ClientIdentifier;
|
||||
use Application\Security\Models\ValueObjects\ClientName;
|
||||
use Application\Security\Models\ValueObjects\ClientSecret;
|
||||
|
|
@ -117,4 +119,35 @@ describe('CreateClient Usecase Unit Tests', function (): void {
|
|||
expect($savedClient)->not->toBeNull()
|
||||
->and($savedClient?->id)->toBe($explicitId);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue