From a859c87455eb2d4863b958b59a2910d9d5d8e167 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Tue, 19 May 2026 19:53:54 +0300 Subject: [PATCH 1/6] add auth middleware test --- .../Auth/Middleware/AuthMiddlewareTest.php | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 backend/tests/Unit/Auth/Middleware/AuthMiddlewareTest.php diff --git a/backend/tests/Unit/Auth/Middleware/AuthMiddlewareTest.php b/backend/tests/Unit/Auth/Middleware/AuthMiddlewareTest.php new file mode 100644 index 0000000..45aaf3d --- /dev/null +++ b/backend/tests/Unit/Auth/Middleware/AuthMiddlewareTest.php @@ -0,0 +1,142 @@ +now = new DateTimeImmutable( + '2026-04-29T12:00:00', + new DateTimeZone('UTC') + ); + $this->sessionRepo = new FakeSessionRepository; + $this->clock = new FakeClock($this->now); + $this->middleware = new AuthMiddleware( + $this->sessionRepo, + $this->clock, + ); + } + + private function makeRequest(?string $token): Request + { + $request = Request::create('/api/anything', 'POST'); + if ($token !== null) { + $request->cookies->set('auth_token', $token); + } + + return $request; + } + + private function nextThatRecords(?Request &$captured): Closure + { + return function (Request $request) use (&$captured) { + $captured = $request; + + return new JsonResponse(['ok' => true], 200); + }; + } + + public function test_missing_cookie_returns_unauthorized_json(): void + { + $captured = null; + $response = $this->middleware->handle( + $this->makeRequest(null), + $this->nextThatRecords($captured), + ); + + $this->assertSame(401, $response->getStatusCode()); + $this->assertSame( + ['error' => 'unauthenticated'], + json_decode($response->getContent(), true), + ); + $this->assertNull($captured); + } + + public function test_unknown_token_returns_unauthorized(): void + { + $captured = null; + $response = $this->middleware->handle( + $this->makeRequest('does-not-exist'), + $this->nextThatRecords($captured), + ); + + $this->assertSame(401, $response->getStatusCode()); + $this->assertNull($captured); + } + + public function test_expired_session_returns_unauthorized_and_is_deleted(): void + { + $user = new User( + id: 7, + email: new EmailAddress('user@example.com'), + passwordHash: 'password', + ); + $this->sessionRepo->create(new CreateSessionDto( + token: 'expired-token', + user: $user, + createdAt: $this->now->modify('-8 days'), + expiresAt: $this->now->modify('-1 day'), + )); + + $captured = null; + $response = $this->middleware->handle( + $this->makeRequest('expired-token'), + $this->nextThatRecords($captured), + ); + + $this->assertSame(401, $response->getStatusCode()); + $this->assertNull($captured); + $this->assertNull( + $this->sessionRepo->findByToken('expired-token') + ); + } + + public function test_valid_session_attaches_user_and_calls_next(): void + { + $user = new User( + id: 7, + email: new EmailAddress('user@example.com'), + passwordHash: 'password', + ); + $this->sessionRepo->create(new CreateSessionDto( + token: 'valid-token', + user: $user, + createdAt: $this->now, + expiresAt: $this->now->modify('+7 days'), + )); + + $captured = null; + $response = $this->middleware->handle( + $this->makeRequest('valid-token'), + $this->nextThatRecords($captured), + ); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertNotNull($captured); + $attachedUser = $captured->attributes->get('user'); + $this->assertInstanceOf(User::class, $attachedUser); + $this->assertSame(7, $attachedUser->getId()); + } +} From f143562a4086fe4a04d2635161a1ae6cbe1909b9 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Tue, 19 May 2026 19:54:12 +0300 Subject: [PATCH 2/6] give fake clock a constructor --- backend/tests/Fakes/FakeClock.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/tests/Fakes/FakeClock.php b/backend/tests/Fakes/FakeClock.php index f32d5a4..f112836 100644 --- a/backend/tests/Fakes/FakeClock.php +++ b/backend/tests/Fakes/FakeClock.php @@ -4,12 +4,18 @@ namespace Tests\Fakes; use App\Auth\Clock; use DateTimeImmutable; -use DateTimeZone; class FakeClock implements Clock { + public function __construct(private DateTimeImmutable $currentTime) {} + public function now(): DateTimeImmutable { - return new DateTimeImmutable('2026-05-18 12:00:00', new DateTimeZone('UTC')); + return $this->currentTime; + } + + public function setTime(DateTimeImmutable $newTime): void + { + $this->currentTime = $newTime; } } From 218f885251401473c02f04ee670bcc9be2682e98 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Tue, 19 May 2026 19:55:07 +0300 Subject: [PATCH 3/6] name params, style --- backend/tests/Fakes/FakeSessionRepository.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/tests/Fakes/FakeSessionRepository.php b/backend/tests/Fakes/FakeSessionRepository.php index 120ccd8..ab03173 100644 --- a/backend/tests/Fakes/FakeSessionRepository.php +++ b/backend/tests/Fakes/FakeSessionRepository.php @@ -14,10 +14,10 @@ class FakeSessionRepository implements SessionRepository public function create(CreateSessionDto $dto): Session { $session = new Session( - $dto->token, - $dto->user, - $dto->createdAt, - $dto->expiresAt + token: $dto->token, + user: $dto->user, + createdAt: $dto->createdAt, + expiresAt: $dto->expiresAt, ); $this->sessionsByToken[$dto->token] = $session; @@ -33,10 +33,10 @@ class FakeSessionRepository implements SessionRepository $stored = $this->sessionsByToken[$token]; return new Session( - $stored->getToken(), - $stored->getUser(), - $stored->getCreatedAt(), - $stored->getExpiresAt() + token: $session->getToken(), + user: $session->getUser(), + createdAt: $session->getCreatedAt(), + expiresAt: $session->getExpiresAt(), ); } From 3ebb2c14b304cb5e055468e2930c0c09ec5820ae Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Tue, 19 May 2026 19:55:49 +0300 Subject: [PATCH 4/6] change up logout test style --- .../tests/Unit/Auth/UseCases/LogoutTest.php | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/backend/tests/Unit/Auth/UseCases/LogoutTest.php b/backend/tests/Unit/Auth/UseCases/LogoutTest.php index 2b28630..5dda63f 100644 --- a/backend/tests/Unit/Auth/UseCases/LogoutTest.php +++ b/backend/tests/Unit/Auth/UseCases/LogoutTest.php @@ -6,41 +6,51 @@ use App\Auth\CreateSessionDto; use App\Auth\UseCases\Logout\Logout; use App\Shared\ValueObject\EmailAddress; use App\User\User; -use PHPUnit\Framework\TestCase; +use DateTimeImmutable; +use DateTimeZone; use Tests\Fakes\FakeSessionRepository; +use Tests\TestCase; class LogoutTest extends TestCase { private FakeSessionRepository $sessionRepo; - private Logout $logout; + + private Logout $useCase; + + private DateTimeImmutable $now; protected function setUp(): void { - $this->sessionRepo = new FakeSessionRepository(); - $this->logout = new Logout($this->sessionRepo); + $this->now = new DateTimeImmutable( + '2026-04-29T12:00:00', + new DateTimeZone('UTC') + ); + $this->sessionRepo = new FakeSessionRepository; + $this->useCase = new Logout($this->sessionRepo); } - public function testDeletesSessionByToken(): void + public function test_existing_token_session_is_removed(): void { - $email = new EmailAddress('user@example.com'); - $user = new User(1, $email, 'hashed-password'); - - $session = $this->sessionRepo->create(new CreateSessionDto( - 'session-token', - $user, - new \DateTimeImmutable(), - new \DateTimeImmutable('+1 hour') + $this->sessionRepo->create(new CreateSessionDto( + token: 'token-abc', + user: new User( + id: 7, + email: new EmailAddress('a@b.com'), + passwordHash: 'password', + ), + createdAt: $this->now, + expiresAt: $this->now->modify('+7 days'), )); - $this->logout->execute('session-token'); + $this->useCase->execute('token-abc'); - $this->assertNull($this->sessionRepo->findByToken('session-token')); + $this->assertNull($this->sessionRepo->findByToken('token-abc')); } - public function testDeletesMissingTokenIsIdempotent(): void + public function test_unknown_token_does_not_throw(): void { - $this->logout->execute('nonexistent-token'); + $this->useCase->execute('unknown-token'); - $this->assertNull($this->sessionRepo->findByToken('nonexistent-token')); + $this->assertNull($this->sessionRepo->findByToken('unknown-token')); } } From 453e8284b43e541d57fb77f8f2e5f74d18fe5ad2 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Tue, 19 May 2026 19:56:56 +0300 Subject: [PATCH 5/6] streamline find by token in session repo --- backend/tests/Fakes/FakeSessionRepository.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/tests/Fakes/FakeSessionRepository.php b/backend/tests/Fakes/FakeSessionRepository.php index ab03173..c1733b0 100644 --- a/backend/tests/Fakes/FakeSessionRepository.php +++ b/backend/tests/Fakes/FakeSessionRepository.php @@ -26,12 +26,11 @@ class FakeSessionRepository implements SessionRepository public function findByToken(string $token): ?Session { - if (! isset($this->sessionsByToken[$token])) { + $session = $this->sessionsByToken[$token] ?? null; + if ($session === null) { return null; } - $stored = $this->sessionsByToken[$token]; - return new Session( token: $session->getToken(), user: $session->getUser(), From 51537bbf247f104d15917473a1e67b3c77fed2cd Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Tue, 19 May 2026 19:57:57 +0300 Subject: [PATCH 6/6] change testcase import, add datetime to fake clock, style lines --- .../tests/Unit/Auth/UseCases/CreateSessionTest.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/backend/tests/Unit/Auth/UseCases/CreateSessionTest.php b/backend/tests/Unit/Auth/UseCases/CreateSessionTest.php index d6368f2..0367f1c 100644 --- a/backend/tests/Unit/Auth/UseCases/CreateSessionTest.php +++ b/backend/tests/Unit/Auth/UseCases/CreateSessionTest.php @@ -6,10 +6,11 @@ use App\Auth\Clock; use App\Auth\UseCases\CreateSession\CreateSession; use App\Shared\ValueObject\EmailAddress; use App\User\User; -use PHPUnit\Framework\TestCase; +use DateTimeImmutable; use Tests\Fakes\FakeClock; use Tests\Fakes\FakeSessionRepository; use Tests\Fakes\FakeTokenGenerator; +use Tests\TestCase; class CreateSessionTest extends TestCase { @@ -22,9 +23,15 @@ class CreateSessionTest extends TestCase { $this->sessionRepo = new FakeSessionRepository(); $this->tokenGenerator = new FakeTokenGenerator(); - $this->clock = new FakeClock(); + $this->clock = new FakeClock( + new DateTimeImmutable('2026-05-18 12:00:00') + ); - $this->createSession = new CreateSession($this->sessionRepo, $this->tokenGenerator, $this->clock); + $this->createSession = new CreateSession( + $this->sessionRepo, + $this->tokenGenerator, + $this->clock + ); } public function testCreatesSessionForUser(): void