86 lines
4.7 KiB
Swift
86 lines
4.7 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import MikeMail
|
|
|
|
@MainActor
|
|
struct MailIdleTests {
|
|
@Test func classifiesServerResponses() {
|
|
#expect(IMAPClient.isMailboxChange("* 12 EXISTS"))
|
|
#expect(IMAPClient.isMailboxChange("* 2 EXPUNGE"))
|
|
#expect(IMAPClient.isMailboxChange("* 2 FETCH (FLAGS (\\Seen))"))
|
|
#expect(!IMAPClient.isMailboxChange("* OK Still here"))
|
|
#expect(!IMAPClient.isMailboxChange("M4 OK IDLE completed"))
|
|
#expect(IMAPClient.isAuthenticationFailure("M1 NO [AUTHENTICATIONFAILED] Authentication failed."))
|
|
#expect(IMAPClient.isAuthenticationFailure("M1 NO LOGIN failed"))
|
|
#expect(!IMAPClient.isAuthenticationFailure("M1 NO [UNAVAILABLE] Server temporarily unavailable"))
|
|
#expect(!IMAPClient.isAuthenticationFailure("* BYE Connection closed"))
|
|
}
|
|
|
|
@Test func idleChangesTrackSequenceNumbersWithoutRefreshing() {
|
|
var mailbox = IMAPClient.IdleMailbox(validity: "7", uids: [10, 20, 30])
|
|
#expect(mailbox.receive("* 2 FETCH (FLAGS (\\Seen))") ==
|
|
.flags(validity: "7", uid: 20, flags: ["\\Seen"]))
|
|
#expect(mailbox.receive("* 2 EXPUNGE") == .removed(validity: "7", uid: 20))
|
|
#expect(mailbox.receive("* 2 EXPUNGE") == .removed(validity: "7", uid: 30))
|
|
#expect(mailbox.receive("* 1 EXISTS") == nil)
|
|
#expect(mailbox.receive("* 2 EXISTS") == .refresh)
|
|
#expect(mailbox.receive("* 2 FETCH (UID 40 FLAGS ())") ==
|
|
.flags(validity: "7", uid: 40, flags: []))
|
|
#expect(mailbox.receive("* 2 EXPUNGE") == .removed(validity: "7", uid: 40))
|
|
#expect(mailbox.receive("* 9 EXPUNGE") == .refresh)
|
|
#expect(mailbox.receive("* OK Still here") == nil)
|
|
}
|
|
|
|
@Test func idleEchoAfterMoveOrDeletionDoesNotReloadOrReviveSource() throws {
|
|
let root = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
|
|
defer { try? FileManager.default.removeItem(at: root) }
|
|
let archive = try MailArchive(root: root)
|
|
let account = MailAccount(name: "A")
|
|
let inbox = MailFolder(accountID: account.id, path: "INBOX", name: "Inbox")
|
|
let trash = MailFolder(accountID: account.id, path: "Trash", name: "Trash")
|
|
try archive.saveConfiguration(ArchiveConfiguration(accounts: [account],
|
|
folders: [account.id: [inbox, trash]], recipients: []))
|
|
let raw = Data("Subject: Test\r\n\r\nBody".utf8)
|
|
try archive.store(raw: raw, folderID: inbox.id, validity: "7", uid: "10", flags: [])
|
|
let store = MailStore(archive: archive)
|
|
let id = inbox.id + ":7:10"
|
|
try archive.applyMessageChange(id: id, destination: trash.id,
|
|
assigned: .init(validity: "8", uid: "50"))
|
|
try store.applyIdleChange(accountID: account.id,
|
|
change: .flags(validity: "7", uid: 10, flags: ["\\Deleted"]))
|
|
try store.applyIdleChange(accountID: account.id, change: .removed(validity: "7", uid: 10))
|
|
#expect(store.archiveRevision == 0)
|
|
#expect(try archive.messages(folderID: inbox.id).isEmpty)
|
|
#expect(try archive.messages(folderID: trash.id).count == 1)
|
|
#expect(!store.isBusy)
|
|
|
|
// Changes from another client still update the archive without a network refresh.
|
|
try archive.store(raw: raw, folderID: inbox.id, validity: "7", uid: "20", flags: [])
|
|
try store.applyIdleChange(accountID: account.id,
|
|
change: .flags(validity: "7", uid: 20, flags: ["\\Seen"]))
|
|
#expect(try archive.messages(folderID: inbox.id).first?.isRead == true)
|
|
try store.applyIdleChange(accountID: account.id, change: .removed(validity: "7", uid: 20))
|
|
#expect(try archive.messages(folderID: inbox.id).isEmpty)
|
|
#expect(store.archiveRevision == 2)
|
|
}
|
|
|
|
@Test func queuesPasswordPromptsPerAccount() throws {
|
|
let root = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
|
|
defer { try? FileManager.default.removeItem(at: root) }
|
|
let archive = try MailArchive(root: root)
|
|
let a = MailAccount(name: "A"), b = MailAccount(name: "B")
|
|
try archive.saveConfiguration(ArchiveConfiguration(accounts: [a, b], folders: [:], recipients: []))
|
|
let store = MailStore(archive: archive)
|
|
store.requestPassword(accountID: a.id)
|
|
store.requestPassword(accountID: a.id)
|
|
store.requestPassword(accountID: b.id)
|
|
#expect(store.authenticationAccount?.id == a.id)
|
|
store.dismissPasswordRequest()
|
|
#expect(store.authenticationAccount?.id == b.id)
|
|
store.dismissPasswordRequest()
|
|
#expect(store.authenticationAccount == nil)
|
|
store.requestPassword(accountID: UUID())
|
|
#expect(store.authenticationAccount == nil)
|
|
}
|
|
}
|