diff --git a/tests/e2e/Controllers/AuthControllerTest.php b/tests/e2e/Controllers/AuthControllerTest.php new file mode 100644 index 0000000..d7bc415 --- /dev/null +++ b/tests/e2e/Controllers/AuthControllerTest.php @@ -0,0 +1,284 @@ +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->createUser = new CreateUser($this->userRepo); + $this->authenticateUser = new AuthenticateUser($this->userRepo); + $this->createSession = new CreateSession( + $this->sessionRepo, + $this->tokenGenerator, + $this->clock, + ); + + $this->createUser->execute(new CreateUserRequest( + email: 'existing@test.com', + password: 'password1', + )); + + $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-01'), + expiresAt: new DateTimeImmutable('2025-01-08'), + )); + + $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']); + } +}