From 8098596c746f71ab63d49cc636a34d9a368ad223 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Tue, 6 Sep 2022 14:55:39 +0200 Subject: [PATCH] Add MessengerSearchUsers function --- .../Functions/MessengerSearchUsers.swift | 93 ++++++++ .../Messenger/Messenger.swift | 7 +- .../Messenger/MessengerEnvironment.swift | 6 + .../Functions/MessengerSearchUsersTests.swift | 201 ++++++++++++++++++ 4 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift new file mode 100644 index 00000000..a24a7652 --- /dev/null +++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerSearchUsers.swift @@ -0,0 +1,93 @@ +import Foundation +import XCTestDynamicOverlay +import XXClient + +public struct MessengerSearchUsers { + public struct Query: Equatable { + public init( + username: String? = nil, + email: String? = nil, + phone: String? = nil + ) { + self.username = username + self.email = email + self.phone = phone + } + + public var username: String? + public var email: String? + public var phone: String? + } + + public enum Error: Swift.Error, Equatable { + case notConnected + case notLoggedIn + } + + public var run: (Query) throws -> [Contact] + + public func callAsFunction(query: Query) throws -> [Contact] { + try run(query) + } +} + +extension MessengerSearchUsers { + public static func live(_ env: MessengerEnvironment) -> MessengerSearchUsers { + MessengerSearchUsers { query in + guard let e2e = env.e2e() else { + throw Error.notConnected + } + guard let ud = env.ud() else { + throw Error.notLoggedIn + } + var result: Result<[Contact], Swift.Error>! + let semaphore = DispatchSemaphore(value: 0) + _ = try env.searchUD( + e2eId: e2e.getId(), + udContact: try ud.getContact(), + facts: query.facts, + singleRequestParamsJSON: env.getSingleUseParams(), + callback: .init { searchResult in + switch searchResult { + case .success(let contacts): + result = .success(contacts) + case .failure(let error): + result = .failure(error) + } + semaphore.signal() + } + ) + semaphore.wait() + return try result.get() + } + } +} + +extension MessengerSearchUsers.Query { + public var isEmpty: Bool { + [username, email, phone] + .compactMap { $0 } + .map { $0.isEmpty == false } + .contains(where: { $0 == true }) == false + } + + var facts: [Fact] { + var facts: [Fact] = [] + if let username = username, username.isEmpty == false { + facts.append(Fact(fact: username, type: 0)) + } + if let email = email, email.isEmpty == false { + facts.append(Fact(fact: email, type: 1)) + } + if let phone = phone, phone.isEmpty == false { + facts.append(Fact(fact: phone, type: 2)) + } + return facts + } +} + +extension MessengerSearchUsers { + public static let unimplemented = MessengerSearchUsers( + run: XCTUnimplemented("\(Self.self)") + ) +} diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift index 652eda39..3fd872c6 100644 --- a/Sources/XXMessengerClient/Messenger/Messenger.swift +++ b/Sources/XXMessengerClient/Messenger/Messenger.swift @@ -19,6 +19,7 @@ public struct Messenger { public var waitForNetwork: MessengerWaitForNetwork public var waitForNodes: MessengerWaitForNodes public var destroy: MessengerDestroy + public var searchUsers: MessengerSearchUsers } extension Messenger { @@ -41,7 +42,8 @@ extension Messenger { logIn: .live(env), waitForNetwork: .live(env), waitForNodes: .live(env), - destroy: .live(env) + destroy: .live(env), + searchUsers: .live(env) ) } } @@ -65,6 +67,7 @@ extension Messenger { logIn: .unimplemented, waitForNetwork: .unimplemented, waitForNodes: .unimplemented, - destroy: .unimplemented + destroy: .unimplemented, + searchUsers: .unimplemented ) } diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift index ad94b5ce..f846117f 100644 --- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift +++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift @@ -11,6 +11,7 @@ public struct MessengerEnvironment { public var generateSecret: GenerateSecret public var getCMixParams: GetCMixParams public var getE2EParams: GetE2EParams + public var getSingleUseParams: GetSingleUseParams public var isRegisteredWithUD: IsRegisteredWithUD public var loadCMix: LoadCMix public var login: Login @@ -18,6 +19,7 @@ public struct MessengerEnvironment { public var newCMix: NewCMix public var newOrLoadUd: NewOrLoadUd public var passwordStorage: PasswordStorage + public var searchUD: SearchUD public var sleep: (TimeInterval) -> Void public var storageDir: String public var ud: Stored<UserDiscovery?> @@ -43,6 +45,7 @@ extension MessengerEnvironment { generateSecret: .live, getCMixParams: .liveDefault, getE2EParams: .liveDefault, + getSingleUseParams: .liveDefault, isRegisteredWithUD: .live, loadCMix: .live, login: .live, @@ -50,6 +53,7 @@ extension MessengerEnvironment { newCMix: .live, newOrLoadUd: .live, passwordStorage: .keychain, + searchUD: .live, sleep: { Thread.sleep(forTimeInterval: $0) }, storageDir: MessengerEnvironment.defaultStorageDir, ud: .inMemory(), @@ -70,6 +74,7 @@ extension MessengerEnvironment { generateSecret: .unimplemented, getCMixParams: .unimplemented, getE2EParams: .unimplemented, + getSingleUseParams: .unimplemented, isRegisteredWithUD: .unimplemented, loadCMix: .unimplemented, login: .unimplemented, @@ -77,6 +82,7 @@ extension MessengerEnvironment { newCMix: .unimplemented, newOrLoadUd: .unimplemented, passwordStorage: .unimplemented, + searchUD: .unimplemented, sleep: XCTUnimplemented("\(Self.self).sleep"), storageDir: "unimplemented", ud: .unimplemented(), diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift new file mode 100644 index 00000000..b6eaac39 --- /dev/null +++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerSearchUsersTests.swift @@ -0,0 +1,201 @@ +import CustomDump +import XCTest +import XXClient +@testable import XXMessengerClient + +final class MessengerSearchUsersTests: XCTestCase { + func testSearch() throws { + struct SearchUdParams: Equatable { + var e2eId: Int + var udContact: Contact + var facts: [Fact] + var singleRequestParamsJSON: Data + } + + var didSearchUdWithParams: [SearchUdParams] = [] + + var env: MessengerEnvironment = .unimplemented + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getId.run = { 123 } + return e2e + } + env.ud.get = { + var ud: UserDiscovery = .unimplemented + ud.getContact.run = { .unimplemented("ud-contact".data(using: .utf8)!) } + return ud + } + env.getSingleUseParams.run = { "single-use-params".data(using: .utf8)! } + env.searchUD.run = { e2eId, udContact, facts, singleRequestParamsJSON, callback in + didSearchUdWithParams.append(.init( + e2eId: e2eId, + udContact: udContact, + facts: facts, + singleRequestParamsJSON: singleRequestParamsJSON + )) + callback.handle(.success([ + .unimplemented("contact-1".data(using: .utf8)!), + .unimplemented("contact-2".data(using: .utf8)!), + .unimplemented("contact-3".data(using: .utf8)!), + ])) + return SingleUseSendReport(rounds: [], ephId: 0, receptionId: Data()) + } + + let search: MessengerSearchUsers = .live(env) + let query = MessengerSearchUsers.Query( + username: "Username", + email: "Email", + phone: "Phone" + ) + + let contacts = try search(query: query) + + XCTAssertNoDifference(didSearchUdWithParams, [.init( + e2eId: 123, + udContact: .unimplemented("ud-contact".data(using: .utf8)!), + facts: query.facts, + singleRequestParamsJSON: "single-use-params".data(using: .utf8)! + )]) + + XCTAssertNoDifference(contacts, [ + .unimplemented("contact-1".data(using: .utf8)!), + .unimplemented("contact-2".data(using: .utf8)!), + .unimplemented("contact-3".data(using: .utf8)!), + ]) + } + + func testSearchNotConnected() { + var env: MessengerEnvironment = .unimplemented + env.e2e.get = { nil } + let search: MessengerSearchUsers = .live(env) + + XCTAssertThrowsError(try search(query: .init())) { error in + XCTAssertNoDifference(error as? MessengerSearchUsers.Error, .notConnected) + } + } + + func testSearchNotLoggedIn() { + var env: MessengerEnvironment = .unimplemented + env.e2e.get = { .unimplemented } + env.ud.get = { nil } + let search: MessengerSearchUsers = .live(env) + + XCTAssertThrowsError(try search(query: .init())) { error in + XCTAssertNoDifference(error as? MessengerSearchUsers.Error, .notLoggedIn) + } + } + + func testSearchFailure() { + struct Failure: Error, Equatable {} + let error = Failure() + + var env: MessengerEnvironment = .unimplemented + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getId.run = { 0 } + return e2e + } + env.ud.get = { + var ud: UserDiscovery = .unimplemented + ud.getContact.run = { .unimplemented(Data()) } + return ud + } + env.getSingleUseParams.run = { Data() } + env.searchUD.run = { _, _, _, _, _ in throw error } + + let search: MessengerSearchUsers = .live(env) + + XCTAssertThrowsError(try search(query: .init())) { err in + XCTAssertNoDifference(err as? Failure, error) + } + } + + func testSearchCallbackFailure() { + struct Failure: Error, Equatable {} + let error = Failure() + + var env: MessengerEnvironment = .unimplemented + env.e2e.get = { + var e2e: E2E = .unimplemented + e2e.getId.run = { 0 } + return e2e + } + env.ud.get = { + var ud: UserDiscovery = .unimplemented + ud.getContact.run = { .unimplemented(Data()) } + return ud + } + env.getSingleUseParams.run = { Data() } + env.searchUD.run = { _, _, _, _, callback in + callback.handle(.failure(error as NSError)) + return SingleUseSendReport(rounds: [], ephId: 0, receptionId: Data()) + } + + let search: MessengerSearchUsers = .live(env) + + XCTAssertThrowsError(try search(query: .init())) { err in + XCTAssertNoDifference(err as? Failure, error) + } + } + + func testQueryIsEmpty() { + let emptyQueries: [MessengerSearchUsers.Query] = [ + .init(username: nil, email: nil, phone: nil), + .init(username: "", email: nil, phone: nil), + .init(username: nil, email: "", phone: nil), + .init(username: nil, email: nil, phone: ""), + .init(username: "", email: "", phone: ""), + ] + + emptyQueries.forEach { query in + XCTAssertTrue(query.isEmpty, "\(query) should be empty") + } + + let nonEmptyQueries: [MessengerSearchUsers.Query] = [ + .init(username: "test", email: nil, phone: nil), + .init(username: nil, email: "test", phone: nil), + .init(username: nil, email: nil, phone: "test"), + .init(username: "a", email: "b", phone: "c"), + ] + + nonEmptyQueries.forEach { query in + XCTAssertFalse(query.isEmpty, "\(query) should not be empty") + } + } + + func testQueryFacts() { + XCTAssertNoDifference( + MessengerSearchUsers.Query(username: nil, email: nil, phone: nil).facts, + [] + ) + + XCTAssertNoDifference( + MessengerSearchUsers.Query(username: "", email: "", phone: "").facts, + [] + ) + + XCTAssertNoDifference( + MessengerSearchUsers.Query( + username: "username", + email: "email", + phone: "phone" + ).facts, + [ + Fact(fact: "username", type: 0), + Fact(fact: "email", type: 1), + Fact(fact: "phone", type: 2), + ] + ) + + XCTAssertNoDifference( + MessengerSearchUsers.Query( + username: "username", + email: "", + phone: nil + ).facts, + [ + Fact(fact: "username", type: 0), + ] + ) + } +} -- GitLab