generated from Nodarx/template
65fdb23fa4
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
176 lines
6.7 KiB
PHP
176 lines
6.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KTXT\ProviderImap\Tests\Unit;
|
|
|
|
use Generator;
|
|
use KTXC\Db\Collection;
|
|
use KTXC\Db\DataStore;
|
|
use KTXF\Resource\Filter\IFilter;
|
|
use KTXF\Resource\Sort\ISort;
|
|
use KTXM\ProviderImap\Client\Mailbox;
|
|
use KTXM\ProviderImap\Providers\CollectionResource;
|
|
use KTXM\ProviderImap\Providers\Service;
|
|
use KTXM\ProviderImap\Service\Cache\HarmonizationService;
|
|
use KTXM\ProviderImap\Service\Cache\MessageIngestor;
|
|
use KTXM\ProviderImap\Service\Live\LiveMailService;
|
|
use KTXM\ProviderImap\Stores\MailboxStore;
|
|
use KTXM\ProviderImap\Stores\MessageFileStore;
|
|
use KTXM\ProviderImap\Stores\MessageStore;
|
|
use LogicException;
|
|
use MongoDB\UpdateResult;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class HarmonizationServiceTest extends TestCase
|
|
{
|
|
private array $calls = [];
|
|
|
|
public function testMailboxesAreAddedUpdatedAndRemoved(): void
|
|
{
|
|
$service = $this->harmonizer(
|
|
cached: ['INBOX' => [], 'Old' => []],
|
|
remote: ['INBOX', 'Reports'],
|
|
);
|
|
|
|
$result = $service->harmonizeMailboxes();
|
|
|
|
$this->assertSame(['added' => ['Reports'], 'updated' => ['INBOX'], 'removed' => ['Old']], $result);
|
|
$this->assertSame([
|
|
['upsert', 'tenant', 'svc', 'INBOX'],
|
|
['upsert', 'tenant', 'svc', 'Reports'],
|
|
['messages.deleteByMailbox', 'svc', 'Old'],
|
|
['files.deleteByMailbox', 'tenant', 'svc', 'Old'],
|
|
['mailboxes.delete', 'svc', 'Old'],
|
|
], $this->calls);
|
|
}
|
|
|
|
public function testNothingIsPurgedWhenTheServerListHasNoInbox(): void
|
|
{
|
|
$service = $this->harmonizer(
|
|
cached: ['INBOX' => [], 'Reports' => []],
|
|
remote: [],
|
|
);
|
|
|
|
$result = $service->harmonizeMailboxes();
|
|
|
|
$this->assertSame(['added' => [], 'updated' => [], 'removed' => []], $result);
|
|
$this->assertSame([], $this->calls);
|
|
}
|
|
|
|
public function testUnboundServiceIsRejected(): void
|
|
{
|
|
$unbound = new HarmonizationService(
|
|
$this->createStub(MailboxStore::class),
|
|
$this->createStub(MessageStore::class),
|
|
$this->createStub(MessageFileStore::class),
|
|
$this->createStub(MessageIngestor::class),
|
|
);
|
|
|
|
$this->expectException(LogicException::class);
|
|
$unbound->harmonizeMailboxes();
|
|
}
|
|
|
|
public function testLockIsTakenOnlyWhenFreeExpiredOrOwned(): void
|
|
{
|
|
$collection = $this->createMock(Collection::class);
|
|
$collection->expects($this->exactly(2))
|
|
->method('updateOne')
|
|
->willReturnCallback(function (array $filter, array $update) use (&$matched): UpdateResult {
|
|
$this->assertSame(['lock' => null], $filter['$or'][0]);
|
|
$this->assertArrayHasKey('$lte', $filter['$or'][1]['lock.expiresAt']);
|
|
$this->assertSame(['lock.owner' => 'run-1'], $filter['$or'][2]);
|
|
$this->assertSame('run-1', $update['$set']['lock']['owner']);
|
|
$result = $this->createStub(UpdateResult::class);
|
|
$result->method('getMatchedCount')->willReturn(array_shift($matched));
|
|
return $result;
|
|
});
|
|
$matched = [1, 0];
|
|
|
|
$store = new MailboxStore($this->dataStore($collection));
|
|
|
|
$this->assertTrue($store->acquireLock('svc', 'INBOX', 'run-1', 300));
|
|
$this->assertFalse($store->acquireLock('svc', 'INBOX', 'run-1', 300));
|
|
}
|
|
|
|
public function testStateFillsDefaultsAndIgnoresOtherFields(): void
|
|
{
|
|
$collection = $this->createStub(Collection::class);
|
|
$collection->method('findOne')->willReturn([
|
|
'sid' => 'svc', 'name' => 'INBOX', 'uidValidity' => 7, 'lock' => ['owner' => 'x'],
|
|
]);
|
|
|
|
$state = (new MailboxStore($this->dataStore($collection)))->state('svc', 'INBOX');
|
|
|
|
$this->assertSame([...MailboxStore::STATE_DEFAULTS, 'uidValidity' => 7], $state);
|
|
}
|
|
|
|
public function testNewMailboxStartsWithDefaultState(): void
|
|
{
|
|
$collection = $this->createMock(Collection::class);
|
|
$collection->expects($this->once())
|
|
->method('updateOne')
|
|
->with(
|
|
$this->anything(),
|
|
$this->callback(fn (array $update): bool => $update['$setOnInsert'] === MailboxStore::STATE_DEFAULTS
|
|
&& array_intersect_key($update['$set'], MailboxStore::STATE_DEFAULTS) === []),
|
|
['upsert' => true],
|
|
);
|
|
|
|
$mailbox = (new CollectionResource('imap', 'svc'))->fromImap(new Mailbox('INBOX', '/', []));
|
|
(new MailboxStore($this->dataStore($collection)))->upsert('tenant', 'svc', $mailbox);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, array> $cached
|
|
* @param string[] $remote
|
|
*/
|
|
private function harmonizer(array $cached, array $remote): HarmonizationService
|
|
{
|
|
$mailboxes = $this->createStub(MailboxStore::class);
|
|
$mailboxes->method('list')->willReturn($cached);
|
|
$mailboxes->method('upsert')->willReturnCallback(function (string $tid, string $sid, $collection): void {
|
|
$this->calls[] = ['upsert', $tid, $sid, $collection->identifier()];
|
|
});
|
|
$mailboxes->method('delete')->willReturnCallback(function (string $sid, string $name): void {
|
|
$this->calls[] = ['mailboxes.delete', $sid, $name];
|
|
});
|
|
|
|
$messages = $this->createStub(MessageStore::class);
|
|
$messages->method('deleteByMailbox')->willReturnCallback(function (string $sid, string $name): void {
|
|
$this->calls[] = ['messages.deleteByMailbox', $sid, $name];
|
|
});
|
|
|
|
$files = $this->createStub(MessageFileStore::class);
|
|
$files->method('deleteByMailbox')->willReturnCallback(function (string $tid, string $sid, string $name): void {
|
|
$this->calls[] = ['files.deleteByMailbox', $tid, $sid, $name];
|
|
});
|
|
|
|
$service = (new Service())->fromStore(['tid' => 'tenant', 'sid' => 'svc']);
|
|
$live = new HarmonizationServiceTestLiveStub($service);
|
|
$live->mailboxes = array_map(static fn (string $name): Mailbox => new Mailbox($name, '/', []), $remote);
|
|
|
|
return (new HarmonizationService($mailboxes, $messages, $files, $this->createStub(MessageIngestor::class)))->for($service, $live);
|
|
}
|
|
|
|
private function dataStore(Collection $collection): DataStore
|
|
{
|
|
$dataStore = $this->createStub(DataStore::class);
|
|
$dataStore->method('selectCollection')->willReturn($collection);
|
|
return $dataStore;
|
|
}
|
|
}
|
|
|
|
final class HarmonizationServiceTestLiveStub extends LiveMailService
|
|
{
|
|
/** @var Mailbox[] */
|
|
public array $mailboxes = [];
|
|
|
|
public function collectionList(?string $location = null, IFilter|null $filter = null, ISort|null $sort = null, string $depth = '*'): Generator
|
|
{
|
|
foreach ($this->mailboxes as $mailbox) {
|
|
yield $mailbox->name() => $mailbox;
|
|
}
|
|
}
|
|
}
|