using AIStudio.Settings.DataModel;
using AIStudio.Tools.Mail;
using AIStudio.Tools.Validation;
using MailKit;
using MimeKit;
namespace AIStudio.Tests.Tools.Mail;
///
/// Checks which attachments of a mail get their text read.
///
///
/// All of it is decided from the structure of the mail, before anything of an attachment is fetched.
///
[TestFixture]
public sealed class MailAttachmentRulesTests
{
///
/// Where a part sits in the mail, e.g. "1.2". Nothing here looks at it.
///
private const string PART_SPECIFIER = "2";
private const long MEGABYTE = 1024L * 1024L;
[Test]
public void ADocumentWithinTheLimitIsRead() =>
Assert.That(MailAttachmentRules.GetReasonToSkip(Attachment("application", "pdf", "board-report.pdf", 2 * MEGABYTE), 10), Is.Null);
[Test]
public void AMailboxWhichReadsNoAttachmentsSkipsEveryOne()
{
var mailbox = new DataSourceMailbox { IndexAttachments = false, MaxAttachmentSizeMegabytes = 10 };
Assert.Multiple(() =>
{
Assert.That(MailAttachmentRules.GetMaxSizeMegabytes(mailbox), Is.Null);
Assert.That(MailAttachmentRules.GetReasonToSkip(Attachment("application", "pdf", "board-report.pdf", 1024), null), Is.EqualTo(MailPartTextState.ATTACHMENTS_DISABLED));
});
}
[Test]
public void AnythingButADocumentIsKnownByItsNameOnly()
{
Assert.Multiple(() =>
{
Assert.That(MailAttachmentRules.GetReasonToSkip(Attachment("image", "png", "photo.png", 1024), 10), Is.EqualTo(MailPartTextState.UNSUPPORTED_TYPE));
Assert.That(MailAttachmentRules.GetReasonToSkip(Attachment("application", "zip", "reports.zip", 1024), 10), Is.EqualTo(MailPartTextState.UNSUPPORTED_TYPE));
Assert.That(MailAttachmentRules.GetReasonToSkip(Attachment("application", "octet-stream", null, 1024), 10), Is.EqualTo(MailPartTextState.UNSUPPORTED_TYPE));
});
}
[Test]
public void TheLimitCountsTheFileNotItsEncoding()
{
// 12 MB of Base64 decode to 9 MB, which the limit of 10 MB lets through:
var base64 = Attachment("application", "pdf", "board-report.pdf", 12 * MEGABYTE);
var unencoded = Attachment("application", "pdf", "board-report.pdf", 12 * MEGABYTE, "binary");
Assert.Multiple(() =>
{
Assert.That(MailAttachmentRules.EstimateDecodedSize(base64), Is.EqualTo(9 * MEGABYTE));
Assert.That(MailAttachmentRules.GetReasonToSkip(base64, 10), Is.Null);
Assert.That(MailAttachmentRules.GetReasonToSkip(unencoded, 10), Is.EqualTo(MailPartTextState.TOO_LARGE));
});
}
[Test]
public void AnAttachmentBeyondWhatCanBeFetchedIsTooLargeWhateverTheLimit()
{
// 2.5 GB of Base64 decode to less than the largest limit, but cannot be fetched in pieces:
var attachment = Attachment("application", "pdf", "scanned-archive.pdf", 2560 * MEGABYTE);
Assert.Multiple(() =>
{
Assert.That(MailAttachmentRules.EstimateDecodedSize(attachment), Is.LessThan(DataSourceValidation.MAX_ATTACHMENT_SIZE_MEGABYTES * MEGABYTE));
Assert.That(MailAttachmentRules.GetReasonToSkip(attachment, DataSourceValidation.MAX_ATTACHMENT_SIZE_MEGABYTES), Is.EqualTo(MailPartTextState.TOO_LARGE));
});
}
[Test]
public void TheTypeComesFromTheNameOrElseFromTheContentType()
{
Assert.Multiple(() =>
{
Assert.That(MailAttachmentRules.GetExtension(Attachment("application", "octet-stream", "Minutes.DOCX", 1024)), Is.EqualTo("docx"));
Assert.That(MailAttachmentRules.GetExtension(Attachment("application", "pdf", null, 1024)), Is.EqualTo("pdf"));
Assert.That(MailAttachmentRules.GetExtension(Attachment("application", "pdf", "../../../evil.pdf", 1024)), Is.EqualTo("pdf"), "Something of the name but its type made it into the path of the file.");
Assert.That(MailAttachmentRules.GetExtension(Attachment("application", "x-unknown", "notes", 1024)), Is.Empty);
});
}
[Test]
public void ALimitBeyondTheDialogIsBroughtBackIntoRange()
{
Assert.Multiple(() =>
{
Assert.That(MailAttachmentRules.GetMaxSizeMegabytes(new DataSourceMailbox { MaxAttachmentSizeMegabytes = 25 }), Is.EqualTo(25));
Assert.That(MailAttachmentRules.GetMaxSizeMegabytes(new DataSourceMailbox { MaxAttachmentSizeMegabytes = 100_000 }), Is.EqualTo(DataSourceValidation.MAX_ATTACHMENT_SIZE_MEGABYTES));
Assert.That(MailAttachmentRules.GetMaxSizeMegabytes(new DataSourceMailbox { MaxAttachmentSizeMegabytes = -1 }), Is.EqualTo(DataSourceValidation.MIN_ATTACHMENT_SIZE_MEGABYTES));
});
}
[Test]
public void TheSignatureOfASignedMailIsNoAttachment()
{
Assert.Multiple(() =>
{
Assert.That(MailAttachmentRules.IsSignature(new ContentType("application", "pkcs7-signature")), Is.True);
Assert.That(MailAttachmentRules.IsSignature(new ContentType("application", "x-pkcs7-signature")), Is.True);
Assert.That(MailAttachmentRules.IsSignature(new ContentType("application", "pgp-signature")), Is.True);
Assert.That(MailAttachmentRules.IsSignature(new ContentType("application", "pdf")), Is.False);
});
}
private static BodyPartBasic Attachment(string mediaType, string mediaSubtype, string? fileName, long octets, string transferEncoding = "base64") => new(new ContentType(mediaType, mediaSubtype), PART_SPECIFIER)
{
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment) { FileName = fileName },
ContentTransferEncoding = transferEncoding,
Octets = (uint)octets,
};
}