userRepo = new FakeUserRepository(); $this->sessionRepo = new FakeSessionRepository(); $this->tokenGenerator = new FakeTokenGenerator( ['session-token-xyz'] ); $this->clock = new FakeClock( new DateTimeImmutable('2025-01-01T12:00:00+00:00') ); $this->passwordHasher = new FakePasswordHasher(); $this->createUser = new CreateUser( $this->userRepo, $this->passwordHasher, ); $this->authenticateUser = new AuthenticateUser( $this->userRepo, $this->passwordHasher, ); $this->createSession = new CreateSession( $this->sessionRepo, $this->tokenGenerator, $this->clock, ); $this->createUser->execute(new CreateUserRequest( email: 'existing@test.com', password: 'password1', isAdmin: false, )); $this->controller = new AuthController(); } private function makeJsonRequest( string $method, string $path, array $data, ): ServerRequestInterface { $body = new StreamFactory()->createStream(json_encode($data)); return new ServerRequestFactory() ->createServerRequest($method, 'http://localhost' . $path) ->withHeader('Content-Type', 'application/json') ->withBody($body); } public function test_login_returns_200_and_user(): void { $response = $this->controller->login( $this->makeJsonRequest('POST', '/api/auth/login', [ 'email' => 'existing@test.com', 'password' => 'password1', ]), new Response(), $this->authenticateUser, $this->createSession, ); $this->assertEquals(200, $response->getStatusCode()); $body = json_decode($response->getBody(), true); $this->assertEquals( 'existing@test.com', $body['user']['email'] ); } public function test_login_sets_auth_cookie(): void { $response = $this->controller->login( $this->makeJsonRequest('POST', '/api/auth/login', [ 'email' => 'existing@test.com', 'password' => 'password1', ]), new Response(), $this->authenticateUser, $this->createSession, ); $setCookie = $response->getHeaderLine('Set-Cookie'); $this->assertStringContainsString( 'auth_token=session-token-xyz', $setCookie ); $this->assertStringContainsString('HttpOnly', $setCookie); $this->assertStringContainsString('SameSite=Lax', $setCookie); $this->assertStringContainsString('Path=/', $setCookie); } public function test_login_creates_session(): void { $this->controller->login( $this->makeJsonRequest('POST', '/api/auth/login', [ 'email' => 'existing@test.com', 'password' => 'password1', ]), new Response(), $this->authenticateUser, $this->createSession, ); $this->assertNotNull( $this->sessionRepo->findByToken('session-token-xyz') ); } public function test_login_returns_401_on_wrong_password(): void { $response = $this->controller->login( $this->makeJsonRequest('POST', '/api/auth/login', [ 'email' => 'existing@test.com', 'password' => 'wrongpassword', ]), new Response(), $this->authenticateUser, $this->createSession, ); $this->assertEquals(401, $response->getStatusCode()); } public function test_login_returns_400_when_email_missing(): void { $response = $this->controller->login( $this->makeJsonRequest('POST', '/api/auth/login', [ 'password' => 'password1', ]), new Response(), $this->authenticateUser, $this->createSession, ); $this->assertEquals(400, $response->getStatusCode()); } public function test_register_creates_user_and_logs_in(): void { $response = $this->controller->register( $this->makeJsonRequest('POST', '/api/auth/register', [ 'email' => 'new@test.com', 'password' => 'password1', ]), new Response(), $this->createUser, $this->createSession, ); $this->assertEquals(200, $response->getStatusCode()); $body = json_decode($response->getBody(), true); $this->assertEquals('new@test.com', $body['user']['email']); $setCookie = $response->getHeaderLine('Set-Cookie'); $this->assertStringContainsString( 'auth_token=session-token-xyz', $setCookie ); } public function test_register_returns_400_on_short_password(): void { $response = $this->controller->register( $this->makeJsonRequest('POST', '/api/auth/register', [ 'email' => 'new@test.com', 'password' => 'short', ]), new Response(), $this->createUser, $this->createSession, ); $this->assertEquals(400, $response->getStatusCode()); } public function test_register_returns_400_on_duplicate_email(): void { $response = $this->controller->register( $this->makeJsonRequest('POST', '/api/auth/register', [ 'email' => 'existing@test.com', 'password' => 'password1', ]), new Response(), $this->createUser, $this->createSession, ); $this->assertEquals(400, $response->getStatusCode()); } public function test_register_ignores_is_admin_in_body(): void { $this->controller->register( $this->makeJsonRequest('POST', '/api/auth/register', [ 'email' => 'sneaky@test.com', 'password' => 'password1', 'isAdmin' => true, ]), new Response(), $this->createUser, $this->createSession, ); $newUser = $this->userRepo->findByEmail( new EmailAddress('sneaky@test.com') ); $this->assertFalse($newUser->isAdmin()); } public function test_logout_deletes_session_and_clears_cookie(): void { $this->sessionRepo->create(new CreateSessionDto( token: 'existing-session', userId: 0, createdAt: new DateTimeImmutable('2025-01-01T00:00:00+00:00'), expiresAt: new DateTimeImmutable('2025-01-08T00:00:00+00:00'), )); $request = $this->makeJsonRequest( 'POST', '/api/auth/logout', [] )->withCookieParams(['auth_token' => 'existing-session']); $response = $this->controller->logout( $request, new Response(), $this->sessionRepo, ); $this->assertEquals(204, $response->getStatusCode()); $this->assertNull( $this->sessionRepo->findByToken('existing-session') ); $this->assertStringContainsString( 'auth_token=;', $response->getHeaderLine('Set-Cookie') ); } public function test_me_returns_current_user(): void { $user = new User( id: 5, email: new EmailAddress('me@test.com'), passwordHash: '', isAdmin: true, ); $request = new ServerRequestFactory() ->createServerRequest('GET', 'http://localhost/api/auth/me') ->withAttribute('user', $user); $response = $this->controller->me($request, new Response()); $this->assertEquals(200, $response->getStatusCode()); $body = json_decode($response->getBody(), true); $this->assertEquals(5, $body['user']['id']); $this->assertEquals('me@test.com', $body['user']['email']); $this->assertTrue($body['user']['isAdmin']); } }