using System.Reflection; using FluentAssertions; using Moq; using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Integration.Processors; using Wino.Core.Synchronizers.ImapSync; using Wino.Core.Synchronizers.Mail; using Xunit; namespace Wino.Core.Tests.Synchronizers; public class ImapSynchronizerCalDavConfigurationTests { [Fact] public async Task ResolveCalDavServiceUriAsync_UsesExplicitConfigurationBeforeAutoDiscovery() { var tempDirectory = CreateTempDirectory(); var autoDiscovery = new Mock(MockBehavior.Strict); var serverInformation = CreateServerInformation(); serverInformation.CalDavServiceUrl = "https://caldav.explicit.example.com/"; var synchronizer = CreateSynchronizer(tempDirectory, serverInformation, autoDiscovery.Object); try { var resolvedUri = await InvokePrivateAsync(synchronizer, "ResolveCalDavServiceUriAsync", CancellationToken.None); resolvedUri.Should().Be(new Uri("https://caldav.explicit.example.com/")); autoDiscovery.Verify(a => a.DiscoverCalDavServiceUriAsync(It.IsAny(), It.IsAny()), Times.Never); } finally { await synchronizer.KillSynchronizerAsync(); DeleteDirectory(tempDirectory); } } [Fact] public async Task ResolveCalDavPassword_PrefersExplicitCalDavPassword() { var tempDirectory = CreateTempDirectory(); var serverInformation = CreateServerInformation(); serverInformation.IncomingServerPassword = "incoming-password"; serverInformation.OutgoingServerPassword = "outgoing-password"; serverInformation.CalDavPassword = "caldav-password"; var synchronizer = CreateSynchronizer(tempDirectory, serverInformation); try { var password = InvokePrivate(synchronizer, "ResolveCalDavPassword"); password.Should().Be("caldav-password"); } finally { await synchronizer.KillSynchronizerAsync(); DeleteDirectory(tempDirectory); } } [Fact] public async Task ResolveCalDavUsername_PrefersExplicitCalDavUsername() { var tempDirectory = CreateTempDirectory(); var serverInformation = CreateServerInformation(); serverInformation.Address = "fallback@example.com"; serverInformation.CalDavUsername = "calendar-user@example.com"; var synchronizer = CreateSynchronizer(tempDirectory, serverInformation); try { var username = InvokePrivate(synchronizer, "ResolveCalDavUsername"); username.Should().Be("calendar-user@example.com"); } finally { await synchronizer.KillSynchronizerAsync(); DeleteDirectory(tempDirectory); } } private static ImapSynchronizer CreateSynchronizer(string appDataFolder, CustomServerInformation serverInformation, IAutoDiscoveryService? autoDiscoveryService = null) { var account = new MailAccount { Id = Guid.NewGuid(), Name = "IMAP Test", Address = "test@example.com", ProviderType = MailProviderType.IMAP4, IsCalendarAccessGranted = true, ServerInformation = serverInformation }; var applicationConfiguration = new Mock(); applicationConfiguration.SetupProperty(x => x.ApplicationDataFolderPath, appDataFolder); applicationConfiguration.SetupProperty(x => x.PublisherSharedFolderPath, appDataFolder); applicationConfiguration.SetupProperty(x => x.ApplicationTempFolderPath, appDataFolder); applicationConfiguration.SetupGet(x => x.SentryDNS).Returns(string.Empty); var unifiedSynchronizer = new UnifiedImapSynchronizer( Mock.Of(), Mock.Of(), Mock.Of()); return new ImapSynchronizer( account, Mock.Of(), applicationConfiguration.Object, unifiedSynchronizer, Mock.Of(), Mock.Of(), autoDiscoveryService ?? Mock.Of(), Mock.Of()); } private static CustomServerInformation CreateServerInformation() => new() { Id = Guid.NewGuid(), IncomingServer = "imap.example.com", IncomingServerPort = "993", IncomingServerUsername = "user@example.com", IncomingServerPassword = "password", OutgoingServer = "smtp.example.com", OutgoingServerPort = "587", OutgoingServerUsername = "user@example.com", OutgoingServerPassword = "password", MaxConcurrentClients = 5, CalendarSupportMode = ImapCalendarSupportMode.CalDav }; private static string CreateTempDirectory() { var path = Path.Combine(Path.GetTempPath(), "wino-imap-caldav-tests", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(path); return path; } private static void DeleteDirectory(string path) { if (Directory.Exists(path)) { Directory.Delete(path, recursive: true); } } private static T InvokePrivate(object instance, string methodName) { var method = instance.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"Method '{methodName}' not found."); return (T)method.Invoke(instance, null)!; } private static async Task InvokePrivateAsync(object instance, string methodName, params object[] parameters) { var method = instance.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"Method '{methodName}' not found."); var task = (Task)method.Invoke(instance, parameters)!; return await task.ConfigureAwait(false); } }