diff --git a/backend/tests/Unit/Middleware/AuthMiddlewareTest.php b/backend/tests/Unit/Middleware/AuthMiddlewareTest.php new file mode 100644 index 0000000..eefa63c --- /dev/null +++ b/backend/tests/Unit/Middleware/AuthMiddlewareTest.php @@ -0,0 +1,153 @@ +sessionRepo = new FakeSessionRepository(); + $this->clock = new FakeClock( + new DateTimeImmutable('2026-05-16 12:00:00'), + ); + $this->middleware = new AuthMiddleware( + $this->sessionRepo, + $this->clock, + ); + + $userRepo = new FakeUserRepository(); + $hasher = new FakePasswordHasher(); + $this->user = $userRepo->create(new CreateUserDto( + email: new EmailAddress('user@example.com'), + passwordHash: $hasher->hash('password'), + )); + + $this->sessionRepo->create(new CreateSessionDto( + token: 'valid-token', + user: $this->user, + createdAt: new DateTimeImmutable('2026-05-16 12:00:00'), + expiresAt: new DateTimeImmutable('2026-05-23 12:00:00'), + )); + } + + public function testPassesRequestToHandlerWhenCookieIsValid(): void + { + $request = $this->createRequestWithCookie('valid-token'); + + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->expects($this->once()) + ->method('handle') + ->with($this->callback(function (ServerRequestInterface $request) { + $user = $request->getAttribute('user'); + return $user instanceof User + && $user->getId() === $this->user->getId(); + })) + ->willReturn(new Response(200)); + + $response = $this->middleware->process($request, $handler); + + $this->assertSame(200, $response->getStatusCode()); + } + + public function testReturns401WhenCookieIsMissing(): void + { + $request = self::createRequest(); + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->expects($this->never())->method('handle'); + + $response = $this->middleware->process($request, $handler); + + $this->assertSame(401, $response->getStatusCode()); + } + + public function testReturns401WhenCookieIsEmpty(): void + { + $request = $this->createRequestWithCookie(''); + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->expects($this->never())->method('handle'); + + $response = $this->middleware->process($request, $handler); + + $this->assertSame(401, $response->getStatusCode()); + } + + public function testReturns401WhenTokenIsUnknown(): void + { + $request = $this->createRequestWithCookie('unknown-token'); + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->expects($this->never())->method('handle'); + + $response = $this->middleware->process($request, $handler); + + $this->assertSame(401, $response->getStatusCode()); + } + + public function testReturns401WhenSessionIsExpired(): void + { + $this->clock->setTime( + new DateTimeImmutable('2026-05-30 12:00:00'), + ); + + $request = $this->createRequestWithCookie('valid-token'); + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->expects($this->never())->method('handle'); + + $response = $this->middleware->process($request, $handler); + + $this->assertSame(401, $response->getStatusCode()); + } + + public function testDeletesExpiredSession(): void + { + $this->clock->setTime( + new DateTimeImmutable('2026-05-30 12:00:00'), + ); + + $request = $this->createRequestWithCookie('valid-token'); + $handler = $this->createMock(RequestHandlerInterface::class); + + $this->middleware->process($request, $handler); + + $this->assertNull( + $this->sessionRepo->findByToken('valid-token'), + ); + } + + private static function createRequest(): ServerRequestInterface + { + $factory = new ServerRequestFactory(); + + return $factory->createServerRequest('GET', '/'); + } + + private function createRequestWithCookie( + string $token, + ): ServerRequestInterface { + $request = self::createRequest(); + + return $request->withCookieParams([ + AuthMiddleware::COOKIE_NAME => $token, + ]); + } +}