generated from Nodarx/template
1ce2013c07
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
110 lines
5.1 KiB
PHP
110 lines
5.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KTXT\ProviderImap\Tests\Unit;
|
|
|
|
use KTXM\ProviderImap\Client\{Client, ConnectionConfig, ImapException};
|
|
use KTXM\ProviderImap\Client\Protocol\Command\CreateCommand;
|
|
use KTXM\ProviderImap\Client\Result\CommandCompletion;
|
|
use KTXM\ProviderImap\Client\Transport\{ConnectionInterface, ConnectionFactoryInterface};
|
|
use KTXM\ProviderImap\Providers\Service;
|
|
use KTXM\ProviderImap\Service\Remote\RemoteMailService;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class CommandCompletionTest extends TestCase
|
|
{
|
|
private array $commands = [];
|
|
|
|
private function client(?string $failure = null, bool $move = false, string $failureStatus = 'NO'): Client
|
|
{
|
|
$lines = ["* PREAUTH Ready\r\n"];
|
|
$connection = $this->createStub(ConnectionInterface::class);
|
|
$connection->method('readLine')->willReturnCallback(static function () use (&$lines): string {
|
|
return array_shift($lines) ?? throw new \RuntimeException('Unexpected response read');
|
|
});
|
|
$connection->method('write')->willReturnCallback(function (string $wire) use (&$lines, $failure, $move, $failureStatus): void {
|
|
[$tag, $command] = explode(' ', trim($wire), 2);
|
|
$this->commands[] = $command;
|
|
$operation = str_starts_with($command, 'UID ') ? explode(' ', $command)[1] : explode(' ', $command)[0];
|
|
if ($operation === 'CAPABILITY') {
|
|
$lines[] = '* CAPABILITY IMAP4rev1 UIDPLUS' . ($move ? ' MOVE' : '') . "\r\n";
|
|
}
|
|
$status = $operation === $failure ? $failureStatus : 'OK';
|
|
$text = $status === 'OK' ? 'Completed' : 'Denied';
|
|
if ($status === 'OK' && in_array($operation, ['COPY', 'MOVE'], true)) {
|
|
$text = '[COPYUID 7 42 142] Completed';
|
|
}
|
|
$lines[] = "$tag $status $text\r\n";
|
|
});
|
|
$factory = $this->createStub(ConnectionFactoryInterface::class);
|
|
$factory->method('create')->willReturn($connection);
|
|
$client = new Client($factory);
|
|
$client->connect(new ConnectionConfig('localhost'));
|
|
$this->commands = [];
|
|
return $client;
|
|
}
|
|
|
|
private function service(Client $client): RemoteMailService
|
|
{
|
|
return new class($this->createStub(Service::class), $client) extends RemoteMailService {
|
|
public function __construct(Service $service, private readonly Client $client)
|
|
{
|
|
parent::__construct($service);
|
|
}
|
|
|
|
public function imapClient(): Client
|
|
{
|
|
return $this->client;
|
|
}
|
|
};
|
|
}
|
|
|
|
public function testSuccessfulCommandReturnsCompletionText(): void
|
|
{
|
|
$result = $this->client()->perform(new CreateCommand('Archive'));
|
|
$this->assertInstanceOf(CommandCompletion::class, $result);
|
|
$this->assertSame('OK', $result->status());
|
|
$this->assertSame('Completed', $result->text());
|
|
}
|
|
|
|
public static function failures(): iterable
|
|
{
|
|
foreach (['NO', 'BAD'] as $status) {
|
|
yield "create $status" => ['collectionCreate', ['Archive'], 'CREATE', false, ['CREATE "Archive"'], $status];
|
|
yield "rename $status" => ['collectionRename', ['INBOX', 'Archive'], 'RENAME', false, ['RENAME "INBOX" "Archive"'], $status];
|
|
yield "delete $status" => ['collectionDestroy', ['Archive'], 'DELETE', false, ['DELETE "Archive"'], $status];
|
|
yield "copy $status" => ['entityCopy', ['Archive', 'INBOX', 42], 'COPY', false, ['SELECT "INBOX"', 'UID COPY 42 "Archive"'], $status];
|
|
yield "move $status" => ['entityMove', ['Archive', 'INBOX', 42], 'MOVE', true, ['SELECT "INBOX"', 'UID MOVE 42 "Archive"'], $status];
|
|
yield "fallback copy $status" => ['entityMove', ['Archive', 'INBOX', 42], 'COPY', false, ['SELECT "INBOX"', 'UID COPY 42 "Archive"'], $status];
|
|
yield "fallback store $status" => ['entityMove', ['Archive', 'INBOX', 42], 'STORE', false, ['SELECT "INBOX"', 'UID COPY 42 "Archive"', 'UID STORE 42 +FLAGS.SILENT (\\Deleted)'], $status];
|
|
}
|
|
}
|
|
|
|
#[DataProvider('failures')]
|
|
public function testFailuresPropagateWithoutExecutingLaterCommands(string $method, array $arguments, string $failure, bool $move, array $commands, string $status): void
|
|
{
|
|
$service = $this->service($this->client($failure, $move, $status));
|
|
try {
|
|
$service->$method(...$arguments);
|
|
$this->fail('Expected command failure');
|
|
} catch (ImapException $exception) {
|
|
$this->assertSame($failure . ' failed: Denied', $exception->getMessage());
|
|
}
|
|
$this->assertSame($commands, $this->commands);
|
|
}
|
|
|
|
public function testMoveFallbackDeletesOnlyAfterCopySucceeds(): void
|
|
{
|
|
$result = $this->service($this->client())->entityMove('Archive', 'INBOX', 42);
|
|
$this->assertSame([42 => '142'], $result);
|
|
$this->assertSame([
|
|
'SELECT "INBOX"',
|
|
'UID COPY 42 "Archive"',
|
|
'UID STORE 42 +FLAGS.SILENT (\\Deleted)',
|
|
'UID EXPUNGE 42',
|
|
], $this->commands);
|
|
}
|
|
}
|