From 0758b84603c96fbf9bf138979b7178f06062b588 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Wed, 19 Oct 2022 12:38:01 +0200 Subject: [PATCH] Implement ReceiveFileHandler --- .../ReceiveFileHandler.swift | 98 +++++++- .../AppFeature/AppEnvironment+Live.swift | 4 +- .../ReceiveFileHandlerTests.swift | 214 ++++++++++++++++++ 3 files changed, 311 insertions(+), 5 deletions(-) create mode 100644 Examples/xx-messenger/Tests/AppCoreTests/ReceiveFileHandler/ReceiveFileHandlerTests.swift diff --git a/Examples/xx-messenger/Sources/AppCore/ReceiveFileHandler/ReceiveFileHandler.swift b/Examples/xx-messenger/Sources/AppCore/ReceiveFileHandler/ReceiveFileHandler.swift index 92c266da..84abe3c5 100644 --- a/Examples/xx-messenger/Sources/AppCore/ReceiveFileHandler/ReceiveFileHandler.swift +++ b/Examples/xx-messenger/Sources/AppCore/ReceiveFileHandler/ReceiveFileHandler.swift @@ -1,8 +1,18 @@ +import Foundation import XCTestDynamicOverlay import XXClient import XXMessengerClient +import XXModels public struct ReceiveFileHandler { + public struct ProgressError: Error { + public init(message: String) { + self.message = message + } + + public var message: String + } + public typealias OnError = (Error) -> Void public var run: (@escaping OnError) -> Cancellable @@ -14,14 +24,94 @@ public struct ReceiveFileHandler { extension ReceiveFileHandler { public static func live( - messenger: Messenger + messenger: Messenger, + db: DBManagerGetDB, + now: @escaping () -> Date ) -> ReceiveFileHandler { ReceiveFileHandler { onError in - messenger.registerReceiveFileCallback(.init { result in + func receiveFile(_ file: ReceivedFile) { + do { + let date = now() + try db().saveFileTransfer(XXModels.FileTransfer( + id: file.transferId, + contactId: file.senderId, + name: file.name, + type: file.type, + data: nil, + progress: 0, + isIncoming: true, + createdAt: date + )) + try db().saveMessage(XXModels.Message( + senderId: file.senderId, + recipientId: try messenger.e2e.tryGet().getContact().getId(), + groupId: nil, + date: date, + status: .received, + isUnread: false, + text: "", + fileTransferId: file.transferId + )) + try messenger.receiveFile(.init( + transferId: file.transferId, + callbackIntervalMS: 500 + )) { info in + switch info { + case .progress(let transmitted, let total): + updateProgress( + transferId: file.transferId, + transmitted: transmitted, + total: total + ) + + case .finished(let data): + saveData( + transferId: file.transferId, + data: data + ) + + case .failed(.receiveError(let error)): + onError(error) + + case .failed(.callbackError(let error)): + onError(error) + + case .failed(.progressError(let message)): + onError(ProgressError(message: message)) + } + } + } catch { + onError(error) + } + } + + func updateProgress(transferId: Data, transmitted: Int, total: Int) { + do { + if var transfer = try db().fetchFileTransfers(.init(id: [transferId])).first { + transfer.progress = total > 0 ? Float(transmitted) / Float(total) : 0 + try db().saveFileTransfer(transfer) + } + } catch { + onError(error) + } + } + + func saveData(transferId: Data, data: Data) { + do { + if var transfer = try db().fetchFileTransfers(.init(id: [transferId])).first { + transfer.progress = 1 + transfer.data = data + try db().saveFileTransfer(transfer) + } + } catch { + onError(error) + } + } + + return messenger.registerReceiveFileCallback(.init { result in switch result { case .success(let file): - // TODO: - break + receiveFile(file) case .failure(let error): onError(error) diff --git a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift index 62ea9c13..be768aba 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppEnvironment+Live.swift @@ -114,7 +114,9 @@ extension AppEnvironment { db: dbManager.getDB ), receiveFileHandler: .live( - messenger: messenger + messenger: messenger, + db: dbManager.getDB, + now: Date.init ), backupStorage: backupStorage, log: .live(), diff --git a/Examples/xx-messenger/Tests/AppCoreTests/ReceiveFileHandler/ReceiveFileHandlerTests.swift b/Examples/xx-messenger/Tests/AppCoreTests/ReceiveFileHandler/ReceiveFileHandlerTests.swift new file mode 100644 index 00000000..6d54a9cf --- /dev/null +++ b/Examples/xx-messenger/Tests/AppCoreTests/ReceiveFileHandler/ReceiveFileHandlerTests.swift @@ -0,0 +1,214 @@ +import CustomDump +import XCTest +import XXMessengerClient +import XXClient +import XXModels +@testable import AppCore + +final class ReceiveFileHandlerTests: XCTestCase { + func testReceiveFile() { + let currentDate = Date(timeIntervalSince1970: 123) + let myContactId = "my-contact-id".data(using: .utf8)! + let receivedFile = ReceivedFile.stub() + + var actions: [Action] = [] + var receiveFileCallback: ReceiveFileCallback? + var receivingFileCallback: MessengerReceiveFile.Callback? + + var messenger: Messenger = .unimplemented + messenger.registerReceiveFileCallback.run = { callback in + actions.append(.didRegisterReceiveFileCallback) + receiveFileCallback = callback + return Cancellable { actions.append(.didCancelReceiveFileCallback) } + } + messenger.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getContact.run = { + var contact: XXClient.Contact = .unimplemented(Data()) + contact.getIdFromContact.run = { _ in myContactId } + return contact + } + return e2e + } + messenger.receiveFile.run = { params, callback in + actions.append(.didReceiveFile(params)) + receivingFileCallback = callback + } + + var db: DBManagerGetDB = .unimplemented + db.run = { + var db: Database = .unimplemented + db.saveFileTransfer.run = { model in + actions.append(.didSaveFileTransfer(model)) + return model + } + db.saveMessage.run = { model in + actions.append(.didSaveMessage(model)) + return model + } + db.fetchFileTransfers.run = { query in + actions.append(.didFetchFileTransfers(query)) + return [ + FileTransfer( + id: receivedFile.transferId, + contactId: receivedFile.senderId, + name: receivedFile.name, + type: receivedFile.type, + data: nil, + progress: 0, + isIncoming: true, + createdAt: currentDate + ) + ] + } + return db + } + + let handler = ReceiveFileHandler.live( + messenger: messenger, + db: db, + now: { currentDate } + ) + + XCTAssertNoDifference(actions, []) + + actions = [] + let cancellable = handler(onError: { error in + actions.append(.didCatchError(error as NSError)) + }) + + XCTAssertNoDifference(actions, [ + .didRegisterReceiveFileCallback + ]) + + actions = [] + let error = NSError(domain: "receive-file", code: 1) + receiveFileCallback?.handle(.failure(error)) + + XCTAssertNoDifference(actions, [ + .didCatchError(error) + ]) + + actions = [] + receiveFileCallback?.handle(.success(receivedFile)) + + XCTAssertNoDifference(actions, [ + .didSaveFileTransfer(FileTransfer( + id: receivedFile.transferId, + contactId: receivedFile.senderId, + name: receivedFile.name, + type: receivedFile.type, + data: nil, + progress: 0, + isIncoming: true, + createdAt: currentDate + )), + .didSaveMessage(Message( + networkId: nil, + senderId: receivedFile.senderId, + recipientId: myContactId, + groupId: nil, + date: currentDate, + status: .received, + isUnread: false, + text: "", + replyMessageId: nil, + roundURL: nil, + fileTransferId: receivedFile.transferId + )), + .didReceiveFile(MessengerReceiveFile.Params( + transferId: receivedFile.transferId, + callbackIntervalMS: 500 + )), + ]) + + actions = [] + let receivingFileError = NSError(domain: "receiving-file", code: 2) + receivingFileCallback?(.failed(.receiveError(receivingFileError))) + + XCTAssertNoDifference(actions, [ + .didCatchError(receivingFileError) + ]) + + actions = [] + let receivingFileCallbackError = NSError(domain: "receiving-file-callback", code: 3) + receivingFileCallback?(.failed(.callbackError(receivingFileCallbackError))) + + XCTAssertNoDifference(actions, [ + .didCatchError(receivingFileCallbackError) + ]) + + actions = [] + let receivingFileProgressError = "receiving-file-progress" + receivingFileCallback?(.failed(.progressError(receivingFileProgressError))) + + XCTAssertNoDifference(actions, [ + .didCatchError(ReceiveFileHandler.ProgressError(message: receivingFileProgressError) as NSError) + ]) + + actions = [] + receivingFileCallback?(.progress(transmitted: 1, total: 2)) + + XCTAssertNoDifference(actions, [ + .didFetchFileTransfers(.init(id: [receivedFile.transferId])), + .didSaveFileTransfer(FileTransfer( + id: receivedFile.transferId, + contactId: receivedFile.senderId, + name: receivedFile.name, + type: receivedFile.type, + data: nil, + progress: 0.5, + isIncoming: true, + createdAt: currentDate + )), + ]) + + actions = [] + let fileData = "file-data".data(using: .utf8)! + receivingFileCallback?(.finished(fileData)) + + XCTAssertNoDifference(actions, [ + .didFetchFileTransfers(.init(id: [receivedFile.transferId])), + .didSaveFileTransfer(FileTransfer( + id: receivedFile.transferId, + contactId: receivedFile.senderId, + name: receivedFile.name, + type: receivedFile.type, + data: fileData, + progress: 1, + isIncoming: true, + createdAt: currentDate + )), + ]) + + actions = [] + cancellable.cancel() + + XCTAssertNoDifference(actions, [ + .didCancelReceiveFileCallback + ]) + } +} + +private enum Action: Equatable { + case didRegisterReceiveFileCallback + case didCancelReceiveFileCallback + case didCatchError(NSError) + case didSaveFileTransfer(XXModels.FileTransfer) + case didSaveMessage(XXModels.Message) + case didReceiveFile(MessengerReceiveFile.Params) + case didFetchFileTransfers(XXModels.FileTransfer.Query) +} + +private extension ReceivedFile { + static func stub() -> ReceivedFile { + ReceivedFile( + transferId: "received-file-transferId".data(using: .utf8)!, + senderId: "received-file-senderId".data(using: .utf8)!, + preview: "received-file-preview".data(using: .utf8)!, + name: "received-file-name", + type: "received-file-type", + size: 1234 + ) + } +} -- GitLab