SuperSeller3000/tests/Unit/Infrastructure/Console/CreateApiKeyCommandTest.php

116 lines
3.9 KiB
PHP
Raw Permalink Normal View History

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Infrastructure\Console;
use App\Domain\Auth\ApiKey;
use App\Domain\Auth\Repository\ApiKeyRepositoryInterface;
use App\Domain\Auth\Repository\UserRepositoryInterface;
use App\Domain\Auth\User;
use App\Infrastructure\Console\CreateApiKeyCommand;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;
final class CreateApiKeyCommandTest extends TestCase
{
private UserRepositoryInterface&MockObject $users;
private ApiKeyRepositoryInterface&MockObject $apiKeys;
private CommandTester $tester;
protected function setUp(): void
{
$this->users = $this->createMock(UserRepositoryInterface::class);
$this->apiKeys = $this->createMock(ApiKeyRepositoryInterface::class);
$this->tester = new CommandTester(new CreateApiKeyCommand($this->users, $this->apiKeys));
}
public function testFailsWhenEmailIsEmpty(): void
{
$this->tester->setInputs(['', '']);
$this->tester->execute([]);
self::assertSame(1, $this->tester->getStatusCode());
self::assertStringContainsString('Email is required', $this->tester->getDisplay());
}
public function testFailsWhenUserNotFound(): void
{
$this->users->method('findByEmail')->willReturn(null);
$this->tester->setInputs(['unknown@example.com', '']);
$this->tester->execute([]);
self::assertSame(1, $this->tester->getStatusCode());
self::assertStringContainsString('No user found', $this->tester->getDisplay());
}
public function testFailsWhenLabelIsEmpty(): void
{
$this->users->method('findByEmail')->willReturn(new User('test@example.com', 'hash'));
$this->tester->setInputs(['test@example.com', '']);
$this->tester->execute([]);
self::assertSame(1, $this->tester->getStatusCode());
self::assertStringContainsString('Label is required', $this->tester->getDisplay());
}
public function testCreatesKeyAndPrintsIt(): void
{
$user = new User('test@example.com', 'hash');
$this->users->method('findByEmail')->willReturn($user);
$savedKey = null;
$this->apiKeys->expects($this->once())
->method('save')
->willReturnCallback(static function (ApiKey $key) use (&$savedKey): void {
$savedKey = $key;
});
$this->tester->setInputs(['test@example.com', 'dev laptop']);
$this->tester->execute([]);
self::assertSame(0, $this->tester->getStatusCode());
$display = $this->tester->getDisplay();
self::assertStringContainsString('API key created', $display);
self::assertStringContainsString('dev laptop', $display);
self::assertNotNull($savedKey);
self::assertSame('dev laptop', $savedKey->getLabel());
self::assertSame(8, \strlen($savedKey->getKeyPrefix()));
self::assertStringContainsString($savedKey->getKeyPrefix(), $display);
}
public function testStoredHashVerifiesAgainstPrintedKey(): void
{
$user = new User('test@example.com', 'hash');
$this->users->method('findByEmail')->willReturn($user);
$savedKey = null;
$this->apiKeys->method('save')
->willReturnCallback(static function (ApiKey $key) use (&$savedKey): void {
$savedKey = $key;
});
$this->tester->setInputs(['test@example.com', 'ci-runner']);
$this->tester->execute([]);
self::assertNotNull($savedKey);
// SymfonyStyle table uses spaces, not pipes: " API Key <hex48> "
preg_match('/API Key\s+([a-f0-9]{48})/', $this->tester->getDisplay(), $matches);
self::assertNotEmpty($matches, 'Raw key not found in output');
$rawKey = $matches[1];
self::assertSame(substr($rawKey, 0, 8), $savedKey->getKeyPrefix());
self::assertTrue(password_verify($rawKey, $savedKey->getKeyHash()));
}
}