Goal-Calibration/tests/e2e/Controllers/AuthControllerTest.php

293 lines
9.2 KiB
PHP

<?php
namespace Tests\e2e\Controllers;
use App\Auth\AuthController;
use App\Auth\CreateSessionDto;
use App\Auth\UseCases\CreateSession;
use App\User\UseCases\AuthenticateUser;
use App\User\UseCases\CreateUser;
use App\User\UseCases\CreateUserRequest;
use App\User\User;
use App\ValueObjects\EmailAddress;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Psr7\Factory\ServerRequestFactory;
use Slim\Psr7\Factory\StreamFactory;
use Slim\Psr7\Response;
use Tests\Fakes\FakeClock;
use Tests\Fakes\FakePasswordHasher;
use Tests\Fakes\FakeSessionRepository;
use Tests\Fakes\FakeTokenGenerator;
use Tests\Fakes\FakeUserRepository;
class AuthControllerTest extends TestCase
{
private FakeUserRepository $userRepo;
private FakeSessionRepository $sessionRepo;
private FakeTokenGenerator $tokenGenerator;
private FakeClock $clock;
private FakePasswordHasher $passwordHasher;
private CreateUser $createUser;
private AuthenticateUser $authenticateUser;
private CreateSession $createSession;
private AuthController $controller;
public function setUp(): void
{
$this->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',
));
$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']);
}
}