Forcing every call site to be explicit about admin status and password eliminates a class of bugs where an unintended isAdmin=false or empty passwordHash could silently slip through. The CreateUserTest case that asserted the isAdmin default is dropped since the default no longer exists.
294 lines
9.2 KiB
PHP
294 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',
|
|
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']);
|
|
}
|
|
}
|