CluPilotCloud/tests/Unit/MailTlsPolicyTest.php

70 lines
3.1 KiB
PHP

<?php
use App\Services\Mail\MailTlsPolicy;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
/**
* The single place that decides what "ssl", "tls", and "none" mean on the
* wire. MailboxTransport (real sends) and MailboxTester (the test-send
* button) used to each re-derive the "ssl, or port 465 ⇒ implicit TLS" half
* of this on their own — this table is what replaces both copies, so it is
* pinned down here independently of either caller.
*/
it('maps a stored encryption choice and port to the three EsmtpTransport switches', function (
string $encryption,
int $port,
bool $implicit,
bool $autoTls,
bool $requireTls,
) {
$policy = MailTlsPolicy::for($encryption, $port);
expect($policy->implicit)->toBe($implicit)
->and($policy->autoTls)->toBe($autoTls)
->and($policy->requireTls)->toBe($requireTls);
})->with([
// encryption, port, implicit, autoTls, requireTls
// 'ssl' is implicit TLS from the first byte, on any port — unchanged
// from before this fix.
'ssl on the submission port' => ['ssl', 587, true, true, true],
'ssl on the SMTPS port' => ['ssl', 465, true, true, true],
// Port 465 forces implicit TLS regardless of the stored encryption
// setting, exactly as before this fix: a server listening there speaks
// TLS from byte one and will not answer a plaintext EHLO, so a
// mismatched setting must not turn into a hang.
'tls on the SMTPS port defers to the port' => ['tls', 465, true, true, true],
'none on the SMTPS port defers to the port' => ['none', 465, true, true, true],
// 'tls' is the fix: STARTTLS is REQUIRED, not merely attempted. autoTls
// must stay on too, or the STARTTLS upgrade requireTls checks for would
// never be attempted in the first place.
'tls on the submission port' => ['tls', 587, false, true, true],
'tls on a custom port' => ['tls', 2525, false, true, true],
// 'none' is the other half of the fix: no implicit wrap, and autoTls
// OFF — otherwise a server that happens to offer STARTTLS would still
// silently upgrade despite the operator's explicit choice.
'none on the submission port' => ['none', 587, false, false, false],
'none on a custom port' => ['none', 2525, false, false, false],
// Blank or unrecognised values (a legacy row predating the in:tls,ssl,none
// validation, or one that decoded to something unexpected) fall to the
// same shape as 'none': no implicit wrap, no silent upgrade, no
// spurious failure for a value nobody explicitly chose as 'tls'.
'blank falls back to the none shape' => ['', 587, false, false, false],
'garbage falls back to the none shape' => ['garbage', 587, false, false, false],
]);
it('applies autoTls and requireTls to an already-built transport', function () {
$transport = new EsmtpTransport('mail.example.test', 587, false);
$policy = MailTlsPolicy::for('tls', 587);
$returned = $policy->apply($transport);
expect($returned)->toBe($transport) // fluent — the caller chains straight through
->and($transport->isAutoTls())->toBeTrue()
->and($transport->isTlsRequired())->toBeTrue();
});