Files
Wino-Mail/Wino.Core.Tests/Services/WinoAccountDataSyncServiceTests.cs
T

269 lines
12 KiB
C#
Raw Normal View History

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
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.Domain.Models.Accounts;
using Wino.Core.Tests.Helpers;
using Wino.Mail.Api.Contracts.Users;
using Wino.Services;
using Xunit;
namespace Wino.Core.Tests.Services;
public sealed class WinoAccountDataSyncServiceTests : IAsyncLifetime
{
private InMemoryDatabaseService _databaseService = null!;
private Mock<IWinoAccountProfileService> _profileService = null!;
private Mock<IPreferencesService> _preferencesService = null!;
private AccountService _accountService = null!;
private WinoAccountDataSyncService _service = null!;
public async Task InitializeAsync()
{
_databaseService = new InMemoryDatabaseService();
await _databaseService.InitializeAsync();
_profileService = new Mock<IWinoAccountProfileService>(MockBehavior.Strict);
_preferencesService = new Mock<IPreferencesService>();
_preferencesService.SetupProperty(a => a.StartupEntityId);
_accountService = CreateAccountService(_databaseService, _preferencesService.Object);
_service = new WinoAccountDataSyncService(_profileService.Object, _preferencesService.Object, _accountService);
}
public async Task DisposeAsync()
{
await _databaseService.DisposeAsync();
}
[Fact]
public async Task ExportAsync_ImapMailbox_MapsSanitizedPayload()
{
var accountId = Guid.NewGuid();
await _accountService.CreateAccountAsync(
new MailAccount
{
Id = accountId,
Name = "Custom IMAP",
SenderName = "Custom IMAP Sender",
Address = "imap@example.com",
ProviderType = MailProviderType.IMAP4,
SpecialImapProvider = SpecialImapProvider.iCloud,
AccountColorHex = "#123456",
IsCalendarAccessGranted = true,
SynchronizationDeltaIdentifier = "delta-token",
CalendarSynchronizationDeltaIdentifier = "calendar-delta",
Base64ProfilePictureData = "profile"
},
new CustomServerInformation
{
Id = Guid.NewGuid(),
AccountId = accountId,
Address = "imap@example.com",
IncomingServer = "imap.example.com",
IncomingServerPort = "993",
IncomingServerUsername = "imap-user",
IncomingServerPassword = "secret-incoming",
IncomingServerSocketOption = ImapConnectionSecurity.Auto,
IncomingAuthenticationMethod = ImapAuthenticationMethod.NormalPassword,
OutgoingServer = "smtp.example.com",
OutgoingServerPort = "465",
OutgoingServerUsername = "smtp-user",
OutgoingServerPassword = "secret-outgoing",
OutgoingServerSocketOption = ImapConnectionSecurity.Auto,
OutgoingAuthenticationMethod = ImapAuthenticationMethod.NormalPassword,
CalendarSupportMode = ImapCalendarSupportMode.CalDav,
CalDavServiceUrl = "https://dav.example.com",
CalDavUsername = "dav-user",
CalDavPassword = "secret-caldav",
ProxyServer = "proxy.example.com",
ProxyServerPort = "8080",
MaxConcurrentClients = 7
});
ReplaceUserMailboxesRequestDto? capturedRequest = null;
_profileService
.Setup(a => a.ReplaceMailboxesAsync(It.IsAny<ReplaceUserMailboxesRequestDto>(), It.IsAny<CancellationToken>()))
.Callback<ReplaceUserMailboxesRequestDto, CancellationToken>((request, _) => capturedRequest = request)
.Returns(Task.CompletedTask);
var result = await _service.ExportAsync(new WinoAccountSyncSelection(IncludePreferences: false, IncludeAccounts: true));
result.ExportedMailboxCount.Should().Be(1);
capturedRequest.Should().NotBeNull();
capturedRequest!.Mailboxes.Should().ContainSingle();
var exportedMailbox = capturedRequest.Mailboxes[0];
exportedMailbox.Address.Should().Be("imap@example.com");
exportedMailbox.ProviderType.Should().Be((int)MailProviderType.IMAP4);
exportedMailbox.SpecialImapProvider.Should().Be((int)SpecialImapProvider.iCloud);
exportedMailbox.AccountName.Should().Be("Custom IMAP");
exportedMailbox.SenderName.Should().Be("Custom IMAP Sender");
exportedMailbox.AccountColorHex.Should().Be("#123456");
exportedMailbox.IsCalendarAccessGranted.Should().BeTrue();
exportedMailbox.IncomingServer.Should().Be("imap.example.com");
exportedMailbox.IncomingServerUsername.Should().Be("imap-user");
exportedMailbox.OutgoingServer.Should().Be("smtp.example.com");
exportedMailbox.OutgoingServerUsername.Should().Be("smtp-user");
exportedMailbox.CalDavServiceUrl.Should().Be("https://dav.example.com");
exportedMailbox.CalDavUsername.Should().Be("dav-user");
exportedMailbox.ProxyServer.Should().Be("proxy.example.com");
exportedMailbox.ProxyServerPort.Should().Be("8080");
exportedMailbox.MaxConcurrentClients.Should().Be(7);
_profileService.Verify(a => a.SaveSettingsAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
}
[Fact]
public async Task ExportAsync_GmailMailbox_DoesNotIncludeCustomServerSettings()
{
await _accountService.CreateAccountAsync(
new MailAccount
{
Id = Guid.NewGuid(),
Name = "Gmail",
SenderName = "Gmail Sender",
Address = "gmail@example.com",
ProviderType = MailProviderType.Gmail
},
null!);
ReplaceUserMailboxesRequestDto? capturedRequest = null;
_profileService
.Setup(a => a.ReplaceMailboxesAsync(It.IsAny<ReplaceUserMailboxesRequestDto>(), It.IsAny<CancellationToken>()))
.Callback<ReplaceUserMailboxesRequestDto, CancellationToken>((request, _) => capturedRequest = request)
.Returns(Task.CompletedTask);
await _service.ExportAsync(new WinoAccountSyncSelection(IncludePreferences: false, IncludeAccounts: true));
var exportedMailbox = capturedRequest!.Mailboxes.Single();
exportedMailbox.IncomingServer.Should().BeNull();
exportedMailbox.OutgoingServer.Should().BeNull();
exportedMailbox.CalDavServiceUrl.Should().BeNull();
exportedMailbox.MaxConcurrentClients.Should().BeNull();
}
[Fact]
public async Task ImportAsync_SkipsDuplicateMailbox_ByAddressAndProviderCaseInsensitive()
{
await _accountService.CreateAccountAsync(
new MailAccount
{
Id = Guid.NewGuid(),
Name = "Existing Gmail",
SenderName = "Existing Gmail",
Address = "User@Example.com",
ProviderType = MailProviderType.Gmail
},
null!);
_profileService
.Setup(a => a.GetMailboxesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(new UserMailboxSyncListDto(
[
new UserMailboxSyncItemDto
{
Address = "user@example.com",
ProviderType = (int)MailProviderType.Gmail,
AccountName = "Duplicate Gmail"
},
new UserMailboxSyncItemDto
{
Address = "second@example.com",
ProviderType = (int)MailProviderType.Outlook,
AccountName = "New Outlook"
}
]));
var result = await _service.ImportAsync(new WinoAccountSyncSelection(IncludePreferences: false, IncludeAccounts: true));
result.ImportedMailboxCount.Should().Be(1);
result.SkippedDuplicateMailboxCount.Should().Be(1);
var accounts = await _accountService.GetAccountsAsync();
accounts.Should().HaveCount(2);
accounts.Should().Contain(a => a.Address == "second@example.com" && a.ProviderType == MailProviderType.Outlook);
}
[Fact]
public async Task ImportAsync_ImapMailbox_CreatesRootAliasAndInvalidCredentialsAttentionWithoutPasswords()
{
_profileService
.Setup(a => a.GetMailboxesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(new UserMailboxSyncListDto(
[
new UserMailboxSyncItemDto
{
Address = "imap@example.com",
ProviderType = (int)MailProviderType.IMAP4,
SpecialImapProvider = (int)SpecialImapProvider.Yahoo,
AccountName = "Imported IMAP",
SenderName = "Imported Sender",
CalendarSupportMode = (int)ImapCalendarSupportMode.CalDav,
IncomingServer = "imap.example.com",
IncomingServerPort = "993",
IncomingServerUsername = "imap-user",
IncomingServerSocketOption = (int)ImapConnectionSecurity.Auto,
IncomingAuthenticationMethod = (int)ImapAuthenticationMethod.NormalPassword,
OutgoingServer = "smtp.example.com",
OutgoingServerPort = "465",
OutgoingServerUsername = "smtp-user",
OutgoingServerSocketOption = (int)ImapConnectionSecurity.Auto,
OutgoingAuthenticationMethod = (int)ImapAuthenticationMethod.NormalPassword,
CalDavServiceUrl = "https://dav.example.com",
CalDavUsername = "dav-user",
MaxConcurrentClients = 9
}
]));
var result = await _service.ImportAsync(new WinoAccountSyncSelection(IncludePreferences: false, IncludeAccounts: true));
result.ImportedMailboxCount.Should().Be(1);
var importedAccount = (await _accountService.GetAccountsAsync()).Single();
importedAccount.AttentionReason.Should().Be(AccountAttentionReason.InvalidCredentials);
importedAccount.SynchronizationDeltaIdentifier.Should().BeEmpty();
importedAccount.CalendarSynchronizationDeltaIdentifier.Should().BeEmpty();
var importedAliases = await _accountService.GetAccountAliasesAsync(importedAccount.Id);
importedAliases.Should().ContainSingle(a => a.IsRootAlias && a.IsPrimary && a.AliasAddress == "imap@example.com");
var serverInformation = await _accountService.GetAccountCustomServerInformationAsync(importedAccount.Id);
serverInformation.Should().NotBeNull();
serverInformation.IncomingServerPassword.Should().BeEmpty();
serverInformation.OutgoingServerPassword.Should().BeEmpty();
serverInformation.CalDavPassword.Should().BeEmpty();
serverInformation.MaxConcurrentClients.Should().Be(9);
serverInformation.CalDavServiceUrl.Should().Be("https://dav.example.com");
}
private static AccountService CreateAccountService(InMemoryDatabaseService databaseService, IPreferencesService preferencesService)
{
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
});
return new AccountService(
databaseService,
signatureService.Object,
Mock.Of<IAuthenticationProvider>(),
Mock.Of<IMimeFileService>(),
preferencesService,
Mock.Of<IContactPictureFileService>());
}
}