userRepo = new FakeUserRepository(); $this->sessionRepo = new FakeSessionRepository(); $this->hasher = new FakePasswordHasher(); $this->tokenGenerator = new FakeTokenGenerator([ 'session-token-1', 'session-token-2', ]); $this->clock = new FakeClock( new DateTimeImmutable('2026-05-16 12:00:00'), ); $authenticateUser = new AuthenticateUser( $this->userRepo, $this->hasher, ); $createSession = new CreateSession( $this->sessionRepo, $this->tokenGenerator, $this->clock, ); $logout = new Logout($this->sessionRepo); $this->controller = new AuthController( $authenticateUser, $createSession, $logout, ); $this->user = $this->userRepo->create(new CreateUserDto( email: new EmailAddress('user@example.com'), passwordHash: $this->hasher->hash('correct-password'), )); } public function testLoginReturnsUserAndSetsCookie(): void { $request = $this->jsonRequest('POST', '/login', [ 'email' => 'user@example.com', 'password' => 'correct-password', ]); $response = $this->controller->login($request, new Response()); $this->assertSame(200, $response->getStatusCode()); $this->assertStringContainsString( 'user@example.com', (string) $response->getBody(), ); $cookieHeader = $response->getHeaderLine('Set-Cookie'); $this->assertStringContainsString( AuthMiddleware::COOKIE_NAME . '=session-token-1', $cookieHeader, ); $this->assertStringContainsString('HttpOnly', $cookieHeader); } public function testLoginReturns400ForMissingEmail(): void { $request = $this->jsonRequest('POST', '/login', [ 'password' => 'correct-password', ]); $response = $this->controller->login($request, new Response()); $this->assertSame(400, $response->getStatusCode()); } public function testLoginReturns401ForInvalidCredentials(): void { $request = $this->jsonRequest('POST', '/login', [ 'email' => 'user@example.com', 'password' => 'wrong-password', ]); $response = $this->controller->login($request, new Response()); $this->assertSame(401, $response->getStatusCode()); } public function testLogoutClearsCookieAndReturns204(): void { $request = $this->createRequest() ->withCookieParams([ AuthMiddleware::COOKIE_NAME => 'session-token-1', ]); $response = $this->controller->logout($request, new Response()); $this->assertSame(204, $response->getStatusCode()); $cookieHeader = $response->getHeaderLine('Set-Cookie'); $this->assertStringContainsString( AuthMiddleware::COOKIE_NAME . '=', $cookieHeader, ); } public function testMeReturnsUserFromAttribute(): void { $request = $this->createRequest() ->withAttribute('user', $this->user); $response = $this->controller->me($request, new Response()); $this->assertSame(200, $response->getStatusCode()); $this->assertStringContainsString( 'user@example.com', (string) $response->getBody(), ); } private function jsonRequest( string $method, string $path, array $body, ): ServerRequestInterface { $request = $this->createRequest($method, $path) ->withHeader('Content-Type', 'application/json'); $request->getBody()->write(json_encode($body)); $request->getBody()->rewind(); return $request; } private function createRequest( string $method = 'POST', string $path = '/', ): ServerRequestInterface { $factory = new ServerRequestFactory(); return $factory->createServerRequest($method, $path); } }