mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-18 10:58:00 +00:00
Added mailboxes as local data sources (#1022)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
1 parent
93be539e50
commit
c4400c0ff6
343 files changed
+26051
-3122
No files matched your search
@@ -0,0 +1,145 @@
|
||||
using System.Net.Sockets;
|
||||
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Mail;
|
||||
|
||||
using MailKit;
|
||||
using MailKit.Net.Imap;
|
||||
using MailKit.Security;
|
||||
|
||||
namespace AIStudio.Tests.Tools.Mail;
|
||||
|
||||
/// <summary>
|
||||
/// Checks the parts of the IMAP connection which decide something without a server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Which failure an exception stands for matters more than it seems: only a rejected sign-in may
|
||||
/// pause the mailbox, and a sign-in counted as a network failure would be tried again every
|
||||
/// synchronization, until the directory locks the account.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
public sealed class ImapMailboxConnectorTests
|
||||
{
|
||||
private const string PASSWORD = "correct horse battery staple";
|
||||
|
||||
private static readonly DataSourceMailbox MAILBOX = new()
|
||||
{
|
||||
Host = "imap.example.org",
|
||||
Port = 993,
|
||||
TransportSecurity = MailboxTransportSecurity.SSL_ON_CONNECT,
|
||||
AuthMethod = MailboxAuthMethod.PASSWORD,
|
||||
Username = "someone@example.org",
|
||||
};
|
||||
|
||||
private static IEnumerable<TestCaseData> ExceptionsWithTheirFailure()
|
||||
{
|
||||
yield return Case(new AuthenticationException("The server rejected the credentials."), MailboxConnectionFailure.AUTHENTICATION_FAILED);
|
||||
yield return Case(new SslHandshakeException("The certificate is not trusted."), MailboxConnectionFailure.TLS_FAILED);
|
||||
yield return Case(new SocketException((int)SocketError.HostNotFound), MailboxConnectionFailure.NETWORK_UNAVAILABLE);
|
||||
yield return Case(new IOException("The connection was reset.", new SocketException((int)SocketError.ConnectionReset)), MailboxConnectionFailure.NETWORK_UNAVAILABLE);
|
||||
yield return Case(new TimeoutException(), MailboxConnectionFailure.NETWORK_UNAVAILABLE);
|
||||
yield return Case(new OperationCanceledException(), MailboxConnectionFailure.NETWORK_UNAVAILABLE);
|
||||
yield return Case(new ImapProtocolException("The server sent garbage."), MailboxConnectionFailure.SERVER_ERROR);
|
||||
yield return Case(new FolderNotFoundException("Archive"), MailboxConnectionFailure.SERVER_ERROR);
|
||||
yield break;
|
||||
|
||||
static TestCaseData Case(Exception exception, MailboxConnectionFailure failure) => new TestCaseData(exception, failure).SetArgDisplayNames(exception.GetType().Name, failure.ToString());
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> InvalidSettings()
|
||||
{
|
||||
yield return Case("unknown transport security", MAILBOX with { TransportSecurity = MailboxTransportSecurity.UNKNOWN }, PASSWORD);
|
||||
yield return Case("unknown auth method", MAILBOX with { AuthMethod = MailboxAuthMethod.UNKNOWN }, PASSWORD);
|
||||
yield return Case("no host", MAILBOX with { Host = " " }, PASSWORD);
|
||||
yield return Case("a URL as host", MAILBOX with { Host = "imaps://imap.example.org" }, PASSWORD);
|
||||
yield return Case("port 0", MAILBOX with { Port = 0 }, PASSWORD);
|
||||
yield return Case("port 65536", MAILBOX with { Port = 65536 }, PASSWORD);
|
||||
yield return Case("no username", MAILBOX with { Username = string.Empty }, PASSWORD);
|
||||
yield return Case("no password", MAILBOX, string.Empty);
|
||||
yield break;
|
||||
|
||||
static TestCaseData Case(string name, DataSourceMailbox mailbox, string password) => new TestCaseData(mailbox, password).SetArgDisplayNames(name);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(ExceptionsWithTheirFailure))]
|
||||
public void AnExceptionOfTheClientStandsForItsFailure(Exception exception, MailboxConnectionFailure failure)
|
||||
{
|
||||
Assert.That(ImapMailboxConnector.Classify(exception, CancellationToken.None), Is.EqualTo(failure));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ACancellationByTheUserIsNoFailure()
|
||||
{
|
||||
using var cancellation = new CancellationTokenSource();
|
||||
cancellation.Cancel();
|
||||
|
||||
Assert.That(ImapMailboxConnector.Classify(new OperationCanceledException(cancellation.Token), cancellation.Token), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnExceptionOfAnotherKindIsLeftAlone()
|
||||
{
|
||||
Assert.That(ImapMailboxConnector.Classify(new InvalidOperationException(), CancellationToken.None), Is.Null);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(InvalidSettings))]
|
||||
public async Task InvalidSettingsNeverReachTheServer(DataSourceMailbox mailbox, string password)
|
||||
{
|
||||
//
|
||||
// The host of the mailbox does not exist. Were anything sent, the failure would be a network
|
||||
// failure instead of this one.
|
||||
//
|
||||
await using var connector = new ImapMailboxConnector();
|
||||
var exception = Assert.ThrowsAsync<MailboxConnectionException>(() => connector.ConnectAsync(mailbox, password, MailServerPolicy.ANY_SERVER, CancellationToken.None));
|
||||
Assert.That(exception?.Failure, Is.EqualTo(MailboxConnectionFailure.INVALID_SETTINGS));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AServerTheOrganizationDoesNotAllowIsNeverContacted()
|
||||
{
|
||||
//
|
||||
// As above, the host of the mailbox does not exist. Were anything sent, the failure would be
|
||||
// a network failure instead of this one.
|
||||
//
|
||||
await using var connector = new ImapMailboxConnector();
|
||||
var policy = new MailServerPolicy(true, ["imap.intra.example.org"]);
|
||||
var exception = Assert.ThrowsAsync<MailboxConnectionException>(() => connector.ConnectAsync(MAILBOX, PASSWORD, policy, CancellationToken.None));
|
||||
Assert.That(exception?.Failure, Is.EqualTo(MailboxConnectionFailure.SERVER_NOT_ALLOWED));
|
||||
}
|
||||
|
||||
[TestCase("Projects", '/', ExpectedResult = true)]
|
||||
[TestCase("Projekte 2026", '.', ExpectedResult = true)]
|
||||
[TestCase("Ablage/Alt", '.', ExpectedResult = true)]
|
||||
[TestCase("Ablage/Alt", '/', ExpectedResult = false)]
|
||||
[TestCase("Ablage.Alt", '.', ExpectedResult = false)]
|
||||
[TestCase("Flat", '\0', ExpectedResult = true)]
|
||||
[TestCase("", '/', ExpectedResult = false)]
|
||||
[TestCase(" ", '/', ExpectedResult = false)]
|
||||
[TestCase(" Projects", '/', ExpectedResult = false)]
|
||||
[TestCase("All*", '/', ExpectedResult = false)]
|
||||
[TestCase("100%", '/', ExpectedResult = false)]
|
||||
[TestCase("Line\nBreak", '/', ExpectedResult = false)]
|
||||
public bool AFolderNameNeedsNoReservedCharacter(string name, char directorySeparator) => ImapMailboxConnector.IsValidFolderName(name, directorySeparator);
|
||||
|
||||
[Test]
|
||||
public void AFolderNameHasAMaximumLength()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(ImapMailboxConnector.IsValidFolderName(new string('a', ImapMailboxConnector.MAX_FOLDER_NAME_LENGTH), '/'), Is.True);
|
||||
Assert.That(ImapMailboxConnector.IsValidFolderName(new string('a', ImapMailboxConnector.MAX_FOLDER_NAME_LENGTH + 1), '/'), Is.False);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(FolderAttributes.None, ExpectedResult = MailFolderSpecialUse.NONE)]
|
||||
[TestCase(FolderAttributes.HasChildren | FolderAttributes.Subscribed, ExpectedResult = MailFolderSpecialUse.NONE)]
|
||||
[TestCase(FolderAttributes.Sent, ExpectedResult = MailFolderSpecialUse.SENT)]
|
||||
[TestCase(FolderAttributes.Drafts, ExpectedResult = MailFolderSpecialUse.DRAFTS)]
|
||||
[TestCase(FolderAttributes.Archive, ExpectedResult = MailFolderSpecialUse.ARCHIVE)]
|
||||
[TestCase(FolderAttributes.All, ExpectedResult = MailFolderSpecialUse.ALL)]
|
||||
[TestCase(FolderAttributes.Flagged, ExpectedResult = MailFolderSpecialUse.FLAGGED)]
|
||||
[TestCase(FolderAttributes.Important, ExpectedResult = MailFolderSpecialUse.IMPORTANT)]
|
||||
[TestCase(FolderAttributes.Trash | FolderAttributes.HasNoChildren, ExpectedResult = MailFolderSpecialUse.TRASH)]
|
||||
[TestCase(FolderAttributes.Junk | FolderAttributes.Important, ExpectedResult = MailFolderSpecialUse.JUNK)]
|
||||
public MailFolderSpecialUse AFolderIsForWhatTheServerAnnounces(FolderAttributes attributes) => ImapMailboxConnector.ToSpecialUse(attributes);
|
||||
}
|
||||
Reference in new issue
Block a user