249 lines
9.6 KiB
PHP
249 lines
9.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit\Presentation\Console\Services;
|
|
|
|
use Application\Security\Models\Entities\Client;
|
|
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\Usecases\FindClient\FindClient;
|
|
use Infra\Adapters\Repositories\InMemoryClientRepository;
|
|
use Presentation\Console\Services\ClientConsoleResolver;
|
|
use Ramsey\Uuid\Uuid;
|
|
use Shared\DDD\Usecase\Builder\UsecaseExecutor;
|
|
use Shared\DDD\Usecase\Input;
|
|
use Shared\DDD\Usecase\Presenter;
|
|
use Shared\DDD\Usecase\Usecase;
|
|
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
|
|
|
covers(ClientConsoleResolver::class);
|
|
|
|
describe('ClientConsoleResolver Unit Tests', function (): void {
|
|
$repository = new InMemoryClientRepository();
|
|
$findClient = new FindClient($repository);
|
|
|
|
$executor = new class () implements UsecaseExecutor
|
|
{
|
|
#[\Override]
|
|
public function execute(Usecase $usecase, Input $input, ?Presenter $presenter = null): object
|
|
{
|
|
$output = $usecase->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();
|
|
});
|
|
});
|