corentin_wakdo/tests/Unit/Auth/MeControllerTest.php
Imugiii 91b6241096 feat(rbac): autorisation par permission + garde de session cablee (GET /api/me)
Authorizer verifie une PERMISSION via role_permission (RG-T03), rechargee depuis la base a
chaque appel (10.4 RG-3) ; un role desactive ne confere rien. AuthenticatedController (App\Controllers)
cable SessionGuard (RG-6 + RG-T02) et Authorizer sans inverser la dependance du Core. MeController
expose GET /api/me (identite + permissions ; 401 si session absente/expiree/inactive) : premier
consommateur reel du SessionGuard. Tests unitaires + integration DB (auto-skippee sans base) couvrant
le predicat is_active et la liaison par code de permission.
2026-06-15 18:42:09 +00:00

136 lines
4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Unit\Auth;
use PHPUnit\Framework\TestCase;
use App\Auth\Authorizer;
use App\Auth\SessionGuard;
use App\Auth\SessionManager;
use App\Controllers\MeController;
use App\Core\Config;
use App\Core\Database;
use App\Core\Request;
use App\Tests\Support\FakeDatabase;
/**
* Sous-classe de test : injecte une session en mode test et un FakeDatabase dans
* la garde et l'autorisation, sans base reelle.
*/
final class TestMeController extends MeController
{
public function __construct(
Request $request,
Config $config,
Database $database,
private readonly SessionManager $testSession,
private readonly FakeDatabase $fakeDb,
) {
parent::__construct($request, $config, $database);
}
protected function sessionManager(): SessionManager
{
return $this->testSession;
}
protected function sessionGuard(): SessionGuard
{
return new SessionGuard($this->testSession, $this->fakeDb, $this->config);
}
protected function authorizer(): Authorizer
{
return new Authorizer($this->fakeDb);
}
}
final class MeControllerTest extends TestCase
{
/** @var list<string> */
private array $touchedKeys = [];
protected function setUp(): void
{
$this->setEnv('SESSION_LIFETIME_IDLE', '14400');
$this->setEnv('SESSION_LIFETIME_ABSOLUTE', '36000');
}
protected function tearDown(): void
{
foreach ($this->touchedKeys as $key) {
putenv($key);
}
$this->touchedKeys = [];
}
private function setEnv(string $key, string $value): void
{
$this->touchedKeys[] = $key;
putenv($key . '=' . $value);
}
private function controller(SessionManager $session, FakeDatabase $db): TestMeController
{
$request = new Request('GET', '/api/me', [], [], '', '203.0.113.5');
return new TestMeController($request, new Config(), new Database(new Config()), $session, $db);
}
public function testNoSessionReturns401(): void
{
$response = $this->controller(new SessionManager(new Config(), true), new FakeDatabase())->show();
self::assertSame(401, $response->status());
$body = json_decode($response->body(), true);
self::assertIsArray($body);
self::assertSame('AUTH_REQUIRED', $body['error']['code'] ?? null);
}
public function testAuthenticatedReturnsIdentityAndPermissions(): void
{
$session = new SessionManager(new Config(), true);
// Horodatages relatifs a l'instant reel : MeController appelle check() sans
// injecter de temps (now = time()).
$now = time();
$session->set('user_id', 7);
$session->set('role_id', 3);
$session->set('logged_in_at', $now - 100);
$session->set('last_activity', $now - 50);
$db = new FakeDatabase();
$db->guardUserRow = ['is_active' => 1];
$db->roleRow = ['code' => 'manager'];
$db->permissionCodes = ['product.read', 'stats.read'];
$response = $this->controller($session, $db)->show();
self::assertSame(200, $response->status());
$body = json_decode($response->body(), true);
self::assertIsArray($body);
self::assertSame(7, $body['data']['user_id'] ?? null);
self::assertSame(3, $body['data']['role_id'] ?? null);
self::assertSame('manager', $body['data']['role_code'] ?? null);
self::assertSame(['product.read', 'stats.read'], $body['data']['permissions'] ?? null);
}
public function testInactiveUserSessionReturns401(): void
{
$session = new SessionManager(new Config(), true);
$now = time();
$session->set('user_id', 7);
$session->set('role_id', 3);
$session->set('logged_in_at', $now - 100);
$session->set('last_activity', $now - 50);
$db = new FakeDatabase();
$db->guardUserRow = ['is_active' => 0];
$response = $this->controller($session, $db)->show();
self::assertSame(401, $response->status());
}
}