fix(mail) : stop le spam GlitchTip de sync (reconnexion AUTHENTICATIONFAILED + double-log)
Un seul echec de dossier (empty response) generait 4 events GlitchTip : - le bloc de detection de suppression rappelait listMessages quand le fetch initial avait echoue, forcant une reconnexion IMAP refusee par OVH (AUTHENTICATIONFAILED, throttling) ; - chaque echec etait logge 2x en error (provider + service). Fix : - garde `if (null !== $remoteHeaders)` autour de la detection de suppression : si le fetch a echoue, on saute le diff (reprise au cycle suivant), plus de reconnexion parasite ; - le log service des MailProviderException passe en warning (le provider reste la source unique au niveau error pour GlitchTip, couvre aussi les chemins HTTP). Net : 1 event GlitchTip par echec de dossier. Test de regression : testSyncFolderDoesNotRefetchMessagesWhenInitialFetchFails.
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Module\Mail\Application\Dto\MailFolderDto;
|
||||
use App\Module\Mail\Application\Service\MailSyncService;
|
||||
use App\Module\Mail\Domain\Entity\MailConfiguration;
|
||||
use App\Module\Mail\Domain\Entity\MailFolder;
|
||||
use App\Module\Mail\Domain\Exception\MailProviderException;
|
||||
use App\Module\Mail\Domain\Provider\MailProviderInterface;
|
||||
use App\Module\Mail\Domain\Repository\MailConfigurationRepositoryInterface;
|
||||
use App\Module\Mail\Domain\Repository\MailFolderRepositoryInterface;
|
||||
@@ -237,6 +238,58 @@ class MailSyncServiceTest extends TestCase
|
||||
self::assertNotEmpty($report->errors);
|
||||
}
|
||||
|
||||
public function testSyncFolderDoesNotRefetchMessagesWhenInitialFetchFails(): void
|
||||
{
|
||||
// Regression: when the message fetch fails, the deletion-detection block
|
||||
// used to re-call listMessages, which forced an IMAP reconnect and tripped
|
||||
// OVH throttling (AUTHENTICATIONFAILED) — turning one folder failure into
|
||||
// several GlitchTip events. listMessages must be called exactly once.
|
||||
$config = new MailConfiguration();
|
||||
$config->setEnabled(true);
|
||||
|
||||
$configRepo = $this->createMock(MailConfigurationRepositoryInterface::class);
|
||||
$configRepo->method('findSingleton')->willReturn($config);
|
||||
|
||||
$folder = new MailFolder();
|
||||
$folder->setPath('INBOX/RH/LUCILE NEAU');
|
||||
|
||||
$messageRepo = $this->createMock(MailMessageRepositoryInterface::class);
|
||||
$messageRepo->method('findMaxUidInFolder')->willReturn(0);
|
||||
$messageRepo->method('findLastNByFolder')->willReturn([]);
|
||||
// The DB still holds messages: without the guard the deletion block would
|
||||
// re-fetch the remote list to diff against these UIDs.
|
||||
$messageRepo->method('findAllUidsByFolder')->willReturn([1, 2, 3]);
|
||||
|
||||
$provider = $this->createMock(MailProviderInterface::class);
|
||||
$provider->expects(self::once())
|
||||
->method('listMessages')
|
||||
->willThrowException(
|
||||
MailProviderException::operationFailed('listMessages', 'empty response')
|
||||
)
|
||||
;
|
||||
|
||||
$folderRepo = $this->createMock(MailFolderRepositoryInterface::class);
|
||||
$em = $this->createMock(EntityManagerInterface::class);
|
||||
$em->expects(self::never())->method('remove');
|
||||
|
||||
$service = new MailSyncService(
|
||||
provider: $provider,
|
||||
configRepository: $configRepo,
|
||||
folderRepository: $folderRepo,
|
||||
messageRepository: $messageRepo,
|
||||
entityManager: $em,
|
||||
lockFactory: $this->makeLockFactory(),
|
||||
logger: new NullLogger(),
|
||||
managerRegistry: $this->createMock(ManagerRegistry::class),
|
||||
);
|
||||
|
||||
$report = $service->syncFolder($folder);
|
||||
|
||||
// Exactly one error recorded (the fetch failure), not a cascade.
|
||||
self::assertCount(1, $report->errors);
|
||||
self::assertSame(0, $report->deletedCount);
|
||||
}
|
||||
|
||||
private function makeLockFactory(bool $acquired = true): LockFactory
|
||||
{
|
||||
$lock = $this->createMock(SharedLockInterface::class);
|
||||
|
||||
Reference in New Issue
Block a user