Add local mail pinning support
This commit is contained in:
@@ -259,6 +259,26 @@ public class MailFetchingTests : IAsyncLifetime
|
||||
result.Single().FolderId.Should().Be(_inboxFolder.Id, "a copy from the actively searched folder should win over newer non-searched copies");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FetchPinnedMailsAsync_ReturnsPinnedMailsOutsideRegularPage()
|
||||
{
|
||||
var oldPinned = BuildMail(_inboxFolder.Id, DateTime.UtcNow.AddDays(-5));
|
||||
oldPinned.IsPinned = true;
|
||||
|
||||
var recentMails = Enumerable.Range(0, 120)
|
||||
.Select(i => BuildMail(_inboxFolder.Id, DateTime.UtcNow.AddMinutes(-i)))
|
||||
.ToList();
|
||||
|
||||
await _databaseService.Connection.InsertAsync(oldPinned, typeof(MailCopy));
|
||||
await _databaseService.Connection.InsertAllAsync(recentMails, typeof(MailCopy));
|
||||
|
||||
var options = BuildOptions([_inboxFolder], createThreads: false, take: 20);
|
||||
|
||||
var result = await _mailService.FetchPinnedMailsAsync(options);
|
||||
|
||||
result.Should().ContainSingle(mail => mail.UniqueId == oldPinned.UniqueId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAssignmentAsync_ExistingAssignment_IsIgnored()
|
||||
{
|
||||
@@ -297,6 +317,27 @@ public class MailFetchingTests : IAsyncLifetime
|
||||
insertedCopies.Select(mail => mail.FolderId).Should().BeEquivalentTo([_inboxFolder.Id, archiveFolder.Id]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMailAsync_PreservesLocalPinnedState()
|
||||
{
|
||||
var existingMail = BuildMail(_inboxFolder.Id, DateTime.UtcNow.AddHours(-1));
|
||||
existingMail.IsPinned = true;
|
||||
|
||||
await _databaseService.Connection.InsertAsync(existingMail, typeof(MailCopy));
|
||||
|
||||
var refreshedMail = BuildMail(_inboxFolder.Id, DateTime.UtcNow, id: existingMail.Id);
|
||||
refreshedMail.UniqueId = existingMail.UniqueId;
|
||||
refreshedMail.FileId = existingMail.FileId;
|
||||
refreshedMail.Subject = "Updated subject";
|
||||
|
||||
await _mailService.UpdateMailAsync(refreshedMail);
|
||||
|
||||
var storedMail = await _databaseService.Connection.FindAsync<MailCopy>(existingMail.UniqueId);
|
||||
storedMail.Should().NotBeNull();
|
||||
storedMail!.IsPinned.Should().BeTrue();
|
||||
storedMail.Subject.Should().Be("Updated subject");
|
||||
}
|
||||
|
||||
// ── Performance: 1 000 mails / ~70 threads ─────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -327,6 +327,94 @@ public class MailThreadingTests : IAsyncLifetime
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChangePinnedStatusAsync_SendsHydratedBulkMailUpdatedMessage()
|
||||
{
|
||||
var mail = new MailCopy
|
||||
{
|
||||
UniqueId = Guid.NewGuid(),
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
FolderId = _draftFolder.Id,
|
||||
IsPinned = false,
|
||||
Subject = "Pinned draft"
|
||||
};
|
||||
|
||||
await _databaseService.Connection.InsertAsync(mail, typeof(MailCopy));
|
||||
|
||||
var recipient = new MailUpdateRecipient();
|
||||
WeakReferenceMessenger.Default.Register<MailUpdatedMessage>(recipient);
|
||||
WeakReferenceMessenger.Default.Register<BulkMailUpdatedMessage>(recipient);
|
||||
|
||||
try
|
||||
{
|
||||
await _mailService.ChangePinnedStatusAsync([mail.UniqueId], true);
|
||||
|
||||
recipient.SingleUpdates.Should().BeEmpty();
|
||||
recipient.BulkUpdates.Should().ContainSingle();
|
||||
recipient.BulkUpdates[0].ChangedProperties.Should().Be(MailCopyChangeFlags.IsPinned);
|
||||
recipient.BulkUpdates[0].UpdatedMails.Should().ContainSingle();
|
||||
|
||||
var updatedMail = recipient.BulkUpdates[0].UpdatedMails[0];
|
||||
updatedMail.IsPinned.Should().BeTrue();
|
||||
updatedMail.AssignedFolder.Should().NotBeNull();
|
||||
updatedMail.AssignedFolder!.Id.Should().Be(_draftFolder.Id);
|
||||
updatedMail.AssignedAccount.Should().NotBeNull();
|
||||
updatedMail.AssignedAccount!.Id.Should().Be(_account.Id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WeakReferenceMessenger.Default.Unregister<MailUpdatedMessage>(recipient);
|
||||
WeakReferenceMessenger.Default.Unregister<BulkMailUpdatedMessage>(recipient);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAssignmentAsync_SendsHydratedMailAddedMessage()
|
||||
{
|
||||
var archiveFolder = new MailItemFolder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
MailAccountId = _account.Id,
|
||||
FolderName = "Archive",
|
||||
RemoteFolderId = "archive",
|
||||
SpecialFolderType = SpecialFolderType.Archive,
|
||||
IsSystemFolder = true,
|
||||
IsSynchronizationEnabled = true
|
||||
};
|
||||
|
||||
var mail = new MailCopy
|
||||
{
|
||||
UniqueId = Guid.NewGuid(),
|
||||
Id = "assignment-mail",
|
||||
FolderId = _draftFolder.Id,
|
||||
Subject = "Assigned copy"
|
||||
};
|
||||
|
||||
await _databaseService.Connection.InsertAsync(archiveFolder, typeof(MailItemFolder));
|
||||
await _databaseService.Connection.InsertAsync(mail, typeof(MailCopy));
|
||||
|
||||
var recipient = new MailAddRecipient();
|
||||
WeakReferenceMessenger.Default.Register<MailAddedMessage>(recipient);
|
||||
|
||||
try
|
||||
{
|
||||
await _mailService.CreateAssignmentAsync(_account.Id, mail.Id, archiveFolder.RemoteFolderId);
|
||||
|
||||
recipient.Added.Should().ContainSingle();
|
||||
|
||||
var addedMail = recipient.Added[0].AddedMail;
|
||||
addedMail.UniqueId.Should().NotBe(mail.UniqueId);
|
||||
addedMail.AssignedFolder.Should().NotBeNull();
|
||||
addedMail.AssignedFolder!.Id.Should().Be(archiveFolder.Id);
|
||||
addedMail.AssignedAccount.Should().NotBeNull();
|
||||
addedMail.AssignedAccount!.Id.Should().Be(_account.Id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WeakReferenceMessenger.Default.Unregister<MailAddedMessage>(recipient);
|
||||
}
|
||||
}
|
||||
|
||||
private static MimeMessage CreateReferencedMimeMessage(string subject, string? messageId = null)
|
||||
{
|
||||
var message = new MimeMessage();
|
||||
@@ -350,6 +438,13 @@ public class MailThreadingTests : IAsyncLifetime
|
||||
public void Receive(BulkMailUpdatedMessage message) => BulkUpdates.Add(message);
|
||||
}
|
||||
|
||||
internal sealed class MailAddRecipient : IRecipient<MailAddedMessage>
|
||||
{
|
||||
public List<MailAddedMessage> Added { get; } = [];
|
||||
|
||||
public void Receive(MailAddedMessage message) => Added.Add(message);
|
||||
}
|
||||
|
||||
internal sealed class MailReadStatusRecipient : IRecipient<MailReadStatusChanged>, IRecipient<BulkMailReadStatusChanged>
|
||||
{
|
||||
public List<MailReadStatusChanged> SingleUpdates { get; } = [];
|
||||
|
||||
Reference in New Issue
Block a user