Misc. Fixes

This commit is contained in:
Dr. Michael Kübler
2026-11-10 21:20:37 +01:00
committed by Dr. Michael Kübler
parent 49138503dd
commit 986ec72af0
7 changed files with 491 additions and 39 deletions
+116
View File
@@ -140,6 +140,122 @@ struct MailArchiveTests {
#expect(try reopened.messages(folderID: inboxA.id).count == 1)
}
@Test func unchangedLargeMailboxNeedsOnlyTwoCommands() async throws {
let folder = MailFolder(accountID: UUID(), path: "INBOX", name: "Inbox")
let flags = Dictionary(uniqueKeysWithValues: (1...10_000).map { (UInt64($0), ["\\Seen"]) })
let previous = MailboxInventory(validity: "7", flags: flags, highestModSequence: 100)
var commands: [String] = []
let result = try await IMAPClient.fetchInventory(folder: folder, previous: previous,
supportsCONDSTORE: true) { command in
commands.append(command)
if command == "EXAMINE \"INBOX\" (CONDSTORE)" {
return .init(lines: ["* OK [UIDVALIDITY 7]", "* OK [HIGHESTMODSEQ 100]"])
}
#expect(command == "UID SEARCH ALL")
return .init(lines: ["* SEARCH " + (1...10_000).map(String.init).joined(separator: " ")])
}
#expect(result == previous)
#expect(commands.count == 2) // Previously: EXAMINE + SEARCH + 50 flag batches.
}
@Test func incrementalInventoryMergesFlagsArrivalsAndDeletions() async throws {
let folder = MailFolder(accountID: UUID(), path: "INBOX", name: "Inbox")
let previous = MailboxInventory(validity: "7", flags: [1: [], 2: [], 3: ["\\Flagged"]],
highestModSequence: 100)
var commands: [String] = []
let result = try await IMAPClient.fetchInventory(folder: folder, previous: previous,
supportsCONDSTORE: true) { command in
commands.append(command)
if command.hasPrefix("EXAMINE") {
return .init(lines: ["* OK [UIDVALIDITY 7]", "* OK [HIGHESTMODSEQ 110]"])
}
if command == "UID SEARCH ALL" { return .init(lines: ["* SEARCH 1 3 4"]) }
#expect(command == "UID FETCH 1:* (UID FLAGS) (CHANGEDSINCE 100)")
return .init(lines: ["* 1 FETCH (UID 1 FLAGS (\\Seen) MODSEQ (105))",
"* 3 FETCH (UID 4 FLAGS () MODSEQ (110))"])
}
#expect(result.flags == [1: ["\\Seen"], 3: ["\\Flagged"], 4: []])
#expect(result.highestModSequence == 110)
#expect(commands.count == 3)
}
@Test(arguments: ["unsupported", "nomodseq", "validity", "rollback", "firstSync"])
func inventoryFallsBackToFullFlags(reason: String) async throws {
let folder = MailFolder(accountID: UUID(), path: "INBOX", name: "Inbox")
let previous = MailboxInventory(validity: "7", flags: [1: ["\\Seen"]], highestModSequence: 100)
let validity = reason == "validity" ? "8" : "7"
var commands: [String] = []
let result = try await IMAPClient.fetchInventory(folder: folder,
previous: reason == "firstSync" ? nil : previous, supportsCONDSTORE: reason != "unsupported") { command in
commands.append(command)
if command.hasPrefix("EXAMINE") {
let sequence = reason == "nomodseq" ? "* OK [NOMODSEQ]" :
"* OK [HIGHESTMODSEQ \(reason == "rollback" ? 90 : 110)]"
return .init(lines: ["* OK [UIDVALIDITY \(validity)]", sequence])
}
if command == "UID SEARCH ALL" { return .init(lines: ["* SEARCH 1"]) }
#expect(command == "UID FETCH 1:* (UID FLAGS)")
return .init(lines: ["* 1 FETCH (UID 1 FLAGS ())"])
}
#expect(result.validity == validity)
#expect(result.flags == [1: []])
#expect(commands.count == 3)
if reason == "unsupported" || reason == "nomodseq" {
#expect(result.highestModSequence == nil)
}
}
@Test func unchangedModSequenceStillDetectsDeletions() async throws {
let folder = MailFolder(accountID: UUID(), path: "INBOX", name: "Inbox")
let previous = MailboxInventory(validity: "7", flags: [1: [], 2: []], highestModSequence: 100)
let result = try await IMAPClient.fetchInventory(folder: folder, previous: previous,
supportsCONDSTORE: true) { command in
if command.hasPrefix("EXAMINE") {
return .init(lines: ["* OK [UIDVALIDITY 7]", "* OK [HIGHESTMODSEQ 100]"])
}
#expect(command == "UID SEARCH ALL")
return .init(lines: ["* SEARCH 2"])
}
#expect(result.flags == [2: []])
}
@Test func incompleteDeltaIsRejected() async throws {
let folder = MailFolder(accountID: UUID(), path: "INBOX", name: "Inbox")
let previous = MailboxInventory(validity: "7", flags: [1: []], highestModSequence: 100)
await #expect(throws: (any Error).self) {
_ = try await IMAPClient.fetchInventory(folder: folder, previous: previous,
supportsCONDSTORE: true) { command in
if command.hasPrefix("EXAMINE") {
return .init(lines: ["* OK [UIDVALIDITY 7]", "* OK [HIGHESTMODSEQ 110]"])
}
if command == "UID SEARCH ALL" { return .init(lines: ["* SEARCH 1 2"]) }
return .init(lines: [])
}
}
}
@Test func syncSnapshotSurvivesRelaunchAndLocalResetStillDownloadsBodies() throws {
let root = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
defer { try? FileManager.default.removeItem(at: root) }
let account = UUID()
let folder = MailFolder(accountID: account, path: "INBOX", name: "Inbox")
let inventory = MailboxInventory(validity: "7", flags: [1: []], highestModSequence: 100)
do {
let archive = try MailArchive(root: root)
try archive.store(raw: Data("Subject: Cached\r\n\r\nBody".utf8),
folderID: folder.id, validity: "7", uid: "1", flags: [], visible: false)
#expect(try archive.reconcile(folderID: folder.id, inventory: inventory))
#expect(try !archive.reconcile(folderID: folder.id, inventory: inventory))
}
let reopened = try MailArchive(root: root)
#expect(try reopened.syncInventory(folderID: folder.id) == inventory)
#expect(try reopened.knownUIDs(folderID: folder.id, validity: "7") == [1])
_ = try reopened.clearLocalMessages(accountID: account)
let snapshot = try #require(try reopened.syncInventory(folderID: folder.id))
let known = try reopened.knownUIDs(folderID: folder.id, validity: snapshot.validity)
#expect(Set(snapshot.flags.keys).subtracting(known) == [1])
}
@Test func imapInventoryParsing() throws {
#expect(try IMAPClient.uidValidity(from: ["* OK [UIDVALIDITY 123] UIDs valid"]) == "123")
#expect(throws: (any Error).self) { try IMAPClient.uidValidity(from: ["* OK hello"]) }