execute($input); return $presenter !== null ? $presenter->present($output) : $output; } }; beforeEach(function () use ($repository): void { $repository->clear(); }); it('provides shell autocompletion suggestions from directory', function () use ($repository, $findClient, $executor): void { // GIVEN $client1 = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('Production Gateway'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('a', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('1', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $client2 = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('Staging Hub'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('b', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('2', 86)), isActive: false, updatedAt: new \DateTimeImmutable(), ); $repository->add($client1, $client2); $resolver = new ClientConsoleResolver($repository, $findClient, $executor); $suggestions = new CompletionSuggestions(); // WHEN $resolver->complete($suggestions); // THEN $values = $suggestions->getValueSuggestions(); expect($values)->toHaveCount(2) ->and($values[0]->getValue())->toBe('arcadia_id_' . str_repeat('a', 32)) ->and($values[0]->getDescription())->toBe('Production Gateway (Active)') ->and($values[1]->getValue())->toBe('arcadia_id_' . str_repeat('b', 32)) ->and($values[1]->getDescription())->toBe('Staging Hub (Inactive)'); }); it('returns default search options when query is empty', function () use ($repository, $findClient, $executor): void { // GIVEN $client = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('App Alpha'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('c', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('3', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $repository->add($client); $resolver = new ClientConsoleResolver($repository, $findClient, $executor); // WHEN $options = $resolver->searchOptions(' '); // THEN expect($options)->toHaveKey('arcadia_id_' . str_repeat('c', 32)) ->and($options['arcadia_id_' . str_repeat('c', 32)])->toBe('App Alpha (Active) - arcadia_id_' . str_repeat('c', 32)); }); it('returns searched options when query is provided', function () use ($repository, $findClient, $executor): void { // GIVEN $client = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('Beta Portal'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('d', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('4', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $repository->add($client); $resolver = new ClientConsoleResolver($repository, $findClient, $executor); // WHEN $options = $resolver->searchOptions('beta'); // THEN expect($options)->toHaveKey('arcadia_id_' . str_repeat('d', 32)) ->and($options['arcadia_id_' . str_repeat('d', 32)])->toBe('Beta Portal (Active) - arcadia_id_' . str_repeat('d', 32)); }); it('returns empty array when search options encounter an exception', function () use ($repository, $findClient): void { // GIVEN $failingExecutor = new class () implements UsecaseExecutor { #[\Override] public function execute(Usecase $usecase, Input $input, ?Presenter $presenter = null): object { throw new \RuntimeException('Execution failed'); } }; $resolver = new ClientConsoleResolver($repository, $findClient, $failingExecutor); // WHEN $options = $resolver->searchOptions('broken'); // THEN expect($options)->toBe([]); }); it('finds a client by identifier pattern embedded in string', function () use ($repository, $findClient, $executor): void { // GIVEN $identifier = 'arcadia_id_' . str_repeat('e', 32); $client = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('Target Client'), identifier: new ClientIdentifier($identifier), secret: new ClientSecret('arcadia_sec_' . str_repeat('5', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $repository->add($client); $resolver = new ClientConsoleResolver($repository, $findClient, $executor); // WHEN $found = $resolver->find("Selected: {$identifier} (Active)"); // THEN expect($found)->not->toBeNull() ->and($found->identifier->value)->toBe($identifier); }); it('finds a client by valid UUIDv7 string', function () use ($repository, $findClient, $executor): void { // GIVEN $uuid7 = Uuid::uuid7(); $client = Client::hydrate( id: new ClientId($uuid7), name: new ClientName('UUID Client'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('f', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('6', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $repository->add($client); $resolver = new ClientConsoleResolver($repository, $findClient, $executor); // WHEN $found = $resolver->find($uuid7->toString()); // THEN expect($found)->not->toBeNull() ->and($found->id->value)->toBe($uuid7->toString()); }); it('finds a client by exact search match when single result', function () use ($repository, $findClient, $executor): void { // GIVEN $client = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('Unique App'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('1', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('7', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $repository->add($client); $resolver = new ClientConsoleResolver($repository, $findClient, $executor); // WHEN $found = $resolver->find('Unique App'); // THEN expect($found)->not->toBeNull() ->and($found->name->value)->toBe('Unique App'); }); it('finds a client among multiple search results by matching exact name', function () use ($repository, $findClient, $executor): void { // GIVEN $client1 = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('Payment Service'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('2', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('8', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $client2 = Client::hydrate( id: new ClientId(Uuid::uuid7()), name: new ClientName('Payment Gateway'), identifier: new ClientIdentifier('arcadia_id_' . str_repeat('3', 32)), secret: new ClientSecret('arcadia_sec_' . str_repeat('9', 86)), isActive: true, updatedAt: new \DateTimeImmutable(), ); $repository->add($client1, $client2); $resolver = new ClientConsoleResolver($repository, $findClient, $executor); // WHEN $found = $resolver->find('Payment Service'); // THEN expect($found)->not->toBeNull() ->and($found->name->value)->toBe('Payment Service'); }); it('returns null when input is empty or client not found', function () use ($repository, $findClient, $executor): void { // GIVEN $resolver = new ClientConsoleResolver($repository, $findClient, $executor); // WHEN & THEN expect($resolver->find(''))->toBeNull() ->and($resolver->find(' '))->toBeNull() ->and($resolver->find('non-existent-app'))->toBeNull(); }); });