From dfb51c9fac4f15e85dd61d069246cb0b838e0a5a Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Tue, 13 Sep 2022 12:22:49 +0200 Subject: [PATCH] Add MessengerSendMessage function --- .../Functions/MessengerSendMessage.swift | 85 +++++++ .../Messenger/Messenger.swift | 7 +- .../Functions/MessengerSendMessageTests.swift | 233 ++++++++++++++++++ 3 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerSendMessage.swift create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendMessageTests.swift diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerSendMessage.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerSendMessage.swift new file mode 100644 index 00000000..118ca046 --- /dev/null +++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerSendMessage.swift @@ -0,0 +1,85 @@ +import Foundation +import XCTestDynamicOverlay +import XXClient + +public struct MessengerSendMessage { + public struct DeliveryReport: Equatable { + public enum Result: Equatable { + case delivered + case notDelivered(timedOut: Bool) + case failure(NSError) + } + + public init( + report: E2ESendReport, + result: Result + ) { + self.report = report + self.result = result + } + + public var report: E2ESendReport + public var result: Result + } + + public typealias DeliveryCallback = (DeliveryReport) -> Void + + public enum Error: Swift.Error, Equatable { + case notLoaded + case notConnected + } + + public var run: (Data, Data, DeliveryCallback?) throws -> E2ESendReport + + public func callAsFunction( + recipientId: Data, + payload: Data, + deliveryCallback: DeliveryCallback? + ) throws -> E2ESendReport { + try run(recipientId, payload, deliveryCallback) + } +} + +extension MessengerSendMessage { + public static func live(_ env: MessengerEnvironment) -> MessengerSendMessage { + MessengerSendMessage { recipientId, payload, deliveryCallback in + guard let cMix = env.cMix() else { + throw Error.notLoaded + } + guard let e2e = env.e2e() else { + throw Error.notConnected + } + let report = try e2e.send( + messageType: 2, + recipientId: recipientId, + payload: payload, + e2eParams: env.getE2EParams() + ) + if let deliveryCallback = deliveryCallback { + do { + try cMix.waitForRoundResult( + roundList: try report.encode(), + timeoutMS: 30_000, + callback: .init { result in + switch result { + case .delivered(_): + deliveryCallback(.init(report: report, result: .delivered)) + case .notDelivered(let timedOut): + deliveryCallback(.init(report: report, result: .notDelivered(timedOut: timedOut))) + } + } + ) + } catch { + deliveryCallback(.init(report: report, result: .failure(error as NSError))) + } + } + return report + } + } +} + +extension MessengerSendMessage { + public static let unimplemented = MessengerSendMessage( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift index 8e50a4ae..4995f9a1 100644 --- a/Sources/XXMessengerClient/Messenger/Messenger.swift +++ b/Sources/XXMessengerClient/Messenger/Messenger.swift @@ -24,6 +24,7 @@ public struct Messenger { public var searchUsers: MessengerSearchUsers public var registerForNotifications: MessengerRegisterForNotifications public var verifyContact: MessengerVerifyContact + public var sendMessage: MessengerSendMessage } extension Messenger { @@ -51,7 +52,8 @@ extension Messenger { destroy: .live(env), searchUsers: .live(env), registerForNotifications: .live(env), - verifyContact: .live(env) + verifyContact: .live(env), + sendMessage: .live(env) ) } } @@ -80,6 +82,7 @@ extension Messenger { destroy: .unimplemented, searchUsers: .unimplemented, registerForNotifications: .unimplemented, - verifyContact: .unimplemented + verifyContact: .unimplemented, + sendMessage: .unimplemented ) } diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendMessageTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendMessageTests.swift new file mode 100644 index 00000000..c429c742 --- /dev/null +++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSendMessageTests.swift @@ -0,0 +1,233 @@ +import CustomDump +import XCTest +import XXClient +@testable import XXMessengerClient + +final class MessengerSendMessageTests: XCTestCase { + func testSend() throws { + struct E2ESendParams: Equatable { + var messageType: Int + var recipientId: Data + var payload: Data + var e2eParams: Data + } + var e2eDidSendWithParams: [E2ESendParams] = [] + + let e2eSendReport = E2ESendReport( + rounds: [1, 2, 3], + roundURL: "round-url", + messageId: "message-id".data(using: .utf8)!, + timestamp: 123, + keyResidue: "key-residue".data(using: .utf8)! + ) + + var env: MessengerEnvironment = .unimplemented + env.getE2EParams.run = { "e2e-params".data(using: .utf8)! } + env.cMix.get = { .unimplemented } + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.send.run = { messageType, recipientId, payload, e2eParams in + e2eDidSendWithParams.append(.init( + messageType: messageType, + recipientId: recipientId, + payload: payload, + e2eParams: e2eParams + )) + return e2eSendReport + } + return e2e + } + let send: MessengerSendMessage = .live(env) + + let report = try send( + recipientId: "recipient-id".data(using: .utf8)!, + payload: "payload".data(using: .utf8)!, + deliveryCallback: nil + ) + + XCTAssertNoDifference(e2eDidSendWithParams, [.init( + messageType: 2, + recipientId: "recipient-id".data(using: .utf8)!, + payload: "payload".data(using: .utf8)!, + e2eParams: "e2e-params".data(using: .utf8)! + )]) + + XCTAssertNoDifference(report, e2eSendReport) + } + + func testSendWithDeliveryCallback() throws { + struct WaitForRoundResultsParams: Equatable { + var roundList: Data + var timeoutMS: Int + } + var didWaitForRoundResultsWithParams: [WaitForRoundResultsParams] = [] + var didWaitForRoundResultsWithCallback: [MessageDeliveryCallback] = [] + var didReceiveDeliveryReport: [MessengerSendMessage.DeliveryReport] = [] + + let e2eSendReport = E2ESendReport( + rounds: [1, 2, 3], + roundURL: "round-url", + messageId: "message-id".data(using: .utf8)!, + timestamp: 123, + keyResidue: "key-residue".data(using: .utf8)! + ) + + var env: MessengerEnvironment = .unimplemented + env.getE2EParams.run = { "e2e-params".data(using: .utf8)! } + env.cMix.get = { + var cMix: CMix = .unimplemented + cMix.waitForRoundResult.run = { roundList, timeoutMS, callback in + didWaitForRoundResultsWithParams.append(.init(roundList: roundList, timeoutMS: timeoutMS)) + didWaitForRoundResultsWithCallback.append(callback) + } + return cMix + } + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.send.run = { _, _, _, _ in e2eSendReport } + return e2e + } + let send: MessengerSendMessage = .live(env) + + let report = try send( + recipientId: "recipient-id".data(using: .utf8)!, + payload: "payload".data(using: .utf8)!, + deliveryCallback: .init { deliveryReport in + didReceiveDeliveryReport.append(deliveryReport) + } + ) + + XCTAssertNoDifference(report, e2eSendReport) + + XCTAssertNoDifference(didWaitForRoundResultsWithParams, [ + .init(roundList: try! e2eSendReport.encode(), timeoutMS: 30_000), + ]) + + didWaitForRoundResultsWithCallback.first?.handle(.delivered(roundResults: [1, 2, 3])) + + XCTAssertNoDifference(didReceiveDeliveryReport, [ + .init(report: report, result: .delivered) + ]) + + didReceiveDeliveryReport.removeAll() + didWaitForRoundResultsWithCallback.first?.handle(.notDelivered(timedOut: false)) + + XCTAssertNoDifference(didReceiveDeliveryReport, [ + .init(report: report, result: .notDelivered(timedOut: false)) + ]) + + didReceiveDeliveryReport.removeAll() + didWaitForRoundResultsWithCallback.first?.handle(.notDelivered(timedOut: true)) + + XCTAssertNoDifference(didReceiveDeliveryReport, [ + .init(report: report, result: .notDelivered(timedOut: true)) + ]) + } + + func testSendWhenNotLoaded() { + var env: MessengerEnvironment = .unimplemented + env.cMix.get = { nil } + let send: MessengerSendMessage = .live(env) + + XCTAssertThrowsError( + try send( + recipientId: Data(), + payload: Data(), + deliveryCallback: nil + ) + ) { error in + XCTAssertNoDifference( + error as? MessengerSendMessage.Error, + .notLoaded + ) + } + } + + func testSendWhenNotConnected() { + var env: MessengerEnvironment = .unimplemented + env.cMix.get = { .unimplemented } + env.e2e.get = { nil } + let send: MessengerSendMessage = .live(env) + + XCTAssertThrowsError( + try send( + recipientId: Data(), + payload: Data(), + deliveryCallback: nil + ) + ) { error in + XCTAssertNoDifference( + error as? MessengerSendMessage.Error, + .notConnected + ) + } + } + + func testSendFailure() { + struct Failure: Error, Equatable {} + let error = Failure() + + var env: MessengerEnvironment = .unimplemented + env.getE2EParams.run = { "e2e-params".data(using: .utf8)! } + env.cMix.get = { .unimplemented } + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.send.run = { _, _, _, _ in throw error } + return e2e + } + let send: MessengerSendMessage = .live(env) + + XCTAssertThrowsError( + try send( + recipientId: "recipient-id".data(using: .utf8)!, + payload: "payload".data(using: .utf8)!, + deliveryCallback: nil + ) + ) { err in + XCTAssertNoDifference(err as? Failure, error) + } + } + + func testSendDeliveryFailure() throws { + let e2eSendReport = E2ESendReport( + rounds: [1, 2, 3], + roundURL: "round-url", + messageId: "message-id".data(using: .utf8)!, + timestamp: 123, + keyResidue: "key-residue".data(using: .utf8)! + ) + + struct Failure: Error {} + let error = Failure() + + var didReceiveDeliveryReport: [MessengerSendMessage.DeliveryReport] = [] + + var env: MessengerEnvironment = .unimplemented + env.getE2EParams.run = { "e2e-params".data(using: .utf8)! } + env.cMix.get = { + var cMix: CMix = .unimplemented + cMix.waitForRoundResult.run = { _, _, _ in throw error } + return cMix + } + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.send.run = { _, _, _, _ in e2eSendReport } + return e2e + } + let send: MessengerSendMessage = .live(env) + + let report = try send( + recipientId: "recipient-id".data(using: .utf8)!, + payload: "payload".data(using: .utf8)!, + deliveryCallback: .init { deliveryReport in + didReceiveDeliveryReport.append(deliveryReport) + } + ) + + XCTAssertNoDifference(report, e2eSendReport) + + XCTAssertNoDifference(didReceiveDeliveryReport, [ + .init(report: report, result: .failure(error as NSError)) + ]) + } +} -- GitLab