Add IMAP local calendar operation tests using in-memory DB (#807)

* Add IMAP local calendar operation handler tests

* Fix tests.

* Fix calendar item show as not updating.

* Create one default calendar for local calendar accounts.
This commit is contained in:
Burak Kaan Köse
2026-02-15 18:40:32 +01:00
committed by GitHub
parent 42e51571a8
commit 2baa87daeb
7 changed files with 175 additions and 28 deletions
@@ -0,0 +1,113 @@
using FluentAssertions;
using Moq;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Tests.Helpers;
using Wino.Services;
using Xunit;
namespace Wino.Core.Tests.Services;
public class AccountServiceTests : IAsyncLifetime
{
private InMemoryDatabaseService _databaseService = null!;
private AccountService _accountService = null!;
public async Task InitializeAsync()
{
_databaseService = new InMemoryDatabaseService();
await _databaseService.InitializeAsync();
_accountService = CreateService(_databaseService);
}
public async Task DisposeAsync()
{
await _databaseService.DisposeAsync();
}
[Fact]
public async Task CreateAccountAsync_ImapLocalOnly_CreatesSinglePrimaryDefaultCalendar()
{
var accountId = Guid.NewGuid();
var account = CreateImapAccount(accountId);
var server = new CustomServerInformation
{
Id = Guid.NewGuid(),
AccountId = accountId,
CalendarSupportMode = ImapCalendarSupportMode.LocalOnly
};
await _accountService.CreateAccountAsync(account, server);
var calendars = await _databaseService.Connection.Table<Wino.Core.Domain.Entities.Calendar.AccountCalendar>()
.Where(a => a.AccountId == accountId)
.ToListAsync();
calendars.Should().HaveCount(1);
calendars[0].IsPrimary.Should().BeTrue();
calendars[0].Name.Should().Be(Translator.AccountDetailsPage_TabCalendar);
}
[Fact]
public async Task CreateAccountAsync_ImapCalDav_DoesNotCreateDefaultLocalCalendar()
{
var accountId = Guid.NewGuid();
var account = CreateImapAccount(accountId);
var server = new CustomServerInformation
{
Id = Guid.NewGuid(),
AccountId = accountId,
CalendarSupportMode = ImapCalendarSupportMode.CalDav
};
await _accountService.CreateAccountAsync(account, server);
var calendars = await _databaseService.Connection.Table<Wino.Core.Domain.Entities.Calendar.AccountCalendar>()
.Where(a => a.AccountId == accountId)
.ToListAsync();
calendars.Should().BeEmpty();
}
private static MailAccount CreateImapAccount(Guid accountId)
{
return new MailAccount
{
Id = accountId,
Name = "IMAP Test Account",
Address = "imap@test.local",
SenderName = "IMAP Test",
ProviderType = MailProviderType.IMAP4
};
}
private static AccountService CreateService(InMemoryDatabaseService databaseService)
{
var signatureService = new Mock<ISignatureService>();
signatureService
.Setup(a => a.CreateDefaultSignatureAsync(It.IsAny<Guid>()))
.ReturnsAsync((Guid accountId) => new AccountSignature
{
Id = Guid.NewGuid(),
MailAccountId = accountId,
Name = "Default",
HtmlBody = string.Empty
});
var authenticationProvider = new Mock<IAuthenticationProvider>();
var mimeFileService = new Mock<IMimeFileService>();
var preferencesService = new Mock<IPreferencesService>();
preferencesService.SetupProperty(a => a.StartupEntityId);
return new AccountService(
databaseService,
signatureService.Object,
authenticationProvider.Object,
mimeFileService.Object,
preferencesService.Object);
}
}
@@ -119,7 +119,8 @@ public class ImapSynchronizerCalDavConfigurationTests
unifiedSynchronizer,
Mock.Of<IImapSynchronizerErrorHandlerFactory>(),
Mock.Of<ICalDavClient>(),
autoDiscoveryService ?? Mock.Of<IAutoDiscoveryService>());
autoDiscoveryService ?? Mock.Of<IAutoDiscoveryService>(),
Mock.Of<ICalendarService>());
}
private static CustomServerInformation CreateServerInformation()
@@ -76,6 +76,7 @@ public class ImapSynchronizerIdleTests
unifiedSynchronizer,
Mock.Of<IImapSynchronizerErrorHandlerFactory>(),
Mock.Of<ICalDavClient>(),
Mock.Of<IAutoDiscoveryService>());
Mock.Of<IAutoDiscoveryService>(),
Mock.Of<ICalendarService>());
}
}
@@ -129,7 +129,7 @@ public partial class AccountManagementViewModel : AccountManagementPageViewModel
createdAccount.Address = accountCreationDialogResult.SpecialImapProviderDetails.Address;
createdAccount.SenderName = accountCreationDialogResult.SpecialImapProviderDetails.SenderName;
createdAccount.IsCalendarAccessGranted = customServerInformation.CalendarSupportMode == ImapCalendarSupportMode.CalDav;
createdAccount.IsCalendarAccessGranted = customServerInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled;
createdAccount.ServerInformation = customServerInformation;
await ValidateSpecialImapConnectivityAsync(customServerInformation).ConfigureAwait(false);
@@ -713,7 +713,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
{
DisplayName = DisplayName.Trim(),
EmailAddress = EmailAddress.Trim(),
IsCalendarAccessGranted = serverInformation.CalendarSupportMode == ImapCalendarSupportMode.CalDav,
IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled,
ServerInformation = serverInformation
});
@@ -735,7 +735,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
account.SenderName = DisplayName.Trim();
account.Address = EmailAddress.Trim();
account.IsCalendarAccessGranted = serverInformation.CalendarSupportMode == ImapCalendarSupportMode.CalDav;
account.IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled;
serverInformation.Id = account.ServerInformation?.Id ?? Guid.NewGuid();
serverInformation.AccountId = account.Id;
@@ -60,7 +60,7 @@
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Canvas.ZIndex="10000"
Content="{x:Bind CalendarItem}"
Content="{x:Bind CalendarItem, Mode=OneWay}"
ContentTemplateSelector="{StaticResource ShowAsStripeSelector}"
IsTabStop="False" />
+54 -22
View File
@@ -5,6 +5,8 @@ using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using CommunityToolkit.Mvvm.Messaging;
using Serilog;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
@@ -17,6 +19,22 @@ namespace Wino.Services;
public class AccountService : BaseDatabaseService, IAccountService
{
private static readonly string[] DefaultCalendarFlatColors =
[
"#B91C1C",
"#15803D",
"#0E7490",
"#1D4ED8",
"#7C3AED",
"#C026D3",
"#EC4899",
"#F97316",
"#EAB308",
"#22C55E",
"#06B6D4",
"#60A5FA"
];
public IAuthenticator ExternalAuthenticationAuthenticator { get; set; }
private readonly ISignatureService _signatureService;
@@ -528,35 +546,49 @@ public class AccountService : BaseDatabaseService, IAccountService
if (customServerInformation != null)
await Connection.InsertAsync(customServerInformation, typeof(CustomServerInformation));
if (account.ProviderType == MailProviderType.IMAP4 &&
customServerInformation?.CalendarSupportMode == ImapCalendarSupportMode.LocalOnly)
{
await EnsureDefaultLocalCalendarForImapAsync(account.Id).ConfigureAwait(false);
}
}
//public async Task<string> UpdateSynchronizationIdentifierAsync(Guid accountId, string newIdentifier)
//{
// var account = await GetAccountAsync(accountId);
private async Task EnsureDefaultLocalCalendarForImapAsync(Guid accountId)
{
var existingCalendarCount = await Connection.Table<AccountCalendar>()
.Where(a => a.AccountId == accountId)
.CountAsync()
.ConfigureAwait(false);
// if (account == null)
// {
// _logger.Error("Could not find account with id {AccountId}", accountId);
// return string.Empty;
// }
if (existingCalendarCount > 0)
return;
// var currentIdentifier = account.SynchronizationDeltaIdentifier;
var localCalendar = new AccountCalendar
{
Id = Guid.NewGuid(),
AccountId = accountId,
Name = Translator.AccountDetailsPage_TabCalendar,
IsPrimary = true,
IsSynchronizationEnabled = true,
IsExtended = true,
RemoteCalendarId = string.Empty,
TimeZone = string.Empty,
BackgroundColorHex = GetDefaultCalendarFlatColor(accountId),
TextColorHex = "#FFFFFF"
};
// bool shouldUpdateIdentifier = account.ProviderType == MailProviderType.Gmail ?
// string.IsNullOrEmpty(currentIdentifier) ? true : !string.IsNullOrEmpty(currentIdentifier)
// && ulong.TryParse(currentIdentifier, out ulong currentIdentifierValue)
// && ulong.TryParse(newIdentifier, out ulong newIdentifierValue)
// && newIdentifierValue > currentIdentifierValue : true;
await Connection.InsertAsync(localCalendar, typeof(AccountCalendar)).ConfigureAwait(false);
}
// if (shouldUpdateIdentifier)
// {
// account.SynchronizationDeltaIdentifier = newIdentifier;
private static string GetDefaultCalendarFlatColor(Guid accountId)
{
var bytes = accountId.ToByteArray();
var hash = BitConverter.ToUInt32(bytes, 0);
var index = (int)(hash % (uint)DefaultCalendarFlatColors.Length);
// await UpdateAccountAsync(account);
// }
// return account.SynchronizationDeltaIdentifier;
//}
return DefaultCalendarFlatColors[index];
}
public async Task UpdateAccountOrdersAsync(Dictionary<Guid, int> accountIdOrderPair)
{