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); }); 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, updatedAt: now()->toImmutable(), ); $repository->persist($client); // WHEN expect(fn () => $usecase->execute($input)) // THEN ->toThrow(ClientNameAlreadyExistsException::class); }); });