Files
provider_imap/tests/php/Unit/MessageStoreChangesTest.php
T
2026-10-07 21:21:14 -04:00

93 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace KTXT\ProviderImap\Tests\Unit;
use ArrayIterator;
use KTXC\Db\Collection;
use KTXC\Db\Cursor;
use KTXC\Db\DataStore;
use KTXM\ProviderImap\Stores\MailboxStore;
use KTXM\ProviderImap\Stores\MessageStore;
use MongoDB\Collection as MongoCollection;
use MongoDB\Operation\FindOneAndUpdate;
use PHPUnit\Framework\TestCase;
final class MessageStoreChangesTest extends TestCase
{
public function testChangesQueriesAdditionsModificationsAndDeletions(): void
{
$filters = [];
$collection = $this->createStub(Collection::class);
$collection->method('find')->willReturnCallback(function (array $filter) use (&$filters): Cursor {
$filters[] = $filter;
return new Cursor(new ArrayIterator([['uid' => count($filters)]]));
});
$changes = (new MessageStore($this->dataStore($collection)))->changes('svc', 'INBOX', 7, 10);
$key = ['sid' => 'svc', 'mailbox' => 'INBOX', 'uidValidity' => 7];
$this->assertSame([
[...$key, 'removedSeq' => null, 'addedSeq' => ['$gt' => 10]],
[...$key, 'removedSeq' => null, 'addedSeq' => ['$lte' => 10], 'modSeq' => ['$gt' => 10]],
[...$key, 'addedSeq' => ['$lte' => 10], 'removedSeq' => ['$gt' => 10]],
], $filters);
$this->assertSame(['additions' => [1], 'modifications' => [2], 'deletions' => [3]], $changes);
}
public function testTombstoneKeepsKeyAndSequencesOnly(): void
{
$collection = $this->createMock(Collection::class);
$collection->expects($this->once())
->method('updateMany')
->with(
['sid' => 'svc', 'mailbox' => 'INBOX', 'uidValidity' => 7, 'uid' => ['$in' => [3, 4]], 'removedSeq' => null],
$this->callback(function (array $update): bool {
$this->assertSame(['removedSeq' => 12, 'modSeq' => 12], $update['$set']);
$this->assertArrayHasKey('subject', $update['$unset']);
$this->assertArrayHasKey('flags', $update['$unset']);
$this->assertArrayNotHasKey('addedSeq', $update['$unset']);
return true;
}),
);
(new MessageStore($this->dataStore($collection)))->tombstone('svc', 'INBOX', 7, 12, 3, 4);
}
public function testLiveReadsSkipTombstones(): void
{
$collection = $this->createMock(Collection::class);
$collection->expects($this->once())
->method('find')
->with($this->callback(fn (array $filter): bool => array_key_exists('removedSeq', $filter) && $filter['removedSeq'] === null))
->willReturn(new Cursor(new ArrayIterator([])));
(new MessageStore($this->dataStore($collection)))->flags('svc', 'INBOX', 7);
}
public function testReserveSequenceReturnsTheFirstNumberOfTheRange(): void
{
$mongo = $this->createMock(MongoCollection::class);
$mongo->expects($this->once())
->method('findOneAndUpdate')
->with(
['sid' => 'svc', 'name' => 'INBOX'],
['$inc' => ['changeSeq' => 3]],
$this->callback(fn (array $options): bool => $options['returnDocument'] === FindOneAndUpdate::RETURN_DOCUMENT_AFTER),
)
->willReturn(['changeSeq' => 12]);
$collection = $this->createStub(Collection::class);
$collection->method('getMongoCollection')->willReturn($mongo);
$this->assertSame(10, (new MailboxStore($this->dataStore($collection)))->reserveSequence('svc', 'INBOX', 3));
}
private function dataStore(Collection $collection): DataStore
{
$dataStore = $this->createStub(DataStore::class);
$dataStore->method('selectCollection')->willReturn($collection);
return $dataStore;
}
}