110 lines
3.3 KiB
Swift
110 lines
3.3 KiB
Swift
import Foundation
|
|
import IMAPClient
|
|
import Models
|
|
|
|
final class MockIMAPClient: IMAPClientProtocol, @unchecked Sendable {
|
|
var mailboxes: [IMAPMailboxInfo] = []
|
|
var mailboxStatuses: [String: IMAPMailboxStatus] = [:]
|
|
var envelopes: [FetchedEnvelope] = []
|
|
/// Per-mailbox envelopes — takes precedence over flat `envelopes` when set
|
|
var mailboxEnvelopes: [String: [FetchedEnvelope]] = [:]
|
|
var flagUpdates: [UIDFlagsPair] = []
|
|
var bodies: [Int: (text: String?, html: String?)] = [:]
|
|
|
|
var connectCalled = false
|
|
var disconnectCalled = false
|
|
var selectedMailbox: String?
|
|
|
|
// v0.3 write operation tracking
|
|
var storedFlags: [(uid: Int, mailbox: String, add: [String], remove: [String])] = []
|
|
var movedMessages: [(uid: Int, from: String, to: String)] = []
|
|
var copiedMessages: [(uid: Int, from: String, to: String)] = []
|
|
var expungedMailboxes: [String] = []
|
|
var appendedMessages: [(mailbox: String, message: Data, flags: [String])] = []
|
|
var serverCapabilities: Set<String> = ["IMAP4rev1", "MOVE"]
|
|
var fullMessages: [Int: String] = [:]
|
|
var sections: [String: Data] = [:] // key: "uid-section"
|
|
var fetchFullMessageCalls: [Int] = []
|
|
var fetchSectionCalls: [(uid: Int, mailbox: String, section: String)] = []
|
|
|
|
func connect() async throws {
|
|
connectCalled = true
|
|
}
|
|
|
|
func disconnect() async throws {
|
|
disconnectCalled = true
|
|
}
|
|
|
|
func listMailboxes() async throws -> [IMAPMailboxInfo] {
|
|
mailboxes
|
|
}
|
|
|
|
func selectMailbox(_ name: String) async throws -> IMAPMailboxStatus {
|
|
selectedMailbox = name
|
|
guard let status = mailboxStatuses[name] else {
|
|
throw MockIMAPError.mailboxNotFound(name)
|
|
}
|
|
return status
|
|
}
|
|
|
|
func fetchEnvelopes(uidsGreaterThan uid: Int) async throws -> [FetchedEnvelope] {
|
|
let source: [FetchedEnvelope]
|
|
if let mailbox = selectedMailbox, let perMailbox = mailboxEnvelopes[mailbox] {
|
|
source = perMailbox
|
|
} else {
|
|
source = envelopes
|
|
}
|
|
return source.filter { $0.uid > uid }
|
|
}
|
|
|
|
func fetchFlags(uids: ClosedRange<Int>) async throws -> [UIDFlagsPair] {
|
|
flagUpdates.filter { uids.contains($0.uid) }
|
|
}
|
|
|
|
func fetchBody(uid: Int) async throws -> (text: String?, html: String?) {
|
|
bodies[uid] ?? (nil, nil)
|
|
}
|
|
|
|
// MARK: - v0.5 attachment/MIME operations
|
|
|
|
func fetchFullMessage(uid: Int) async throws -> String {
|
|
fetchFullMessageCalls.append(uid)
|
|
return fullMessages[uid] ?? ""
|
|
}
|
|
|
|
func fetchSection(uid: Int, mailbox: String, section: String) async throws -> Data {
|
|
fetchSectionCalls.append((uid: uid, mailbox: mailbox, section: section))
|
|
return sections["\(uid)-\(section)"] ?? Data()
|
|
}
|
|
|
|
// MARK: - v0.3 write operations
|
|
|
|
func storeFlags(uid: Int, mailbox: String, add: [String], remove: [String]) async throws {
|
|
storedFlags.append((uid: uid, mailbox: mailbox, add: add, remove: remove))
|
|
}
|
|
|
|
func moveMessage(uid: Int, from: String, to: String) async throws {
|
|
movedMessages.append((uid: uid, from: from, to: to))
|
|
}
|
|
|
|
func copyMessage(uid: Int, from: String, to: String) async throws {
|
|
copiedMessages.append((uid: uid, from: from, to: to))
|
|
}
|
|
|
|
func expunge(mailbox: String) async throws {
|
|
expungedMailboxes.append(mailbox)
|
|
}
|
|
|
|
func appendMessage(to mailbox: String, message: Data, flags: [String]) async throws {
|
|
appendedMessages.append((mailbox: mailbox, message: message, flags: flags))
|
|
}
|
|
|
|
func capabilities() async throws -> Set<String> {
|
|
serverCapabilities
|
|
}
|
|
}
|
|
|
|
enum MockIMAPError: Error {
|
|
case mailboxNotFound(String)
|
|
}
|