2026-03-01 12:07:15 +01:00
|
|
|
using FluentAssertions;
|
|
|
|
|
using Wino.Core.Domain.Entities.Mail;
|
|
|
|
|
using Wino.Core.Domain.Enums;
|
|
|
|
|
using Wino.Core.Domain.Interfaces;
|
|
|
|
|
using Wino.Mail.ViewModels.Collections;
|
|
|
|
|
using Wino.Mail.ViewModels.Data;
|
|
|
|
|
using Xunit;
|
|
|
|
|
|
|
|
|
|
namespace Wino.Mail.ViewModels.Tests.Data;
|
|
|
|
|
|
|
|
|
|
public class MailItemViewModelUpdateTests
|
|
|
|
|
{
|
|
|
|
|
[Fact]
|
|
|
|
|
public void UpdateFrom_ShouldNotifyOnlyReadState_WhenSameInstanceAndHintProvided()
|
|
|
|
|
{
|
|
|
|
|
var mailCopy = CreateMailCopy("thread-1", DateTime.UtcNow);
|
|
|
|
|
var sut = new MailItemViewModel(mailCopy);
|
|
|
|
|
var raisedProperties = new List<string>();
|
|
|
|
|
|
|
|
|
|
sut.PropertyChanged += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(e.PropertyName))
|
|
|
|
|
{
|
|
|
|
|
raisedProperties.Add(e.PropertyName);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mailCopy.IsRead = true;
|
|
|
|
|
|
|
|
|
|
sut.UpdateFrom(mailCopy, MailCopyChangeFlags.IsRead);
|
|
|
|
|
|
|
|
|
|
raisedProperties.Should().Equal(nameof(MailItemViewModel.IsRead));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void UpdateFrom_ShouldNotifyAddressAndDependentSenderFields_WhenFromAddressChanges()
|
|
|
|
|
{
|
|
|
|
|
var original = CreateMailCopy("thread-1", DateTime.UtcNow);
|
|
|
|
|
original.FromName = string.Empty;
|
|
|
|
|
var updated = CloneMailCopy(original);
|
|
|
|
|
updated.FromAddress = "updated@wino.dev";
|
|
|
|
|
|
|
|
|
|
var sut = new MailItemViewModel(original);
|
|
|
|
|
var raisedProperties = new List<string>();
|
|
|
|
|
|
|
|
|
|
sut.PropertyChanged += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(e.PropertyName))
|
|
|
|
|
{
|
|
|
|
|
raisedProperties.Add(e.PropertyName);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
sut.UpdateFrom(updated);
|
|
|
|
|
|
|
|
|
|
raisedProperties.Should().Equal(
|
|
|
|
|
nameof(MailItemViewModel.FromAddress),
|
|
|
|
|
nameof(MailItemViewModel.FromName),
|
|
|
|
|
nameof(MailItemViewModel.SortingName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task UpdateMailCopy_ShouldNotifyThreadOnlyForReadState_WhenReadStateChanges()
|
|
|
|
|
{
|
|
|
|
|
var collection = new WinoMailCollection
|
|
|
|
|
{
|
|
|
|
|
CoreDispatcher = new ImmediateDispatcher()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var older = CreateMailCopy("thread-1", DateTime.UtcNow.AddMinutes(-5));
|
|
|
|
|
var latest = CreateMailCopy("thread-1", DateTime.UtcNow);
|
|
|
|
|
|
|
|
|
|
await collection.AddAsync(older);
|
|
|
|
|
await collection.AddAsync(latest);
|
|
|
|
|
|
|
|
|
|
ThreadMailItemViewModel? threadItem = null;
|
|
|
|
|
foreach (var group in collection.MailItems)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in group)
|
|
|
|
|
{
|
|
|
|
|
if (item is ThreadMailItemViewModel thread)
|
|
|
|
|
{
|
|
|
|
|
threadItem = thread;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (threadItem != null)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
threadItem.Should().NotBeNull();
|
|
|
|
|
|
|
|
|
|
var raisedProperties = new List<string>();
|
|
|
|
|
threadItem!.PropertyChanged += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(e.PropertyName))
|
|
|
|
|
{
|
|
|
|
|
raisedProperties.Add(e.PropertyName);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
latest.IsRead = true;
|
|
|
|
|
|
2026-04-07 16:48:46 +02:00
|
|
|
await collection.UpdateMailCopy(latest, EntityUpdateSource.ClientUpdated, MailCopyChangeFlags.IsRead);
|
2026-03-01 12:07:15 +01:00
|
|
|
|
|
|
|
|
raisedProperties.Should().Equal(nameof(ThreadMailItemViewModel.IsRead));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static MailCopy CreateMailCopy(string threadId, DateTime creationDate)
|
|
|
|
|
=> new()
|
|
|
|
|
{
|
|
|
|
|
UniqueId = Guid.NewGuid(),
|
|
|
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
|
|
|
FolderId = Guid.NewGuid(),
|
|
|
|
|
ThreadId = threadId,
|
|
|
|
|
MessageId = $"message-{Guid.NewGuid():N}",
|
|
|
|
|
References = string.Empty,
|
|
|
|
|
InReplyTo = string.Empty,
|
|
|
|
|
FromName = "Sender",
|
|
|
|
|
FromAddress = "sender@wino.dev",
|
|
|
|
|
Subject = "Subject",
|
|
|
|
|
PreviewText = "Preview",
|
|
|
|
|
CreationDate = creationDate,
|
|
|
|
|
Importance = MailImportance.Normal,
|
|
|
|
|
IsRead = false,
|
|
|
|
|
IsFlagged = false,
|
|
|
|
|
IsFocused = false,
|
|
|
|
|
HasAttachments = false,
|
|
|
|
|
ItemType = MailItemType.Mail,
|
|
|
|
|
DraftId = string.Empty,
|
|
|
|
|
IsDraft = false,
|
|
|
|
|
FileId = Guid.NewGuid()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static MailCopy CloneMailCopy(MailCopy source)
|
|
|
|
|
=> new()
|
|
|
|
|
{
|
|
|
|
|
UniqueId = source.UniqueId,
|
|
|
|
|
Id = source.Id,
|
|
|
|
|
FolderId = source.FolderId,
|
|
|
|
|
ThreadId = source.ThreadId,
|
|
|
|
|
MessageId = source.MessageId,
|
|
|
|
|
References = source.References,
|
|
|
|
|
InReplyTo = source.InReplyTo,
|
|
|
|
|
FromName = source.FromName,
|
|
|
|
|
FromAddress = source.FromAddress,
|
|
|
|
|
Subject = source.Subject,
|
|
|
|
|
PreviewText = source.PreviewText,
|
|
|
|
|
CreationDate = source.CreationDate,
|
|
|
|
|
Importance = source.Importance,
|
|
|
|
|
IsRead = source.IsRead,
|
|
|
|
|
IsFlagged = source.IsFlagged,
|
|
|
|
|
IsFocused = source.IsFocused,
|
|
|
|
|
HasAttachments = source.HasAttachments,
|
|
|
|
|
ItemType = source.ItemType,
|
|
|
|
|
DraftId = source.DraftId,
|
|
|
|
|
IsDraft = source.IsDraft,
|
|
|
|
|
FileId = source.FileId,
|
|
|
|
|
SenderContact = source.SenderContact,
|
|
|
|
|
AssignedAccount = source.AssignedAccount,
|
|
|
|
|
AssignedFolder = source.AssignedFolder
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private sealed class ImmediateDispatcher : IDispatcher
|
|
|
|
|
{
|
|
|
|
|
public Task ExecuteOnUIThread(Action action)
|
|
|
|
|
{
|
|
|
|
|
action();
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|