diff --git a/Docs/XXMessengerClient.md b/Docs/XXMessengerClient.md index efbb9de768ea89616473b3df51837785ee880dd4..c2f407c3702f5a04620dd5df035323cda3743ac1 100644 --- a/Docs/XXMessengerClient.md +++ b/Docs/XXMessengerClient.md @@ -155,4 +155,65 @@ let restoredEmail = facts.get(.email)?.value let restoredPhone = facts.get(.phone)?.value ``` -If no error was thrown during restoration, the `Messenger` is already loaded, started, connected, and logged in. \ No newline at end of file +If no error was thrown during restoration, the `Messenger` is already loaded, started, connected, and logged in. + +## 🚢 File transfers + +### Setup for receiving files + +```swift +// register receive file callback before starting file transfer manager: +let cancellable = messenger.registerReceiveFileCallback(.init { result in + switch result { + case .success(let receivedFile): + // handle file metadata... + + // start receiving file data: + try! messenger.receiveFile(.init(transferId: receivedFile.transferId)) { info in + switch info { + case .progress(let transmitted, let total): + // handle progress... + + case .finished(let data): + // handle received file data... + + case .failed(let error): + // handle error... + } + } + + case .failure(let error): + // handle error... + } +}) + +// start file transfer manager: +try messenger.startFileTransfer() +``` + +### Send files + +Make sure to call `messenger.startFileTransfer` before sending files. + +```swift +let file = FileSend( + name: ..., + type: ..., + preview: ..., + contents: ... +) + +// send file: +let transferId = try messenger.sendFile(.init(file: file, recipientId: ...)) { info in + switch info { + case .progress(let transferId, let transmitted, let total): + // handle progress... + + case .finished(let transferId): + // handle completion... + + case .failed(let transferId, let error): + // handle error... + } +} +``` diff --git a/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved index 51664bd603e976bab27b158f1ef45ac937dd18b7..482d554fe8adf685c8dc7ce64c3f7d09ccbf25e0 100644 --- a/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Examples/xx-messenger/XXMessenger.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-case-paths", "state" : { - "revision" : "7346701ea29da0a85d4403cf3d7a589a58ae3dee", - "version" : "0.9.2" + "revision" : "15bba50ebf3a2065388c8d12210debe4f6ada202", + "version" : "0.10.0" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-composable-architecture.git", "state" : { - "revision" : "9ea8c763061287052a68d5e6723fed45e898b7d9", - "version" : "0.40.2" + "revision" : "5c476994eaa79af8e466041f6de1ab116f37c528", + "version" : "0.42.0" } }, { diff --git a/Sources/XXClient/Callbacks/ReceiveFileCallback.swift b/Sources/XXClient/Callbacks/ReceiveFileCallback.swift index 3f414934745ad2f651fb024adbc496033cee8f23..2c1d94be22e7a3a63a6590e985481c2f437ee777 100644 --- a/Sources/XXClient/Callbacks/ReceiveFileCallback.swift +++ b/Sources/XXClient/Callbacks/ReceiveFileCallback.swift @@ -2,11 +2,13 @@ import Bindings import XCTestDynamicOverlay public struct ReceiveFileCallback { - public init(handle: @escaping (Result<ReceivedFile, NSError>) -> Void) { + public typealias Result = Swift.Result<ReceivedFile, NSError> + + public init(handle: @escaping (Result) -> Void) { self.handle = handle } - public var handle: (Result<ReceivedFile, NSError>) -> Void + public var handle: (Result) -> Void } extension ReceiveFileCallback { diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerReceiveFile.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerReceiveFile.swift new file mode 100644 index 0000000000000000000000000000000000000000..ef4290f829d7811c4e0c94c31afa812c095edfbc --- /dev/null +++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerReceiveFile.swift @@ -0,0 +1,87 @@ +import Foundation +import XCTestDynamicOverlay +import XXClient + +public struct MessengerReceiveFile { + public struct Params: Equatable { + public init( + transferId: Data, + callbackIntervalMS: Int = 250 + ) { + self.transferId = transferId + self.callbackIntervalMS = callbackIntervalMS + } + + public var transferId: Data + public var callbackIntervalMS: Int + } + + public enum CallbackInfo: Equatable { + public enum Failure: Equatable { + case callbackError(NSError) + case progressError(String) + case receiveError(NSError) + } + + case progress(transmitted: Int, total: Int) + case finished(Data) + case failed(Failure) + } + + public typealias Callback = (CallbackInfo) -> Void + + public enum Error: Swift.Error, Equatable { + case fileTransferNotStarted + } + + public var run: (Params, @escaping Callback) throws -> Void + + public func callAsFunction( + _ params: Params, + callback: @escaping Callback + ) throws -> Void { + try run(params, callback) + } +} + +extension MessengerReceiveFile { + public static func live(_ env: MessengerEnvironment) -> MessengerReceiveFile { + MessengerReceiveFile { params, callback in + guard let fileTransfer = env.fileTransfer() else { + throw Error.fileTransferNotStarted + } + try fileTransfer.registerReceivedProgressCallback( + transferId: params.transferId, + period: params.callbackIntervalMS, + callback: FileTransferProgressCallback { result in + switch result { + case .success(let info): + if let error = info.progress.error { + callback(.failed(.progressError(error))) + } else if info.progress.completed { + do { + callback(.finished(try fileTransfer.receive(transferId: params.transferId))) + } catch { + callback(.failed(.receiveError(error as NSError))) + } + } else { + callback(.progress( + transmitted: info.progress.transmitted, + total: info.progress.total + )) + } + + case .failure(let error): + callback(.failed(.callbackError(error))) + } + } + ) + } + } +} + +extension MessengerReceiveFile { + public static let unimplemented = MessengerReceiveFile( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerRegisterReceiveFileCallback.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerRegisterReceiveFileCallback.swift new file mode 100644 index 0000000000000000000000000000000000000000..e5dddd8a038f9c16fe9e9b6f562b95048cfee78a --- /dev/null +++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerRegisterReceiveFileCallback.swift @@ -0,0 +1,24 @@ +import XCTestDynamicOverlay +import XXClient + +public struct MessengerRegisterReceiveFileCallback { + public var run: (ReceiveFileCallback) -> Cancellable + + public func callAsFunction(_ callback: ReceiveFileCallback) -> Cancellable { + run(callback) + } +} + +extension MessengerRegisterReceiveFileCallback { + public static func live(_ env: MessengerEnvironment) -> MessengerRegisterReceiveFileCallback { + MessengerRegisterReceiveFileCallback { callback in + env.receiveFileCallbacks.register(callback) + } + } +} + +extension MessengerRegisterReceiveFileCallback { + public static let unimplemented = MessengerRegisterReceiveFileCallback( + run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {}) + ) +} diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerSendFile.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerSendFile.swift index 8aca65c4a59f7d55d350f5ee8c9b189d1217fbe5..fde8607c42e6e7f6d8d4c97da9beca79e588c589 100644 --- a/Sources/XXMessengerClient/Messenger/Functions/MessengerSendFile.swift +++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerSendFile.swift @@ -37,7 +37,7 @@ public struct MessengerSendFile { public typealias Callback = (CallbackInfo) -> Void public enum Error: Swift.Error, Equatable { - case notConnected + case fileTransferNotStarted } public var run: (Params, @escaping Callback) throws -> Data @@ -53,19 +53,9 @@ public struct MessengerSendFile { extension MessengerSendFile { public static func live(_ env: MessengerEnvironment) -> MessengerSendFile { MessengerSendFile { params, callback in - guard let e2e = env.e2e() else { - throw Error.notConnected + guard let fileTransfer = env.fileTransfer() else { + throw Error.fileTransferNotStarted } - let fileTransfer = try env.initFileTransfer( - params: InitFileTransfer.Params( - e2eId: e2e.getId(), - e2eFileTransferParamsJSON: env.getE2EFileTransferParams(), - fileTransferParamsJSON: env.getFileTransferParams() - ), - callback: ReceiveFileCallback { _ in - fatalError("Bindings issue: ReceiveFileCallback called when sending file.") - } - ) func close(id: Data) { do { try fileTransfer.closeSend(transferId: id) diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerStartFileTransfer.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerStartFileTransfer.swift new file mode 100644 index 0000000000000000000000000000000000000000..11dfd35a4cca4cb15e7fb3d3e8f27b85501b322f --- /dev/null +++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerStartFileTransfer.swift @@ -0,0 +1,39 @@ +import XCTestDynamicOverlay +import XXClient + +public struct MessengerStartFileTransfer { + public enum Error: Swift.Error, Equatable { + case notConnected + } + + public var run: () throws -> Void + + public func callAsFunction() throws -> Void { + try run() + } +} + +extension MessengerStartFileTransfer { + public static func live(_ env: MessengerEnvironment) -> MessengerStartFileTransfer { + MessengerStartFileTransfer { + guard let e2e = env.e2e() else { + throw Error.notConnected + } + let fileTransfer = try env.initFileTransfer( + params: InitFileTransfer.Params( + e2eId: e2e.getId(), + e2eFileTransferParamsJSON: env.getE2EFileTransferParams(), + fileTransferParamsJSON: env.getFileTransferParams() + ), + callback: env.receiveFileCallbacks.registered() + ) + env.fileTransfer.set(fileTransfer) + } + } +} + +extension MessengerStartFileTransfer { + public static let unimplemented = MessengerStartFileTransfer( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift index dc427859d3b5e765e047112a9c2fd7f344f09fac..d565ebb2b6515f00bacd776be88f83f7bcd7401c 100644 --- a/Sources/XXMessengerClient/Messenger/Messenger.swift +++ b/Sources/XXMessengerClient/Messenger/Messenger.swift @@ -40,7 +40,10 @@ public struct Messenger { public var stopBackup: MessengerStopBackup public var setLogLevel: MessengerSetLogLevel public var startLogging: MessengerStartLogging + public var registerReceiveFileCallback: MessengerRegisterReceiveFileCallback + public var startFileTransfer: MessengerStartFileTransfer public var sendFile: MessengerSendFile + public var receiveFile: MessengerReceiveFile } extension Messenger { @@ -85,7 +88,10 @@ extension Messenger { stopBackup: .live(env), setLogLevel: .live(env), startLogging: .live(env), - sendFile: .live(env) + registerReceiveFileCallback: .live(env), + startFileTransfer: .live(env), + sendFile: .live(env), + receiveFile: .live(env) ) } } @@ -131,6 +137,9 @@ extension Messenger { stopBackup: .unimplemented, setLogLevel: .unimplemented, startLogging: .unimplemented, - sendFile: .unimplemented + registerReceiveFileCallback: .unimplemented, + startFileTransfer: .unimplemented, + sendFile: .unimplemented, + receiveFile: .unimplemented ) } diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift index 0bd1212cebf2bf1d373d1733d2506e1c52b3d098..a1ab1517817248c93ce78bb435ceb621d5449fa6 100644 --- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift +++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift @@ -10,6 +10,7 @@ public struct MessengerEnvironment { public var downloadNDF: DownloadAndVerifySignedNdf public var e2e: Stored<E2E?> public var fileManager: MessengerFileManager + public var fileTransfer: Stored<FileTransfer?> public var generateSecret: GenerateSecret public var getCMixParams: GetCMixParams public var getE2EFileTransferParams: GetE2EFileTransferParams @@ -32,6 +33,7 @@ public struct MessengerEnvironment { public var newOrLoadUd: NewOrLoadUd public var newUdManagerFromBackup: NewUdManagerFromBackup public var passwordStorage: PasswordStorage + public var receiveFileCallbacks: ReceiveFileCallbacksRegistry public var registerForNotifications: RegisterForNotifications public var registerLogWriter: RegisterLogWriter public var resumeBackup: ResumeBackup @@ -59,6 +61,7 @@ extension MessengerEnvironment { downloadNDF: .live, e2e: .inMemory(), fileManager: .live(), + fileTransfer: .inMemory(), generateSecret: .live, getCMixParams: .liveDefault, getE2EFileTransferParams: .liveDefault, @@ -81,6 +84,7 @@ extension MessengerEnvironment { newOrLoadUd: .live, newUdManagerFromBackup: .live, passwordStorage: .keychain, + receiveFileCallbacks: .live(), registerForNotifications: .live, registerLogWriter: .live, resumeBackup: .live, @@ -103,6 +107,7 @@ extension MessengerEnvironment { downloadNDF: .unimplemented, e2e: .unimplemented(), fileManager: .unimplemented, + fileTransfer: .unimplemented(), generateSecret: .unimplemented, getCMixParams: .unimplemented, getE2EFileTransferParams: .unimplemented, @@ -125,6 +130,7 @@ extension MessengerEnvironment { newOrLoadUd: .unimplemented, newUdManagerFromBackup: .unimplemented, passwordStorage: .unimplemented, + receiveFileCallbacks: .unimplemented, registerForNotifications: .unimplemented, registerLogWriter: .unimplemented, resumeBackup: .unimplemented, diff --git a/Sources/XXMessengerClient/Utils/ReceiveFileCallbacksRegistry.swift b/Sources/XXMessengerClient/Utils/ReceiveFileCallbacksRegistry.swift new file mode 100644 index 0000000000000000000000000000000000000000..e82670ca748e4d28469d3b5c66b5eae0f77e4903 --- /dev/null +++ b/Sources/XXMessengerClient/Utils/ReceiveFileCallbacksRegistry.swift @@ -0,0 +1,36 @@ +import Foundation +import XCTestDynamicOverlay +import XXClient + +public struct ReceiveFileCallbacksRegistry { + public var register: (ReceiveFileCallback) -> Cancellable + public var registered: () -> ReceiveFileCallback +} + +extension ReceiveFileCallbacksRegistry { + public static func live() -> ReceiveFileCallbacksRegistry { + class Registry { + var callbacks: [UUID: ReceiveFileCallback] = [:] + } + let registry = Registry() + return ReceiveFileCallbacksRegistry( + register: { callback in + let id = UUID() + registry.callbacks[id] = callback + return Cancellable { registry.callbacks[id] = nil } + }, + registered: { + ReceiveFileCallback { result in + registry.callbacks.values.forEach { $0.handle(result) } + } + } + ) + } +} + +extension ReceiveFileCallbacksRegistry { + public static let unimplemented = ReceiveFileCallbacksRegistry( + register: XCTUnimplemented("\(Self.self).register", placeholder: Cancellable {}), + registered: XCTUnimplemented("\(Self.self).registered", placeholder: .unimplemented) + ) +} diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerReceiveFileTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerReceiveFileTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..554b7f309114fbdaa2ea2679d290d09f238eaa52 --- /dev/null +++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerReceiveFileTests.swift @@ -0,0 +1,223 @@ +import CustomDump +import XCTest +import XXClient +@testable import XXMessengerClient + +final class MessengerReceiveFileTests: XCTestCase { + func testReceiveFile() throws { + let params: MessengerReceiveFile.Params = .stub + let receivedData = "received".data(using: .utf8)! + + var didRegisterReceivedProgressCallbackWithTransferId: [Data] = [] + var didRegisterReceivedProgressCallbackWithPeriod: [Int] = [] + var didRegisterReceivedProgressCallbackWithCallback: [FileTransferProgressCallback] = [] + var didReceiveTransferId: [Data] = [] + var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = [] + + var env: MessengerEnvironment = .unimplemented + env.fileTransfer.get = { + var fileTransfer: FileTransfer = .unimplemented + fileTransfer.registerReceivedProgressCallback.run = { transferId, period, callback in + didRegisterReceivedProgressCallbackWithTransferId.append(transferId) + didRegisterReceivedProgressCallbackWithPeriod.append(period) + didRegisterReceivedProgressCallbackWithCallback.append(callback) + } + fileTransfer.receive.run = { transferId in + didReceiveTransferId.append(transferId) + return receivedData + } + return fileTransfer + } + let receiveFile: MessengerReceiveFile = .live(env) + + try receiveFile(params) { info in + didReceiveCallback.append(info) + } + + XCTAssertNoDifference(didRegisterReceivedProgressCallbackWithTransferId, [ + params.transferId + ]) + XCTAssertNoDifference(didRegisterReceivedProgressCallbackWithPeriod, [ + params.callbackIntervalMS + ]) + XCTAssertNoDifference(didReceiveCallback, []) + + didReceiveCallback = [] + didRegisterReceivedProgressCallbackWithCallback.first?.handle(.success( + FileTransferProgressCallback.Callback( + progress: Progress( + completed: false, + transmitted: 1, + total: 3, + error: nil + ), + partTracker: .unimplemented + ) + )) + + XCTAssertNoDifference(didReceiveCallback, [ + .progress(transmitted: 1, total: 3), + ]) + + didReceiveCallback = [] + didRegisterReceivedProgressCallbackWithCallback.first?.handle(.success( + FileTransferProgressCallback.Callback( + progress: Progress( + completed: false, + transmitted: 2, + total: 3, + error: nil + ), + partTracker: .unimplemented + ) + )) + + XCTAssertNoDifference(didReceiveCallback, [ + .progress(transmitted: 2, total: 3), + ]) + + didReceiveCallback = [] + didRegisterReceivedProgressCallbackWithCallback.first?.handle(.success( + FileTransferProgressCallback.Callback( + progress: Progress( + completed: true, + transmitted: 3, + total: 3, + error: nil + ), + partTracker: .unimplemented + ) + )) + + XCTAssertNoDifference(didReceiveTransferId, [ + params.transferId, + ]) + XCTAssertNoDifference(didReceiveCallback, [ + .finished(receivedData), + ]) + } + + func testReceiveFileWhenNotConnected() { + var env: MessengerEnvironment = .unimplemented + env.fileTransfer.get = { nil } + let receiveFile: MessengerReceiveFile = .live(env) + + XCTAssertThrowsError(try receiveFile(.stub) { _ in }) { error in + XCTAssertNoDifference( + error as? MessengerReceiveFile.Error, + MessengerReceiveFile.Error.fileTransferNotStarted + ) + } + } + + func testReceiveFileProgressError() throws { + let error = "Something went wrong..." + + var receivedProgressCallback: FileTransferProgressCallback? + var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = [] + + var env: MessengerEnvironment = .unimplemented + env.fileTransfer.get = { + var fileTransfer: FileTransfer = .unimplemented + fileTransfer.registerReceivedProgressCallback.run = { _, _, callback in + receivedProgressCallback = callback + } + return fileTransfer + } + let receiveFile: MessengerReceiveFile = .live(env) + + try receiveFile(.stub) { info in + didReceiveCallback.append(info) + } + + receivedProgressCallback?.handle(.success( + FileTransferProgressCallback.Callback( + progress: Progress( + completed: false, + transmitted: 1, + total: 3, + error: error + ), + partTracker: .unimplemented + ) + )) + + XCTAssertNoDifference(didReceiveCallback, [ + .failed(.progressError(error)) + ]) + } + + func testReceiveFileCallbackError() throws { + let error = NSError(domain: "test", code: 123) + + var receivedProgressCallback: FileTransferProgressCallback? + var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = [] + + var env: MessengerEnvironment = .unimplemented + env.fileTransfer.get = { + var fileTransfer: FileTransfer = .unimplemented + fileTransfer.registerReceivedProgressCallback.run = { _, _, callback in + receivedProgressCallback = callback + } + return fileTransfer + } + let receiveFile: MessengerReceiveFile = .live(env) + + try receiveFile(.stub) { info in + didReceiveCallback.append(info) + } + + receivedProgressCallback?.handle(.failure(error)) + + XCTAssertNoDifference(didReceiveCallback, [ + .failed(.callbackError(error)) + ]) + } + + func testReceiveFileReceiveError() throws { + let error = NSError(domain: "test", code: 123) + + var receivedProgressCallback: FileTransferProgressCallback? + var didReceiveCallback: [MessengerReceiveFile.CallbackInfo] = [] + + var env: MessengerEnvironment = .unimplemented + env.fileTransfer.get = { + var fileTransfer: FileTransfer = .unimplemented + fileTransfer.registerReceivedProgressCallback.run = { _, _, callback in + receivedProgressCallback = callback + } + fileTransfer.receive.run = { _ in + throw error + } + return fileTransfer + } + let receiveFile: MessengerReceiveFile = .live(env) + + try receiveFile(.stub) { info in + didReceiveCallback.append(info) + } + + receivedProgressCallback?.handle(.success( + FileTransferProgressCallback.Callback( + progress: Progress( + completed: true, + transmitted: 3, + total: 3, + error: nil + ), + partTracker: .unimplemented + ) + )) + + XCTAssertNoDifference(didReceiveCallback, [ + .failed(.receiveError(error)) + ]) + } +} + +private extension MessengerReceiveFile.Params { + static let stub = MessengerReceiveFile.Params( + transferId: "transfer-id".data(using: .utf8)!, + callbackIntervalMS: 123 + ) +} diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerRegisterReceiveFileCallbackTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerRegisterReceiveFileCallbackTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..95a6cabcacb1f53f30bf5c25b26f6843928e9034 --- /dev/null +++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerRegisterReceiveFileCallbackTests.swift @@ -0,0 +1,34 @@ +import CustomDump +import XCTest +import XXClient +@testable import XXMessengerClient + +final class MessengerRegisterReceiveFileCallbackTests: XCTestCase { + func testRegisterCallback() { + var registeredCallbacks: [ReceiveFileCallback] = [] + var didHandleResult: [ReceiveFileCallback.Result] = [] + var didCancelRegisteredCallback = 0 + + var env: MessengerEnvironment = .unimplemented + env.receiveFileCallbacks.register = { callback in + registeredCallbacks.append(callback) + return Cancellable { didCancelRegisteredCallback += 1 } + } + let registerCallback: MessengerRegisterReceiveFileCallback = .live(env) + let cancellable = registerCallback(ReceiveFileCallback { result in + didHandleResult.append(result) + }) + + XCTAssertEqual(registeredCallbacks.count, 1) + + registeredCallbacks.forEach { callback in + callback.handle(.success(.stub(1))) + } + + XCTAssertNoDifference(didHandleResult, [.success(.stub(1))]) + + cancellable.cancel() + + XCTAssertEqual(didCancelRegisteredCallback, 1) + } +} diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendFileTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendFileTests.swift index 9adc15b1d89fe2f19445629fb1381b5520661233..4e1c1763b489e66206ca18668bd673d8d75c5570 100644 --- a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendFileTests.swift +++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendFileTests.swift @@ -5,28 +5,15 @@ import XXClient final class MessengerSendFileTests: XCTestCase { func testSendFile() throws { - let e2eId = 123 - let e2eFileTransferParams = "e2eFileTransferParams".data(using: .utf8)! - let fileTransferParams = "fileTransferParams".data(using: .utf8)! let newTransferId = "transferId".data(using: .utf8)! - var didInitFileTransfer: [InitFileTransfer.Params] = [] var didSendFile: [FileTransferSend.Params] = [] var didCloseSend: [Data] = [] var didReceiveCallback: [MessengerSendFile.CallbackInfo] = [] - var fileTransferProgressCallback: FileTransferProgressCallback! var env: MessengerEnvironment = .unimplemented - env.e2e.get = { - var e2e: E2E = .unimplemented - e2e.getId.run = { e2eId } - return e2e - } - env.getE2EFileTransferParams.run = { e2eFileTransferParams } - env.getFileTransferParams.run = { fileTransferParams } - env.initFileTransfer.run = { params, callback in - didInitFileTransfer.append(params) + env.fileTransfer.get = { var fileTransfer: FileTransfer = .unimplemented fileTransfer.send.run = { params, callback in didSendFile.append(params) @@ -47,13 +34,6 @@ final class MessengerSendFileTests: XCTestCase { } XCTAssertNoDifference(transferId, newTransferId) - XCTAssertNoDifference(didInitFileTransfer, [ - .init( - e2eId: e2eId, - e2eFileTransferParamsJSON: e2eFileTransferParams, - fileTransferParamsJSON: fileTransferParams - ) - ]) XCTAssertNoDifference(didSendFile, [ .init( payload: params.file, @@ -99,15 +79,15 @@ final class MessengerSendFileTests: XCTestCase { XCTAssertNoDifference(didCloseSend, [transferId]) } - func testSendFileWhenNotConnected() { + func testSendFileWhenNotStarted() { var env: MessengerEnvironment = .unimplemented - env.e2e.get = { nil } + env.fileTransfer.get = { nil } let sendFile: MessengerSendFile = .live(env) XCTAssertThrowsError(try sendFile(.stub) { _ in }) { error in XCTAssertNoDifference( error as? MessengerSendFile.Error, - MessengerSendFile.Error.notConnected + MessengerSendFile.Error.fileTransferNotStarted ) } } @@ -118,14 +98,7 @@ final class MessengerSendFileTests: XCTestCase { var fileTransferProgressCallback: FileTransferProgressCallback! var env: MessengerEnvironment = .unimplemented - env.e2e.get = { - var e2e: E2E = .unimplemented - e2e.getId.run = { 123 } - return e2e - } - env.getE2EFileTransferParams.run = { Data() } - env.getFileTransferParams.run = { Data() } - env.initFileTransfer.run = { params, callback in + env.fileTransfer.get = { var fileTransfer: FileTransfer = .unimplemented fileTransfer.send.run = { _, callback in fileTransferProgressCallback = callback @@ -157,14 +130,7 @@ final class MessengerSendFileTests: XCTestCase { var fileTransferProgressCallback: FileTransferProgressCallback! var env: MessengerEnvironment = .unimplemented - env.e2e.get = { - var e2e: E2E = .unimplemented - e2e.getId.run = { 123 } - return e2e - } - env.getE2EFileTransferParams.run = { Data() } - env.getFileTransferParams.run = { Data() } - env.initFileTransfer.run = { params, callback in + env.fileTransfer.get = { var fileTransfer: FileTransfer = .unimplemented fileTransfer.send.run = { _, callback in fileTransferProgressCallback = callback @@ -205,14 +171,7 @@ final class MessengerSendFileTests: XCTestCase { var fileTransferProgressCallback: FileTransferProgressCallback! var env: MessengerEnvironment = .unimplemented - env.e2e.get = { - var e2e: E2E = .unimplemented - e2e.getId.run = { 123 } - return e2e - } - env.getE2EFileTransferParams.run = { Data() } - env.getFileTransferParams.run = { Data() } - env.initFileTransfer.run = { params, callback in + env.fileTransfer.get = { var fileTransfer: FileTransfer = .unimplemented fileTransfer.send.run = { _, callback in fileTransferProgressCallback = callback diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartFileTransferTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartFileTransferTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..8e6d352880f7c2bbd27700f79fa0cef54c120cf1 --- /dev/null +++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartFileTransferTests.swift @@ -0,0 +1,80 @@ +import CustomDump +import XCTest +import XXClient +@testable import XXMessengerClient + +final class MessengerStartFileTransferTests: XCTestCase { + func testStart() throws { + let e2eId = 123 + let e2eFileTransferParams = "e2eFileTransferParams".data(using: .utf8)! + let fileTransferParams = "fileTransferParams".data(using: .utf8)! + + var didInitFileTransfer: [InitFileTransfer.Params] = [] + var receiveFileCallback: ReceiveFileCallback? + var didSetFileTransfer: [FileTransfer?] = [] + var didReceiveFile: [ReceiveFileCallback.Result] = [] + + var env: MessengerEnvironment = .unimplemented + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getId.run = { e2eId } + return e2e + } + env.getE2EFileTransferParams.run = { + e2eFileTransferParams + } + env.getFileTransferParams.run = { + fileTransferParams + } + env.initFileTransfer.run = { params, callback in + didInitFileTransfer.append(params) + receiveFileCallback = callback + return .unimplemented + } + env.fileTransfer.set = { + didSetFileTransfer.append($0) + } + env.receiveFileCallbacks.registered = { + ReceiveFileCallback { result in + didReceiveFile.append(result) + } + } + + let start: MessengerStartFileTransfer = .live(env) + + try start() + + XCTAssertNoDifference(didInitFileTransfer, [.init( + e2eId: e2eId, + e2eFileTransferParamsJSON: e2eFileTransferParams, + fileTransferParamsJSON: fileTransferParams + )]) + XCTAssertNotNil(receiveFileCallback) + XCTAssertNoDifference(didSetFileTransfer.map { $0 != nil }, [true]) + + let error = NSError(domain: "test", code: 7) + receiveFileCallback?.handle(.success(.stub(1))) + receiveFileCallback?.handle(.failure(error)) + receiveFileCallback?.handle(.success(.stub(2))) + + XCTAssertNoDifference(didReceiveFile, [ + .success(.stub(1)), + .failure(error), + .success(.stub(2)), + ]) + } + + func testStartWhenNotConnected() { + var env: MessengerEnvironment = .unimplemented + env.e2e.get = { nil } + let start: MessengerStartFileTransfer = .live(env) + + XCTAssertThrowsError(try start()) { error in + XCTAssertNoDifference( + error as NSError, + MessengerStartFileTransfer.Error.notConnected as NSError + ) + } + } +} + diff --git a/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift b/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift index c9a90bde352a9da4ac0c359c26fed8e1a89d9f8f..93110b2efb3698aec563f2f6cc9a0e51f03189a9 100644 --- a/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift +++ b/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift @@ -23,3 +23,16 @@ extension BackupParams { username: "stub-username" ) } + +extension ReceivedFile { + static func stub(_ id: Int) -> ReceivedFile { + ReceivedFile( + transferId: "transfer-id-\(id)".data(using: .utf8)!, + senderId: "sender-id-\(id)".data(using: .utf8)!, + preview: "preview-\(id)".data(using: .utf8)!, + name: "name-\(id)", + type: "type-\(id)", + size: id + ) + } +} diff --git a/Tests/XXMessengerClientTests/Utils/ReceiveFileCallbacksRegistryTests.swift b/Tests/XXMessengerClientTests/Utils/ReceiveFileCallbacksRegistryTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..87733caac8992f8eebfb8ee52497eb79437745be --- /dev/null +++ b/Tests/XXMessengerClientTests/Utils/ReceiveFileCallbacksRegistryTests.swift @@ -0,0 +1,44 @@ +import CustomDump +import XCTest +import XXClient +@testable import XXMessengerClient + +final class ReceiveFileCallbacksRegistryTests: XCTestCase { + func testRegistry() { + var firstCallbackDidHandle: [ReceiveFileCallback.Result] = [] + var secondCallbackDidHandle: [ReceiveFileCallback.Result] = [] + + let firstCallback = ReceiveFileCallback { result in + firstCallbackDidHandle.append(result) + } + let secondCallback = ReceiveFileCallback { result in + secondCallbackDidHandle.append(result) + } + let callbackRegistry: ReceiveFileCallbacksRegistry = .live() + let registeredCallbacks = callbackRegistry.registered() + let firstCallbackCancellable = callbackRegistry.register(firstCallback) + let secondCallbackCancellable = callbackRegistry.register(secondCallback) + + let firstResult = ReceiveFileCallback.Result.success(.stub(1)) + registeredCallbacks.handle(firstResult) + + XCTAssertNoDifference(firstCallbackDidHandle, [firstResult]) + XCTAssertNoDifference(secondCallbackDidHandle, [firstResult]) + + firstCallbackCancellable.cancel() + let secondError = NSError(domain: "test", code: 321) + let secondResult = ReceiveFileCallback.Result.failure(secondError) + registeredCallbacks.handle(secondResult) + + XCTAssertNoDifference(firstCallbackDidHandle, [firstResult]) + XCTAssertNoDifference(secondCallbackDidHandle, [firstResult, secondResult]) + + secondCallbackCancellable.cancel() + + let thirdData = ReceiveFileCallback.Result.success(.stub(2)) + registeredCallbacks.handle(thirdData) + + XCTAssertNoDifference(firstCallbackDidHandle, [firstResult]) + XCTAssertNoDifference(secondCallbackDidHandle, [firstResult, secondResult]) + } +}