feat: serve message fetches from the cache

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-10-07 22:20:13 -04:00
parent b1e92e5b07
commit 5e458c5bc8
7 changed files with 261 additions and 4 deletions
+75
View File
@@ -255,6 +255,81 @@ final class CachedServiceTest extends TestCase
$this->assertCount(2, $this->service->entityListBulk('INBOX'));
}
public function testFetchComesFromTheCache(): void
{
$this->harmonized([1 => [], 2 => ['\\Flagged']]);
$this->live->fetchBatches = [];
$entities = array_values($this->service->entityFetchBulk(
new EntityIdentifier('imap', 'svc', 'INBOX', '2'),
));
$this->assertCount(1, $entities);
$this->assertSame(['flagged' => true], $entities[0]->getProperties()->getFlags());
$this->assertSame([], $this->live->fetchBatches);
}
public function testFetchOfAnUncachedMessageGoesToTheServerAndCaches(): void
{
$this->harmonized([1 => []]);
$this->live->flags[5] = [];
$this->live->fetchBatches = [];
$entities = array_values($this->service->entityFetchBulk(new EntityIdentifier('imap', 'svc', 'INBOX', '5')));
$this->assertSame('5', (string) $entities[0]->identifier());
$this->assertSame([[5]], $this->live->fetchBatches);
$this->assertContains(5, $this->files->uids(7));
}
public function testFetchFromANeverHarmonizedMailboxIsReadFromTheServer(): void
{
$this->live->flags = [3 => []];
$entities = array_values($this->service->entityFetchBulk(new EntityIdentifier('imap', 'svc', 'Sent', '3')));
$this->assertSame('3', (string) $entities[0]->identifier());
$this->assertSame([], $this->files->uids(7), 'nothing is cached without a mailbox generation');
}
public function testFetchRejectsIdentifiersOfAnotherService(): void
{
$this->expectException(\InvalidArgumentException::class);
iterator_to_array($this->service->entityFetchStream(new EntityIdentifier('imap', 'other', 'INBOX', '1')));
}
public function testIncompleteSectionsAreCompletedOnceAndStored(): void
{
$this->harmonized([1 => []]);
$content = $this->files->content(7, 1);
$content['incomplete'] = ['1'];
$content['properties']['body'] = ['partId' => '1', 'type' => 'text/plain', 'content' => 'Hello wor'];
$this->files->write('tenant', 'svc', 'INBOX', 7, 1, $content);
$this->live->sections = [1 => ['1' => 'Hello world, the full text.']];
$listed = array_values(iterator_to_array($this->service->entityListStream('INBOX'), false));
$fetched = array_values($this->service->entityFetchBulk(new EntityIdentifier('imap', 'svc', 'INBOX', '1')));
$this->assertSame('Hello world, the full text.', $listed[0]->getProperties()->getBodyTextPlain());
$this->assertSame('Hello world, the full text.', $fetched[0]->getProperties()->getBodyTextPlain());
$this->assertSame([], $this->files->content(7, 1)['incomplete']);
$this->assertCount(1, $this->calls('messageSections'), 'completed once, then served from the cache');
}
public function testIncompleteSectionsStayCutOffWhenTheServerDoesNotReturnThem(): void
{
$this->harmonized([1 => []]);
$content = $this->files->content(7, 1);
$content['incomplete'] = ['1'];
$content['properties']['body'] = ['partId' => '1', 'type' => 'text/plain', 'content' => 'Hello wor'];
$this->files->write('tenant', 'svc', 'INBOX', 7, 1, $content);
$fetched = array_values($this->service->entityFetchBulk(new EntityIdentifier('imap', 'svc', 'INBOX', '1')));
$this->assertSame('Hello wor', $fetched[0]->getProperties()->getBodyTextPlain());
$this->assertSame(['1'], $this->files->content(7, 1)['incomplete']);
}
/**
* Harmonize INBOX with the given server UIDs and flags.
*