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: 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 """ 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 , 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 --\(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" --\(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: """ 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: """ let msg = MIMEParser.parse(raw) #expect(msg.inReplyTo == "parent@example.com") } }