refactor: rename remote mail service

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-09-26 22:00:16 -04:00
parent 54a24c9892
commit 8abd2c2c8c
7 changed files with 52 additions and 52 deletions
+3 -3
View File
@@ -17,7 +17,7 @@ use KTXM\ProviderImap\Client\Message;
use KTXM\ProviderImap\Client\Protocol\SequenceSet; use KTXM\ProviderImap\Client\Protocol\SequenceSet;
use KTXM\ProviderImap\Providers\Provider; use KTXM\ProviderImap\Providers\Provider;
use KTXM\ProviderImap\Providers\Service; use KTXM\ProviderImap\Providers\Service;
use KTXM\ProviderImap\Service\Remote\RemoteMailService; use KTXM\ProviderImap\Service\Live\LiveMailService;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@@ -119,7 +119,7 @@ class TestCommand extends Command
$startedAt = microtime(true); $startedAt = microtime(true);
try { try {
$mailService = new RemoteMailService($service); $mailService = new LiveMailService($service);
$mailboxes = $mailService->collectionList(); $mailboxes = $mailService->collectionList();
} catch (\Throwable $e) { } catch (\Throwable $e) {
$io->error('IMAP diagnostic failed: ' . $e->getMessage()); $io->error('IMAP diagnostic failed: ' . $e->getMessage());
@@ -183,7 +183,7 @@ class TestCommand extends Command
$io->section(sprintf('Recent Messages: %s', $mailboxName)); $io->section(sprintf('Recent Messages: %s', $mailboxName));
try { try {
$mailboxService = new RemoteMailService($service); $mailboxService = new LiveMailService($service);
$selectedMailbox = $mailboxService->imapClient()->perform(new SelectCommand($mailboxName, true)); $selectedMailbox = $mailboxService->imapClient()->perform(new SelectCommand($mailboxName, true));
} catch (\Throwable $e) { } catch (\Throwable $e) {
$io->error('Mailbox inspection failed: ' . $e->getMessage()); $io->error('Mailbox inspection failed: ' . $e->getMessage());
+3 -3
View File
@@ -18,7 +18,7 @@ use KTXF\Mail\Service\ServiceMutableInterface;
use KTXF\Resource\Provider\ResourceServiceLocationInterface; use KTXF\Resource\Provider\ResourceServiceLocationInterface;
use KTXF\Resource\Provider\ResourceServiceMutateInterface; use KTXF\Resource\Provider\ResourceServiceMutateInterface;
use KTXM\ProviderImap\Service\Discovery; use KTXM\ProviderImap\Service\Discovery;
use KTXM\ProviderImap\Service\Remote\RemoteMailService; use KTXM\ProviderImap\Service\Live\LiveMailService;
use KTXM\ProviderImap\Stores\ServiceStore; use KTXM\ProviderImap\Stores\ServiceStore;
/** /**
@@ -192,8 +192,8 @@ class Provider implements ProviderBaseInterface, ProviderServiceMutateInterface,
$service->fromStore(['sid' => 'test']); $service->fromStore(['sid' => 'test']);
// Attempt to authenticate and list mailboxes as a connectivity check // Attempt to authenticate and list mailboxes as a connectivity check
$remote = new RemoteMailService($service); $live = new LiveMailService($service);
$mailboxes = iterator_to_array($remote->collectionList()); $mailboxes = iterator_to_array($live->collectionList());
$latency = (int) round((microtime(true) - $startTime) * 1000); $latency = (int) round((microtime(true) - $startTime) * 1000);
+29 -29
View File
@@ -38,7 +38,7 @@ use KTXF\Resource\Sort\Sort;
use KTXM\ProviderImap\Providers\ServiceIdentityBasic; use KTXM\ProviderImap\Providers\ServiceIdentityBasic;
use KTXM\ProviderImap\Providers\ServiceLocation; use KTXM\ProviderImap\Providers\ServiceLocation;
use KTXM\ProviderImap\Mime\MessageBuilder; use KTXM\ProviderImap\Mime\MessageBuilder;
use KTXM\ProviderImap\Service\Remote\RemoteMailService; use KTXM\ProviderImap\Service\Live\LiveMailService;
use KTXM\ProviderImap\Providers\CollectionResource; use KTXM\ProviderImap\Providers\CollectionResource;
use KTXF\Mail\Collection\CollectionRoles; use KTXF\Mail\Collection\CollectionRoles;
use KTXF\Mail\Object\MessagePropertiesMutableInterface; use KTXF\Mail\Object\MessagePropertiesMutableInterface;
@@ -111,14 +111,14 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
'EntityTransmit' => true, 'EntityTransmit' => true,
]; ];
private RemoteMailService $remoteService; private LiveMailService $liveService;
public function __construct() {} public function __construct() {}
private function initialize(): void private function initialize(): void
{ {
if (!isset($this->remoteService)) { if (!isset($this->liveService)) {
$this->remoteService = new RemoteMailService($this); $this->liveService = new LiveMailService($this);
} }
} }
@@ -368,7 +368,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$list = []; $list = [];
foreach ($this->remoteService->collectionList($location, $filter, $sort) as $mailbox) { foreach ($this->liveService->collectionList($location, $filter, $sort) as $mailbox) {
$resource = $this->collectionFresh(); $resource = $this->collectionFresh();
$resource->fromImap($mailbox); $resource->fromImap($mailbox);
$list[$mailbox->name()] = $resource; $list[$mailbox->name()] = $resource;
@@ -395,7 +395,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
foreach ($identifiers as $identifier) { foreach ($identifiers as $identifier) {
$key = (string) $identifier; $key = (string) $identifier;
$result = $this->remoteService->collectionFetch($key); $result = $this->liveService->collectionFetch($key);
$list[$key] = $result !== false; $list[$key] = $result !== false;
} }
@@ -407,7 +407,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
{ {
$this->initialize(); $this->initialize();
$mailbox = $this->remoteService->collectionFetch((string) $identifier); $mailbox = $this->liveService->collectionFetch((string) $identifier);
if ($mailbox === null) { if ($mailbox === null) {
return null; return null;
} }
@@ -436,13 +436,13 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
if ($target !== null) { if ($target !== null) {
$path = $target->collection(); $path = $target->collection();
// Determine the hierarchy delimiter from an existing mailbox, default to '/' // Determine the hierarchy delimiter from an existing mailbox, default to '/'
$mailboxes = iterator_to_array($this->remoteService->collectionList(null, null, null, '')); $mailboxes = iterator_to_array($this->liveService->collectionList(null, null, null, ''));
$rootMailbox = $mailboxes === [] ? null : reset($mailboxes); $rootMailbox = $mailboxes === [] ? null : reset($mailboxes);
$delimiter = $rootMailbox === false ? '/' : ($rootMailbox?->delimiter() ?? '/'); $delimiter = $rootMailbox === false ? '/' : ($rootMailbox?->delimiter() ?? '/');
$label = rtrim((string) $path, $delimiter) . $delimiter . ltrim($label, $delimiter); $label = rtrim((string) $path, $delimiter) . $delimiter . ltrim($label, $delimiter);
} }
$mailbox = $this->remoteService->collectionCreate($label); $mailbox = $this->liveService->collectionCreate($label);
$collection = $this->collectionFresh(); $collection = $this->collectionFresh();
$collection->fromImap($mailbox, ['delimiter' => $delimiter ?? null]); $collection->fromImap($mailbox, ['delimiter' => $delimiter ?? null]);
@@ -462,7 +462,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
// In IMAP, "update" = rename to the new label // In IMAP, "update" = rename to the new label
$oldPath = (string) $target->collection(); $oldPath = (string) $target->collection();
$newName = $properties->getLabel(); $newName = $properties->getLabel();
$mailbox = $this->remoteService->collectionRename($oldPath, $newName); $mailbox = $this->liveService->collectionRename($oldPath, $newName);
$collection = $this->collectionFresh(); $collection = $this->collectionFresh();
$collection->fromImap($mailbox); $collection->fromImap($mailbox);
@@ -489,7 +489,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$result = match ($deleteMode) { $result = match ($deleteMode) {
'soft' => $this->collectionMove(new CollectionIdentifier($target->provider(), $target->service(), $deleteTarget), $target), 'soft' => $this->collectionMove(new CollectionIdentifier($target->provider(), $target->service(), $deleteTarget), $target),
'hard' => $this->remoteService->collectionDestroy((string) $target->collection()), 'hard' => $this->liveService->collectionDestroy((string) $target->collection()),
}; };
return $result; return $result;
} }
@@ -498,8 +498,8 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
{ {
$this->initialize(); $this->initialize();
$sourceMailbox = $this->remoteService->collectionFetch((string) $source->collection()); $sourceMailbox = $this->liveService->collectionFetch((string) $source->collection());
$targetMailbox = $this->remoteService->collectionFetch((string) $target->collection()); $targetMailbox = $this->liveService->collectionFetch((string) $target->collection());
if ($sourceMailbox === null) { if ($sourceMailbox === null) {
throw new \RuntimeException('Source collection not found for move operation'); throw new \RuntimeException('Source collection not found for move operation');
} }
@@ -515,7 +515,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$freshPath = rtrim($targetMailbox->name(), $targetDelimiter) . $targetDelimiter . end($extantPathLeafs); $freshPath = rtrim($targetMailbox->name(), $targetDelimiter) . $targetDelimiter . end($extantPathLeafs);
$mutatedMailbox = $this->remoteService->collectionRename($extantPath, $freshPath); $mutatedMailbox = $this->liveService->collectionRename($extantPath, $freshPath);
$collection = $this->collectionFresh(); $collection = $this->collectionFresh();
$collection->fromImap($mutatedMailbox, ['delimiter' => $targetDelimiter]); $collection->fromImap($mutatedMailbox, ['delimiter' => $targetDelimiter]);
@@ -531,7 +531,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
{ {
$this->initialize(); $this->initialize();
foreach ($this->remoteService->entityList((string) $collection, $filter, $sort, $range) as $identifier => $message) { foreach ($this->liveService->entityList((string) $collection, $filter, $sort, $range) as $identifier => $message) {
$resource = $this->entityFresh(); $resource = $this->entityFresh();
$resource->fromImap($message, $collection); $resource->fromImap($message, $collection);
yield $resource->urn() => $resource; yield $resource->urn() => $resource;
@@ -569,7 +569,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
foreach ($identifiers as $collection => $entities) { foreach ($identifiers as $collection => $entities) {
$uids = array_keys($entities); $uids = array_keys($entities);
foreach ($this->remoteService->entityFetch((string) $collection, null, ...$uids) as $uid => $message) { foreach ($this->liveService->entityFetch((string) $collection, null, ...$uids) as $uid => $message) {
$resource = $this->entityFresh(); $resource = $this->entityFresh();
$resource->fromImap($message, $collection); $resource->fromImap($message, $collection);
yield $resource->urn() => $resource; yield $resource->urn() => $resource;
@@ -584,7 +584,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$uid = (int) $target->entity(); $uid = (int) $target->entity();
$partId = isset($part['partId']) ? (string) $part['partId'] : null; $partId = isset($part['partId']) ? (string) $part['partId'] : null;
return $this->remoteService->entityDownload($collection, $uid, $partId); return $this->liveService->entityDownload($collection, $uid, $partId);
} }
public function entityDelta(string|int $collection, string $signature, string $detail = 'ids'): Delta public function entityDelta(string|int $collection, string $signature, string $detail = 'ids'): Delta
@@ -602,7 +602,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
static fn (int $uid): bool => $uid > 0, static fn (int $uid): bool => $uid > 0,
)); ));
$existing = array_flip($this->remoteService->entityExtant((string) $collection, ...$uids)); $existing = array_flip($this->liveService->entityExtant((string) $collection, ...$uids));
$extant = []; $extant = [];
foreach ($identifiers as $id) { foreach ($identifiers as $id) {
@@ -635,7 +635,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
} }
$this->initialize(); $this->initialize();
$smtp = $this->remoteService->smtpClient(); $smtp = $this->liveService->smtpClient();
try { try {
$queueId = $smtp->send(trim($sender->getAddress()), $recipients, $raw); $queueId = $smtp->send(trim($sender->getAddress()), $recipients, $raw);
} finally { } finally {
@@ -656,7 +656,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$this->initialize(); $this->initialize();
$sentCollection = $this->resolveSentCollection(); $sentCollection = $this->resolveSentCollection();
if ($sentCollection !== null) { if ($sentCollection !== null) {
$uid = $this->remoteService->entityCreate($sentCollection, $raw, ['\\Seen']); $uid = $this->liveService->entityCreate($sentCollection, $raw, ['\\Seen']);
if ($uid !== null && $uid > 0) { if ($uid !== null && $uid > 0) {
$sentEntity = new EntityIdentifier($this->provider(), $this->identifier(), $sentCollection, (string) $uid); $sentEntity = new EntityIdentifier($this->provider(), $this->identifier(), $sentCollection, (string) $uid);
} }
@@ -675,7 +675,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
&& $source->service() === $this->identifier()) { && $source->service() === $this->identifier()) {
try { try {
$this->initialize(); $this->initialize();
$this->remoteService->entityDestroy($source->collection(), (int) $source->entity()); $this->liveService->entityDestroy($source->collection(), (int) $source->entity());
} catch (\Throwable) { } catch (\Throwable) {
// ignore — the message was already delivered // ignore — the message was already delivered
} }
@@ -698,7 +698,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$this->initialize(); $this->initialize();
[$nativeMessage, $nativeFlags] = $this->messagePayload($properties, $options); [$nativeMessage, $nativeFlags] = $this->messagePayload($properties, $options);
$created = $this->remoteService->entityCreate( $created = $this->liveService->entityCreate(
(string)$target->collection(), (string)$target->collection(),
$nativeMessage, $nativeMessage,
$nativeFlags, $nativeFlags,
@@ -719,7 +719,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$this->initialize(); $this->initialize();
[$nativeMessage, $nativeFlags] = $this->messagePayload($properties); [$nativeMessage, $nativeFlags] = $this->messagePayload($properties);
$modified = $this->remoteService->entityReplace( $modified = $this->liveService->entityReplace(
(string)$target->collection(), (string)$target->collection(),
(int)$target->entity(), (int)$target->entity(),
$nativeMessage, $nativeMessage,
@@ -755,7 +755,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
} }
} }
$mutations = $this->remoteService->entityPatch($targetCollection, $flagsAdd, $flagsRemove, ...$uids); $mutations = $this->liveService->entityPatch($targetCollection, $flagsAdd, $flagsRemove, ...$uids);
foreach ($uids as $uid) { foreach ($uids as $uid) {
$list[(string)$targetIdentifiers[$uid]] = ['disposition' => 'patched']; $list[(string)$targetIdentifiers[$uid]] = ['disposition' => 'patched'];
@@ -802,8 +802,8 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$uids = array_keys($sourceEntities); $uids = array_keys($sourceEntities);
$mutations = match ($deleteMode) { $mutations = match ($deleteMode) {
'soft' => $this->remoteService->entityMove($deleteTargetNative, $sourceCollection, ...$uids), 'soft' => $this->liveService->entityMove($deleteTargetNative, $sourceCollection, ...$uids),
'hard' => $this->remoteService->entityDestroy($sourceCollection, ...$uids), 'hard' => $this->liveService->entityDestroy($sourceCollection, ...$uids),
}; };
foreach ($uids as $uid) { foreach ($uids as $uid) {
@@ -837,7 +837,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
foreach ($sources as $sourceCollection => $sourceEntities) { foreach ($sources as $sourceCollection => $sourceEntities) {
$uids = array_keys($sourceEntities); $uids = array_keys($sourceEntities);
$mutations = $this->remoteService->entityMove($target->collection(), $sourceCollection, ...$uids); $mutations = $this->liveService->entityMove($target->collection(), $sourceCollection, ...$uids);
foreach ($uids as $uid) { foreach ($uids as $uid) {
$mutatedUid = $mutations[$uid] ?? null; $mutatedUid = $mutations[$uid] ?? null;
@@ -871,7 +871,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
foreach ($sources as $sourceCollection => $sourceEntities) { foreach ($sources as $sourceCollection => $sourceEntities) {
$uids = array_keys($sourceEntities); $uids = array_keys($sourceEntities);
$mutations = $this->remoteService->entityCopy($target->collection(), $sourceCollection, ...$uids); $mutations = $this->liveService->entityCopy($target->collection(), $sourceCollection, ...$uids);
foreach ($uids as $uid) { foreach ($uids as $uid) {
$mutatedUid = $mutations[$uid] ?? null; $mutatedUid = $mutations[$uid] ?? null;
@@ -895,7 +895,7 @@ class Service implements ServiceBaseInterface, ServiceMutableInterface, ServiceC
$filter->condition('role', $role->value); $filter->condition('role', $role->value);
/** @var Mailbox[] $mailboxes */ /** @var Mailbox[] $mailboxes */
$mailboxes = iterator_to_array($this->remoteService->collectionList(null, $filter, null)); $mailboxes = iterator_to_array($this->liveService->collectionList(null, $filter, null));
if ($mailboxes === []) { if ($mailboxes === []) {
return null; return null;
} }
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXM\ProviderImap\Service\Remote; namespace KTXM\ProviderImap\Service\Live;
use DateTimeImmutable; use DateTimeImmutable;
use Generator; use Generator;
@@ -54,9 +54,9 @@ use KTXM\ProviderImap\Smtp\Client as SmtpClient;
use RuntimeException; use RuntimeException;
/** /**
* IMAP Remote Mail Service * IMAP Live Mail Service
*/ */
class RemoteMailService class LiveMailService
{ {
private const COLLECTION_FILTER_OPTIONS = ['name', 'role', 'subscription']; private const COLLECTION_FILTER_OPTIONS = ['name', 'role', 'subscription'];
+3 -3
View File
@@ -9,7 +9,7 @@ use KTXM\ProviderImap\Client\Protocol\Command\CreateCommand;
use KTXM\ProviderImap\Client\Result\CommandCompletion; use KTXM\ProviderImap\Client\Result\CommandCompletion;
use KTXM\ProviderImap\Client\Transport\{ConnectionInterface, ConnectionFactoryInterface}; use KTXM\ProviderImap\Client\Transport\{ConnectionInterface, ConnectionFactoryInterface};
use KTXM\ProviderImap\Providers\Service; use KTXM\ProviderImap\Providers\Service;
use KTXM\ProviderImap\Service\Remote\RemoteMailService; use KTXM\ProviderImap\Service\Live\LiveMailService;
use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -46,9 +46,9 @@ final class CommandCompletionTest extends TestCase
return $client; return $client;
} }
private function service(Client $client): RemoteMailService private function service(Client $client): LiveMailService
{ {
return new class($this->createStub(Service::class), $client) extends RemoteMailService { return new class($this->createStub(Service::class), $client) extends LiveMailService {
public function __construct(Service $service, private readonly Client $client) public function __construct(Service $service, private readonly Client $client)
{ {
parent::__construct($service); parent::__construct($service);
@@ -5,14 +5,14 @@ declare(strict_types=1);
namespace KTXT\ProviderImap\Tests\Unit; namespace KTXT\ProviderImap\Tests\Unit;
use KTXM\ProviderImap\Providers\Service; use KTXM\ProviderImap\Providers\Service;
use KTXM\ProviderImap\Service\Remote\RemoteMailService; use KTXM\ProviderImap\Service\Live\LiveMailService;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RuntimeException; use RuntimeException;
final class RemoteMailServiceTest extends TestCase { final class LiveMailServiceTest extends TestCase {
public function testEntityReplaceReturnsAuthoritativeReplacementUid(): void { public function testEntityReplaceReturnsAuthoritativeReplacementUid(): void {
$service = new RemoteMailServiceStub($this->createStub(Service::class)); $service = new LiveMailServiceStub($this->createStub(Service::class));
$service->appendUid = 202; $service->appendUid = 202;
$service->destroyResults = [[101 => true]]; $service->destroyResults = [[101 => true]];
@@ -28,7 +28,7 @@ final class RemoteMailServiceTest extends TestCase {
} }
public function testEntityReplaceRemovesReplacementWhenOldUidCannotBeDeleted(): void { public function testEntityReplaceRemovesReplacementWhenOldUidCannotBeDeleted(): void {
$service = new RemoteMailServiceStub($this->createStub(Service::class)); $service = new LiveMailServiceStub($this->createStub(Service::class));
$service->appendUid = 202; $service->appendUid = 202;
$service->destroyResults = [ $service->destroyResults = [
[101 => false], [101 => false],
@@ -49,7 +49,7 @@ final class RemoteMailServiceTest extends TestCase {
} }
} }
final class RemoteMailServiceStub extends RemoteMailService { final class LiveMailServiceStub extends LiveMailService {
public ?int $appendUid = null; public ?int $appendUid = null;
public array $createCalls = []; public array $createCalls = [];
+6 -6
View File
@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace KTXT\ProviderImap\Tests\Unit; namespace KTXT\ProviderImap\Tests\Unit;
use KTXM\ProviderImap\Providers\Service; use KTXM\ProviderImap\Providers\Service;
use KTXM\ProviderImap\Service\Remote\RemoteMailService; use KTXM\ProviderImap\Service\Live\LiveMailService;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use ReflectionProperty; use ReflectionProperty;
@@ -14,18 +14,18 @@ final class ServiceTest extends TestCase
public function testEntityExtantReportsExistenceOfEachIdentifier(): void public function testEntityExtantReportsExistenceOfEachIdentifier(): void
{ {
$service = new Service(); $service = new Service();
$remote = new ServiceTestRemoteStub($this->createStub(Service::class)); $live = new ServiceTestLiveStub($this->createStub(Service::class));
$remote->existing = [5]; $live->existing = [5];
(new ReflectionProperty(Service::class, 'remoteService'))->setValue($service, $remote); (new ReflectionProperty(Service::class, 'liveService'))->setValue($service, $live);
$result = $service->entityExtant('INBOX', 5, '6', 'abc', 0); $result = $service->entityExtant('INBOX', 5, '6', 'abc', 0);
$this->assertSame([5 => true, 6 => false, 'abc' => false, 0 => false], $result); $this->assertSame([5 => true, 6 => false, 'abc' => false, 0 => false], $result);
$this->assertSame([['INBOX', [5, 6]]], $remote->extantCalls); $this->assertSame([['INBOX', [5, 6]]], $live->extantCalls);
} }
} }
final class ServiceTestRemoteStub extends RemoteMailService final class ServiceTestLiveStub extends LiveMailService
{ {
/** @var int[] */ /** @var int[] */
public array $existing = []; public array $existing = [];