stashing test fixing to work on other things
Sync GitHub / sync (push) Successful in 7s

This commit is contained in:
2026-06-21 16:02:58 -04:00
parent 75b2bd6d8c
commit 9886867c36
3 changed files with 162 additions and 163 deletions
+3
View File
@@ -0,0 +1,3 @@
tests/Unit/Core/UtilitiesTest.php
tests/Unit/Trait/HasLoggerTraitTest.php
tests/Unit/Trait/MakeNonceTraitTest.php
+91
View File
@@ -0,0 +1,91 @@
App\Tests\Unit\Command\GenerateBackupCodesCommandTest::testExecuteWithCustomCount
App\Tests\Unit\Command\GenerateBackupCodesCommandTest::testExecuteWithCustomCount
App\Tests\Unit\Command\GenerateBackupCodesCommandTest::testExecuteWithDefaultCount
App\Tests\Unit\Command\GenerateBackupCodesCommandTest::testExecuteWithDefaultCount
App\Tests\Unit\Command\GenerateBackupCodesCommandTest::testExecuteWithZeroCount
App\Tests\Unit\Command\GenerateBackupCodesCommandTest::testExecuteWithZeroCount
App\Tests\Unit\Core\ConfigBagTest::testGettersWithExplicitValues
App\Tests\Unit\Core\ConfigBagTest::testGettersWithExplicitValues
App\Tests\Unit\Core\ConfigBagTest::testIpTtlFallsBackToNullWhenNull
App\Tests\Unit\Core\ConfigBagTest::testIpTtlFallsBackToNullWhenNull
App\Tests\Unit\Core\ConfigBagTest::testIpTtlFallsBackToNullWhenZero
App\Tests\Unit\Core\ConfigBagTest::testIpTtlFallsBackToNullWhenZero
App\Tests\Unit\Core\ConfigBagTest::testTotpUriFallsBackToUtilitiesWhenEmpty
App\Tests\Unit\Core\ConfigBagTest::testTotpUriFallsBackToUtilitiesWhenEmpty
App\Tests\Unit\Core\MonitorCacheKeysTest::testClearWhenEmpty
App\Tests\Unit\Core\MonitorCacheKeysTest::testClearWhenEmpty
App\Tests\Unit\Core\MonitorCacheKeysTest::testClearWhenNotEmpty
App\Tests\Unit\Core\MonitorCacheKeysTest::testClearWhenNotEmpty
App\Tests\Unit\Core\MonitorCacheKeysTest::testCommitDelegates
App\Tests\Unit\Core\MonitorCacheKeysTest::testCommitDelegates
App\Tests\Unit\Core\MonitorCacheKeysTest::testConstructorDoesNotInitializeWhenItemsExist
App\Tests\Unit\Core\MonitorCacheKeysTest::testConstructorDoesNotInitializeWhenItemsExist
App\Tests\Unit\Core\MonitorCacheKeysTest::testConstructorInitializesWhenKeyListMissing
App\Tests\Unit\Core\MonitorCacheKeysTest::testConstructorInitializesWhenKeyListMissing
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemsThrowsOnPrivateKey
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemsThrowsOnPrivateKey
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemsUpdatesKeyList
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemsUpdatesKeyList
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemThrowsOnPrivateKey
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemThrowsOnPrivateKey
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemUpdatesKeyList
App\Tests\Unit\Core\MonitorCacheKeysTest::testDeleteItemUpdatesKeyList
App\Tests\Unit\Core\MonitorCacheKeysTest::testGetChanges
App\Tests\Unit\Core\MonitorCacheKeysTest::testGetChanges
App\Tests\Unit\Core\MonitorCacheKeysTest::testGetItemDelegates
App\Tests\Unit\Core\MonitorCacheKeysTest::testGetItemDelegates
App\Tests\Unit\Core\MonitorCacheKeysTest::testGetItemsDelegates
App\Tests\Unit\Core\MonitorCacheKeysTest::testGetKeys
App\Tests\Unit\Core\MonitorCacheKeysTest::testGetKeys
App\Tests\Unit\Core\MonitorCacheKeysTest::testHasItemDelegates
App\Tests\Unit\Core\MonitorCacheKeysTest::testHasItemDelegates
App\Tests\Unit\Core\MonitorCacheKeysTest::testMarkClean
App\Tests\Unit\Core\MonitorCacheKeysTest::testMarkClean
App\Tests\Unit\Core\MonitorCacheKeysTest::testSaveDeferredUpdatesKeyList
App\Tests\Unit\Core\MonitorCacheKeysTest::testSaveDeferredUpdatesKeyList
App\Tests\Unit\Core\MonitorCacheKeysTest::testSaveThrowsOnPrivateKey
App\Tests\Unit\Core\MonitorCacheKeysTest::testSaveThrowsOnPrivateKey
App\Tests\Unit\Core\MonitorCacheKeysTest::testSaveUpdatesKeyList
App\Tests\Unit\Core\MonitorCacheKeysTest::testSaveUpdatesKeyList
App\Tests\Unit\Core\PersistCacheTest::testBootWhenCacheIsEmpty
App\Tests\Unit\Core\PersistCacheTest::testBootWhenCacheIsEmpty
App\Tests\Unit\Core\PersistCacheTest::testBootWhenCacheIsNotEmpty
App\Tests\Unit\Core\PersistCacheTest::testBootWhenCacheIsNotEmpty
App\Tests\Unit\Core\PersistCacheTest::testPersistWithChanges
App\Tests\Unit\Core\PersistCacheTest::testPersistWithChanges
App\Tests\Unit\Core\PersistCacheTest::testPersistWithNoChanges
App\Tests\Unit\Core\PersistCacheTest::testPersistWithNoChanges
App\Tests\Unit\Service\BackupCodeManagerTest::testExpireDoesNothingWhenNoBackupCodes
App\Tests\Unit\Service\BackupCodeManagerTest::testExpireDoesNothingWhenNoBackupCodes
App\Tests\Unit\Service\BackupCodeManagerTest::testExpireRemovesBackupCodes
App\Tests\Unit\Service\BackupCodeManagerTest::testExpireRemovesBackupCodes
App\Tests\Unit\Service\BackupCodeManagerTest::testGenerateCodesAreUnique
App\Tests\Unit\Service\BackupCodeManagerTest::testGenerateCodesAreUnique
App\Tests\Unit\Service\BackupCodeManagerTest::testGenerateCreatesCodes
App\Tests\Unit\Service\BackupCodeManagerTest::testGenerateCreatesCodes
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeIsCaseInsensitive
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeIsCaseInsensitive
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeStripsInvalidChars
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeStripsInvalidChars
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeWithInvalidCode
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeWithInvalidCode
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeWithMissingCode
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeWithMissingCode
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeWithValidCode
App\Tests\Unit\Service\BackupCodeManagerTest::testVerifyAndConsumeWithValidCode
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithInvalidNonce
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithInvalidNonce
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithInvalidToken
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithInvalidToken
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithIpScopeWhenDisabled
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithIpScopeWhenDisabled
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithMissingNonce
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithMissingNonce
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithUlidCollisionThrows
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithUlidCollisionThrows
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithValidBackupCode
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithValidBackupCode
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithValidTotpAndCookieScope
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithValidTotpAndCookieScope
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithValidTotpAndNoneScope
App\Tests\Unit\Service\LoginManagerTest::testCheckTokenWithValidTotpAndNoneScope
+68 -163
View File
@@ -6,40 +6,49 @@ namespace App\Tests\Unit\Service;
use App\ConfigBag;
use App\Data\Payload;
use App\Enum\Scope;
use App\Service\BackupCodeManager;
use App\Service\BackupCodeInterface;
use App\Service\DomainManager;
use App\Service\LoginManager;
use OTPHP\TOTP;
use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Clock\ClockInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
final class LoginManagerTest extends TestCase {
private function createMockItem(string $key, mixed $value = null, bool $isHit = true): CacheItemInterface {
$item = $this->createMock(CacheItemInterface::class);
$item->method('getKey')->willReturn($key);
$item->method('get')->willReturn($value);
$item->method('isHit')->willReturn($isHit);
$item->method('set')->willReturnSelf();
$item->method('expiresAfter')->willReturnSelf();
return $item;
private ?TOTP $totp = null;
private ?ClockInterface $clock = null;
private function getClock(): ClockInterface {
if ($this->clock === null) {
$now = time();
$this->clock = $this->createMock(ClockInterface::class);
$this->clock->method('now')->willReturn(new \DateTimeImmutable('@' . $now));
}
return $this->clock;
}
private function getTotp(): TOTP {
if ($this->totp === null) {
$this->totp = TOTP::generate($this->getClock());
$this->totp->setLabel('Test');
}
return $this->totp;
}
private function createConfigBag(int $ipTtl = 1800): ConfigBag {
$clock = $this->createMock(ClockInterface::class);
$utilities = $this->createMock(\App\Utilities::class);
$totp = TOTP::generate($clock);
$totp->setLabel('Test');
$clock = $this->getClock();
$cache = $this->createMock(CacheItemPoolInterface::class);
$utilities = new \App\Utilities($clock, $cache);
return new ConfigBag(
$utilities,
$clock,
3600,
$totp->getProvisioningUri(),
$this->getTotp()->getProvisioningUri(),
$ipTtl,
false,
'Error',
@@ -48,54 +57,40 @@ final class LoginManagerTest extends TestCase {
);
}
private function getCurrentTotpToken(): string {
return $this->getTotp()->now();
}
private function createManager(
CacheItemPoolInterface $cache,
?BackupCodeManager $backupCodeManager = null,
?DomainManager $domainManager = null
ArrayAdapter $cache,
?BackupCodeInterface $backupCodeManager = null,
?DomainManager $domainManager = null,
?ArrayAdapter $nonceCache = null
): LoginManager {
$bcm = $backupCodeManager ?? $this->createMock(BackupCodeManager::class);
$bcm = $backupCodeManager ?? $this->createMock(BackupCodeInterface::class);
$dm = $domainManager ?? new DomainManager(false, '');
$manager = new LoginManager($cache, $bcm, $dm);
$manager->setConfig($this->createConfigBag());
$manager->setLogger($this->createMock(LoggerInterface::class));
$nonceCache = $this->createMock(CacheItemPoolInterface::class);
$nonceItem = $this->createMockItem('nonce_abc', true, true);
$nonceCache->method('getItem')->willReturn($nonceItem);
$nonceCache->method('save')->willReturn(true);
$manager->setNonceCache($nonceCache);
$nc = $nonceCache ?? new ArrayAdapter();
if (!$nonceCache) {
$nonceItem = $nc->getItem('nonce_abc');
$nonceItem->set(true);
$nc->save($nonceItem);
}
$manager->setNonceCache($nc);
return $manager;
}
public function testCheckTokenWithValidTotpAndCookieScope(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
$cache->method('getItem')
->willReturnCallback(function ($key) {
if (str_starts_with($key, 'cookie_')) {
return $this->createMockItem($key, null, false);
}
return $this->createMockItem($key, null, false);
});
$cache->method('saveDeferred')->willReturn(true);
$cache->method('commit')->willReturn(true);
$cache->method('save')->willReturn(true);
$cache = new ArrayAdapter();
$manager = $this->createManager($cache);
$clock = $this->createMock(ClockInterface::class);
$totp = TOTP::generate($clock);
$totp->setLabel('Test');
$payload = new Payload();
$payload->id = 'user1';
$payload->token = $totp->now();
$payload->token = $this->getCurrentTotpToken();
$payload->nonce = 'abc';
$payload->json = true;
$payload->scope = Scope::Cookie;
@@ -110,23 +105,12 @@ final class LoginManagerTest extends TestCase {
}
public function testCheckTokenWithValidTotpAndNoneScope(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
$cache = new ArrayAdapter();
$manager = $this->createManager($cache);
$clock = $this->createMock(ClockInterface::class);
$totp = TOTP::generate($clock);
$totp->setLabel('Test');
$payload = new Payload();
$payload->id = 'user1';
$payload->token = $totp->now();
$payload->token = $this->getCurrentTotpToken();
$payload->nonce = 'abc';
$payload->json = false;
$payload->scope = Scope::None;
@@ -141,14 +125,7 @@ final class LoginManagerTest extends TestCase {
}
public function testCheckTokenWithInvalidToken(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
$cache = new ArrayAdapter();
$manager = $this->createManager($cache);
$payload = new Payload();
@@ -165,25 +142,9 @@ final class LoginManagerTest extends TestCase {
}
public function testCheckTokenWithValidBackupCode(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache = new ArrayAdapter();
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
$cache->method('getItem')
->willReturnCallback(function ($key) {
if (str_starts_with($key, 'cookie_')) {
return $this->createMockItem($key, null, false);
}
return $this->createMockItem($key, null, false);
});
$cache->method('saveDeferred')->willReturn(true);
$cache->method('commit')->willReturn(true);
$cache->method('save')->willReturn(true);
$backupManager = $this->createMock(BackupCodeManager::class);
$backupManager = $this->createMock(BackupCodeInterface::class);
$backupManager->method('verifyAndConsume')->willReturn(true);
$manager = $this->createManager($cache, $backupManager);
@@ -202,33 +163,12 @@ final class LoginManagerTest extends TestCase {
}
public function testCheckTokenWithIpScopeWhenDisabled(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
$cache->method('getItem')
->willReturnCallback(function ($key) {
if (str_starts_with($key, 'cookie_')) {
return $this->createMockItem($key, null, false);
}
return $this->createMockItem($key, null, false);
});
$cache->method('saveDeferred')->willReturn(true);
$cache->method('commit')->willReturn(true);
$cache->method('save')->willReturn(true);
$cache = new ArrayAdapter();
$manager = $this->createManager($cache);
$clock = $this->createMock(ClockInterface::class);
$totp = TOTP::generate($clock);
$totp->setLabel('Test');
$payload = new Payload();
$payload->id = 'user1';
$payload->token = $totp->now();
$payload->token = $this->getCurrentTotpToken();
$payload->nonce = 'abc';
$payload->json = true;
$payload->scope = Scope::Ip;
@@ -242,28 +182,19 @@ final class LoginManagerTest extends TestCase {
}
public function testCheckTokenWithInvalidNonce(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache = new ArrayAdapter();
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
// Create nonce cache with invalid nonce
$nonceCache = new ArrayAdapter();
$nonceItem = $nonceCache->getItem('nonce_bad');
$nonceItem->set(false);
$nonceCache->save($nonceItem);
$manager = $this->createManager($cache);
$nonceCache = $this->createMock(CacheItemPoolInterface::class);
$nonceItem = $this->createMockItem('nonce_bad', false, true);
$nonceCache->method('getItem')->willReturn($nonceItem);
$manager->setNonceCache($nonceCache);
$clock = $this->createMock(ClockInterface::class);
$totp = TOTP::generate($clock);
$totp->setLabel('Test');
$manager = $this->createManager($cache, nonceCache: $nonceCache);
$payload = new Payload();
$payload->id = 'user1';
$payload->token = $totp->now();
$payload->token = $this->getCurrentTotpToken();
$payload->nonce = 'bad';
$payload->json = true;
$payload->scope = Scope::None;
@@ -275,28 +206,16 @@ final class LoginManagerTest extends TestCase {
}
public function testCheckTokenWithMissingNonce(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache = new ArrayAdapter();
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
// Create empty nonce cache (missing nonce)
$nonceCache = new ArrayAdapter();
$manager = $this->createManager($cache);
$nonceCache = $this->createMock(CacheItemPoolInterface::class);
$nonceItem = $this->createMockItem('nonce_missing', null, false);
$nonceCache->method('getItem')->willReturn($nonceItem);
$manager->setNonceCache($nonceCache);
$clock = $this->createMock(ClockInterface::class);
$totp = TOTP::generate($clock);
$totp->setLabel('Test');
$manager = $this->createManager($cache, nonceCache: $nonceCache);
$payload = new Payload();
$payload->id = 'user1';
$payload->token = $totp->now();
$payload->token = $this->getCurrentTotpToken();
$payload->nonce = 'missing';
$payload->json = true;
$payload->scope = Scope::None;
@@ -308,31 +227,17 @@ final class LoginManagerTest extends TestCase {
}
public function testCheckTokenWithUlidCollisionThrows(): void {
$cache = $this->createMock(CacheItemPoolInterface::class);
$keyListItem = $this->createMockItem('__key_list', [], true);
$changeListItem = $this->createMockItem('__chg_list', [], true);
$cache->method('getItems')
->with(['__key_list', '__chg_list'])
->willReturn([$keyListItem, $changeListItem]);
$cache->method('getItem')
->willReturnCallback(function ($key) {
if (str_starts_with($key, 'cookie_')) {
// Simulate collision
return $this->createMockItem($key, 'existing', true);
}
return $this->createMockItem($key, null, false);
});
$cache = new ArrayAdapter();
$manager = $this->createManager($cache);
$clock = $this->createMock(ClockInterface::class);
$totp = TOTP::generate($clock);
$totp->setLabel('Test');
// Pre-populate cache with a cookie to simulate collision
$cookieItem = $cache->getItem('cookie_test');
$cookieItem->set('existing');
$cache->save($cookieItem);
$payload = new Payload();
$payload->id = 'user1';
$payload->token = $totp->now();
$payload->token = $this->getCurrentTotpToken();
$payload->nonce = 'abc';
$payload->json = true;
$payload->scope = Scope::Cookie;