now = new DateTimeImmutable( '2026-04-29T12:00:00', new DateTimeZone('UTC') ); $this->clock = new FakeClock($this->now); $this->tokenGenerator = new FakeTokenGenerator(['session-token-1']); $this->userRepo = new FakeUserRepository(); $this->hasher = new FakeHasher(); $this->sessionRepo = new FakeSessionRepository(); $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, ); } private function seedStartupUser(string $email, string $password): void { $user = $this->userRepo->create( new CreateUserDto( email: new EmailAddress($email), passwordHash: 'hashed-password', ) ); } public function test_login_returns_200_and_sets_cookie_on_success(): void { $email = 'user@example.com'; $password = 'password'; $this->seedStartupUser($email, $password); $request = new Request([ 'email' => $email, 'password' => $password, ]); $response = $this->controller->login($request); $this->assertEquals(200, $response->getStatusCode()); $body = json_decode($response->getContent(), true); $this->assertSame($email, $body['user']['email']); $cookies = $response->headers->getCookies(); $this->assertCount(1, $cookies); $cookie = $cookies[0]; $this->assertSame( AuthMiddleware::COOKIE_NAME, $cookie->getName() ); $this->assertSame('session-token-1', $cookie->getValue()); $this->assertTrue($cookie->isHttpOnly()); $this->assertSame('lax', $cookie->getSameSite()); $this->assertNotNull( $this->sessionRepo->findByToken('session-token-1') ); } public function test_login_returns_400_when_email_missing(): void { $request = new Request(['password' => 'correctpassword']); $response = $this->controller->login($request); $this->assertEquals(400, $response->getStatusCode()); } public function test_login_returns_400_when_password_missing(): void { $request = new Request(['email' => 'user@example.com']); $response = $this->controller->login($request); $this->assertEquals(400, $response->getStatusCode()); } public function test_login_returns_401_when_credentials_invalid(): void { $this->seedStartupUser('user@example.com', 'correctpassword'); $request = new Request([ 'email' => 'user@example.com', 'password' => 'wrongpassword', ]); $response = $this->controller->login($request); $this->assertEquals(401, $response->getStatusCode()); } public function test_logout_returns_204_and_clears_cookie(): void { $this->seedStartupUser('user@example.com', 'correctpassword'); $loginRequest = new Request([ 'email' => 'user@example.com', 'password' => 'correctpassword', ]); $this->controller->login($loginRequest); $logoutRequest = new Request; $logoutRequest->cookies->set( AuthMiddleware::COOKIE_NAME, 'session-token-1' ); $response = $this->controller->logout($logoutRequest); $this->assertEquals(204, $response->getStatusCode()); $this->assertNull( $this->sessionRepo->findByToken('session-token-1') ); $cookies = $response->headers->getCookies(); $this->assertCount(1, $cookies); $this->assertSame( AuthMiddleware::COOKIE_NAME, $cookies[0]->getName() ); $this->assertSame('', $cookies[0]->getValue()); } public function test_me_returns_200_with_user_when_authenticated(): void { $email = 'me@example.com'; $user = $this->userRepo->create( new CreateUserDto( email: new EmailAddress($email), passwordHash: 'password' ) ); $request = new Request; $request->attributes->set('user', $user); $response = $this->controller->me($request); $this->assertEquals(200, $response->getStatusCode()); $body = json_decode($response->getContent(), true); $this->assertSame($user->getId(), $body['user']['id']); $this->assertSame($email, $body['user']['email']); } }