Refatoring : Working state
This commit is contained in:
@@ -3,23 +3,22 @@ declare(strict_types=1);
|
||||
|
||||
namespace Tests\Auth;
|
||||
|
||||
use App\Auth\Infrastructure\PdoLoginAttemptRepository as LoginAttemptRepository;
|
||||
use App\Auth\Infrastructure\PdoLoginAttemptRepository;
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour LoginAttemptRepository.
|
||||
* Tests unitaires pour PdoLoginAttemptRepository.
|
||||
*
|
||||
* Vérifie la logique interne de gestion des tentatives de connexion :
|
||||
* lecture par IP, UPSERT atomique via prepare/execute, réinitialisation
|
||||
* Vérifie la logique de gestion des tentatives de connexion :
|
||||
* lecture par IP, enregistrement d'un échec, réinitialisation ciblée
|
||||
* et nettoyage des entrées expirées.
|
||||
*
|
||||
* recordFailure() utilise un UPSERT SQL atomique (ON CONFLICT DO UPDATE)
|
||||
* pour éliminer la race condition du pattern SELECT + INSERT/UPDATE.
|
||||
* Les tests vérifient que prepare() est appelé avec le bon SQL et que
|
||||
* execute() reçoit les bons paramètres.
|
||||
* Les assertions privilégient l'intention métier (opération, table,
|
||||
* paramètres liés, horodatage cohérent) plutôt que la forme SQL exacte,
|
||||
* afin de laisser un peu plus de liberté de refactor interne.
|
||||
*
|
||||
* PDO et PDOStatement sont mockés pour isoler complètement
|
||||
* le dépôt de la base de données.
|
||||
@@ -30,7 +29,7 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
/** @var PDO&MockObject */
|
||||
private PDO $db;
|
||||
|
||||
private LoginAttemptRepository $repository;
|
||||
private PdoLoginAttemptRepository $repository;
|
||||
|
||||
/**
|
||||
* Initialise le mock PDO et le dépôt avant chaque test.
|
||||
@@ -38,7 +37,7 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->db = $this->createMock(PDO::class);
|
||||
$this->repository = new LoginAttemptRepository($this->db);
|
||||
$this->repository = new PdoLoginAttemptRepository($this->db);
|
||||
}
|
||||
|
||||
// ── Helper ─────────────────────────────────────────────────────
|
||||
@@ -107,8 +106,8 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
// ── recordFailure — UPSERT atomique ────────────────────────────
|
||||
|
||||
/**
|
||||
* recordFailure() doit utiliser un UPSERT SQL (ON CONFLICT DO UPDATE)
|
||||
* via prepare/execute — garantie de l'atomicité.
|
||||
* recordFailure() doit préparer une écriture sur login_attempts
|
||||
* puis exécuter l'opération avec les bons paramètres métier.
|
||||
*/
|
||||
public function testRecordFailureUsesUpsertSql(): void
|
||||
{
|
||||
@@ -117,8 +116,9 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
$this->db->expects($this->once())
|
||||
->method('prepare')
|
||||
->with($this->logicalAnd(
|
||||
$this->stringContains('INSERT INTO login_attempts'),
|
||||
$this->stringContains('ON CONFLICT'),
|
||||
$this->stringContains('login_attempts'),
|
||||
$this->stringContains('attempts'),
|
||||
$this->stringContains('locked_until'),
|
||||
))
|
||||
->willReturn($stmt);
|
||||
|
||||
@@ -230,7 +230,7 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
// ── resetForIp ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* resetForIp() doit préparer un DELETE ciblant la bonne IP.
|
||||
* resetForIp() doit préparer une suppression ciblant la bonne IP.
|
||||
*/
|
||||
public function testResetForIpCallsDeleteWithCorrectIp(): void
|
||||
{
|
||||
@@ -239,7 +239,10 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
|
||||
$this->db->expects($this->once())
|
||||
->method('prepare')
|
||||
->with($this->stringContains('DELETE FROM login_attempts'))
|
||||
->with($this->logicalAnd(
|
||||
$this->stringContains('login_attempts'),
|
||||
$this->stringContains('DELETE'),
|
||||
))
|
||||
->willReturn($stmt);
|
||||
|
||||
$stmt->expects($this->once())
|
||||
@@ -253,7 +256,7 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
// ── deleteExpired ──────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* deleteExpired() doit préparer un DELETE ciblant locked_until expiré
|
||||
* deleteExpired() doit préparer une suppression sur login_attempts
|
||||
* et lier une date au format 'Y-m-d H:i:s' comme paramètre :now.
|
||||
*/
|
||||
public function testDeleteExpiredExecutesQueryWithCurrentTimestamp(): void
|
||||
@@ -262,7 +265,10 @@ final class LoginAttemptRepositoryTest extends TestCase
|
||||
|
||||
$this->db->expects($this->once())
|
||||
->method('prepare')
|
||||
->with($this->stringContains('DELETE FROM login_attempts'))
|
||||
->with($this->logicalAnd(
|
||||
$this->stringContains('login_attempts'),
|
||||
$this->stringContains('DELETE'),
|
||||
))
|
||||
->willReturn($stmt);
|
||||
|
||||
$stmt->expects($this->once())
|
||||
|
||||
Reference in New Issue
Block a user