macsync/@packages/imail/Tests/IMailSyncTests/MIMEParserTests.swift

165 lines
4.3 KiB
Swift

import Testing
@testable import IMailSync
@Suite("MIMEParser")
struct MIMEParserTests {
// MARK: - Header parsing
@Test("Parses Message-ID stripping angle brackets")
func messageId() {
let raw = """
Message-ID: <abc123@mail.example.com>
Subject: Hello
Body
"""
let msg = MIMEParser.parse(raw)
#expect(msg.messageId == "abc123@mail.example.com")
}
@Test("Parses basic text/plain message")
func plainTextBody() {
let raw = """
Content-Type: text/plain; charset=utf-8
Subject: Test
Hello, world!
"""
let msg = MIMEParser.parse(raw)
#expect(msg.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "Hello, world!")
#expect(msg.html == nil)
}
@Test("Parses From header into address and name")
func fromHeader() {
let raw = """
From: Quinn Test <quinn@example.com>
"""
let msg = MIMEParser.parse(raw)
#expect(msg.from?.address == "quinn@example.com")
#expect(msg.from?.name == "Quinn Test")
}
@Test("Parses bare email address in From")
func fromHeaderBare() {
let raw = """
From: quinn@example.com
"""
let msg = MIMEParser.parse(raw)
#expect(msg.from?.address == "quinn@example.com")
#expect(msg.from?.name == nil)
}
@Test("Parses multiple To recipients")
func toRecipients() {
let raw = """
To: Alice <alice@example.com>, bob@example.com
"""
let msg = MIMEParser.parse(raw)
#expect(msg.to.count == 2)
#expect(msg.to[0].address == "alice@example.com")
#expect(msg.to[0].name == "Alice")
#expect(msg.to[1].address == "bob@example.com")
#expect(msg.to[1].name == nil)
}
// MARK: - Multipart
@Test("Extracts text and html from multipart/alternative")
func multipartAlternative() {
let boundary = "----=_Part_1"
let raw = """
Content-Type: multipart/alternative; boundary="\(boundary)"
--\(boundary)
Content-Type: text/plain; charset=utf-8
Plain text body
--\(boundary)
Content-Type: text/html; charset=utf-8
<html><body>HTML body</body></html>
--\(boundary)--
"""
let msg = MIMEParser.parse(raw)
#expect(msg.text?.contains("Plain text body") == true)
#expect(msg.html?.contains("HTML body") == true)
}
@Test("Detects attachments in multipart/mixed")
func multipartMixedAttachment() {
let boundary = "----=_Part_2"
let raw = """
Content-Type: multipart/mixed; boundary="\(boundary)"
--\(boundary)
Content-Type: text/plain
Message body
--\(boundary)
Content-Type: application/pdf
Content-Disposition: attachment; filename="file.pdf"
<binary>
--\(boundary)--
"""
let msg = MIMEParser.parse(raw)
#expect(msg.hasAttachments == true)
}
// MARK: - RFC 2047
@Test("Decodes Base64-encoded RFC 2047 subject")
func rfc2047Base64() {
// =?UTF-8?B?SGVsbG8gV29ybGQ=?= decodes to "Hello World"
let raw = """
Subject: =?UTF-8?B?SGVsbG8gV29ybGQ=?=
"""
let msg = MIMEParser.parse(raw)
#expect(msg.subject == "Hello World")
}
@Test("Decodes Q-encoded RFC 2047 subject")
func rfc2047QEncoded() {
// =?iso-8859-1?Q?Caf=E9?= decodes to "Café"
let raw = """
Subject: =?iso-8859-1?Q?Caf=E9?=
"""
let msg = MIMEParser.parse(raw)
#expect(msg.subject?.hasPrefix("Caf") == true)
}
// MARK: - References
@Test("Parses References header into array")
func referencesHeader() {
let raw = """
References: <ref1@example.com> <ref2@example.com>
"""
let msg = MIMEParser.parse(raw)
#expect(msg.references.count == 2)
#expect(msg.references[0] == "ref1@example.com")
#expect(msg.references[1] == "ref2@example.com")
}
@Test("Parses In-Reply-To stripping angle brackets")
func inReplyTo() {
let raw = """
In-Reply-To: <parent@example.com>
"""
let msg = MIMEParser.parse(raw)
#expect(msg.inReplyTo == "parent@example.com")
}
}