All checks were successful
CI / secret-scan (pull_request) Successful in 15s
CI / php-lint (pull_request) Successful in 28s
CI / static-tests (pull_request) Successful in 1m6s
CI / js-tests (pull_request) Successful in 40s
CI / secret-scan (push) Successful in 14s
CI / php-lint (push) Successful in 33s
CI / static-tests (push) Successful in 1m11s
CI / js-tests (push) Successful in 38s
Client SMTP maison (zero lib, contrainte from-scratch) : ESMTP + STARTTLS + AUTH LOGIN, conduit par SmtpClient contre un SmtpTransport injectable (seam de test). SmtpMailer assemble un message text/plain UTF-8 (dot-stuffing, en-tetes RFC2047) et implemente l'interface Mailer existante. PasswordResetController choisit SmtpMailer si SMTP_HOST+USER+PASSWORD presents, sinon garde LogMailer (dev sans infra mail inchange). STARTTLS exige avant AUTH (pas d'auth en clair). Garde anti-injection CRLF sur les adresses (SmtpClient) + filter_var du destinataire (SmtpMailer). readReply borne (anti-boucle sur reponse malformee). Secrets uniquement en .env (hote) : placeholders dans .env.example / .env.prod.example, rien de versionne. Revue compliance : verdict block initial (injection CRLF + readReply non borne), 2 must_fix corriges + tests de regression. 8 tests SMTP, 429 total, PHPStan L6.
66 lines
1.5 KiB
PHP
66 lines
1.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Support;
|
|
|
|
use App\Auth\SmtpTransport;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* Transport SMTP double : rejoue des reponses serveur scriptees et enregistre les
|
|
* ecritures du client, pour tester la logique du protocole sans reseau.
|
|
*/
|
|
final class FakeSmtpTransport implements SmtpTransport
|
|
{
|
|
/** @var list<string> ce que le client a ecrit, dans l'ordre */
|
|
public array $writes = [];
|
|
|
|
public bool $cryptoEnabled = false;
|
|
public bool $closed = false;
|
|
public bool $opened = false;
|
|
|
|
/** @var list<string> reponses a rendre, dans l'ordre des readReply() */
|
|
private array $replies;
|
|
|
|
/** @param list<string> $replies */
|
|
public function __construct(array $replies)
|
|
{
|
|
$this->replies = $replies;
|
|
}
|
|
|
|
public function open(string $host, int $port, int $timeoutSeconds): void
|
|
{
|
|
$this->opened = true;
|
|
}
|
|
|
|
public function write(string $raw): void
|
|
{
|
|
$this->writes[] = $raw;
|
|
}
|
|
|
|
public function readReply(): string
|
|
{
|
|
if ($this->replies === []) {
|
|
throw new RuntimeException('FakeSmtpTransport : plus de reponse scriptee');
|
|
}
|
|
|
|
return array_shift($this->replies);
|
|
}
|
|
|
|
public function enableCrypto(): void
|
|
{
|
|
$this->cryptoEnabled = true;
|
|
}
|
|
|
|
public function close(): void
|
|
{
|
|
$this->closed = true;
|
|
}
|
|
|
|
/** Concatene toutes les ecritures (pratique pour assertions sur le message). */
|
|
public function written(): string
|
|
{
|
|
return implode('', $this->writes);
|
|
}
|
|
}
|