using AIStudio.Settings.DataModel; using Lua; using Lua.Standard; using Microsoft.Extensions.Logging.Abstractions; namespace AIStudio.Tests.Settings; /// /// Checks how the mail servers of an organization are read from a configuration plugin. /// /// /// Users connect with their password to whatever server such an entry names, so an entry is taken /// exactly as written or not at all. Leaving out an invalid field instead would connect another way /// than the organization meant, e.g., to the default port of another encryption. /// [TestFixture] public sealed class MailboxProviderConfigurationTests { private static readonly Guid PLUGIN_ID = new("33333333-3333-3333-3333-333333333333"); private const string ID = "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"; private static readonly IReadOnlyDictionary REQUIRED_FIELDS = new Dictionary { ["Id"] = $"\"{ID}\"", ["Name"] = "\"Exchange\"", ["Host"] = "\"imap.example.org\"", }; [Test] public async Task AFullEntryIsReadAsWritten() { var provider = await ReadAsync(new Dictionary { ["Name"] = "\" Exchange (headquarters) \"", ["TransportSecurity"] = "\"STARTTLS\"", ["Port"] = "1143", ["UsernameHint"] = "\"Your account as DOMAIN\\\\username\"", ["HelpUrl"] = "\"https://intranet.example.org/mail/imap\"", }); Assert.That(provider, Is.Not.Null); Assert.Multiple(() => { Assert.That(provider!.Id, Is.EqualTo(ID)); Assert.That(provider.EnterpriseConfigurationPluginId, Is.EqualTo(PLUGIN_ID)); Assert.That(provider.Name, Is.EqualTo("Exchange (headquarters)")); Assert.That(provider.Host, Is.EqualTo("imap.example.org")); Assert.That(provider.TransportSecurity, Is.EqualTo(MailboxTransportSecurity.STARTTLS)); Assert.That(provider.Port, Is.EqualTo(1143)); Assert.That(provider.UsernameHint, Is.EqualTo(@"Your account as DOMAIN\username")); Assert.That(provider.HelpUrl, Is.EqualTo("https://intranet.example.org/mail/imap")); }); } [Test] public async Task WithoutAPortTheEncryptionDecidesIt() { var withoutEncryption = await ReadAsync(new Dictionary()); var withStartTls = await ReadAsync(new Dictionary { ["TransportSecurity"] = "\"STARTTLS\"" }); Assert.Multiple(() => { Assert.That(withoutEncryption?.TransportSecurity, Is.EqualTo(MailboxTransportSecurity.SSL_ON_CONNECT), "TLS from the first byte on is the safer of the two ways."); Assert.That(withoutEncryption?.Port, Is.EqualTo(993)); Assert.That(withStartTls?.Port, Is.EqualTo(143)); Assert.That(withoutEncryption?.UsernameHint, Is.Empty); Assert.That(withoutEncryption?.HelpUrl, Is.Empty); }); } [Test] public async Task AHostWithUmlautsIsKeptAsWritten() { var provider = await ReadAsync(new Dictionary { ["Host"] = "\" imap.müller.example \"" }); Assert.That(provider?.Host, Is.EqualTo("imap.müller.example"), "The dialog shows the host the way the organization wrote it; only the connection uses its ASCII form."); } [TestCase("Id", null)] [TestCase("Id", "\"not a GUID\"")] [TestCase("Name", null)] [TestCase("Name", "\" \"")] [TestCase("Host", null)] [TestCase("Host", "\"\"")] [TestCase("Host", "\"imaps://imap.example.org\"")] [TestCase("Host", "\"imap.example.org:993\"")] [TestCase("TransportSecurity", "\"NONE\"")] [TestCase("TransportSecurity", "\"UNKNOWN\"")] [TestCase("TransportSecurity", "\"SSL_ON_CONNECT, STARTTLS\"")] [TestCase("Port", "0")] [TestCase("Port", "65536")] [TestCase("HelpUrl", "\"ftp://intranet.example.org/mail\"")] [TestCase("HelpUrl", "\"/mail/imap\"")] public async Task AnEntryWithAnInvalidFieldIsLeftOut(string field, string? value) { var provider = await ReadAsync(new Dictionary { [field] = value }); Assert.That(provider, Is.Null); } /// /// Reads an entry made of the required fields, changed by the given ones. /// /// The fields to add or to replace, as Lua expressions; null removes a field. private static async Task ReadAsync(IReadOnlyDictionary changedFields) { var fields = new Dictionary(REQUIRED_FIELDS); foreach (var (name, value) in changedFields) fields[name] = value; var entry = string.Join(",\n", fields.Where(field => field.Value is not null).Select(field => $"[\"{field.Key}\"] = {field.Value}")); var state = LuaState.Create(); state.OpenBasicLibrary(); await state.DoStringAsync($$""" PROVIDER = { {{entry}} } """); if (!state.Environment["PROVIDER"].TryRead(out var table)) throw new InvalidOperationException("The entry of this test is not a Lua table."); return DataMailboxProvider.TryParseConfiguration(1, table, PLUGIN_ID, NullLogger.Instance, out var provider) ? provider : null; } }