Skip to content
Snippets Groups Projects
Commit 5b190dca authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Merge branch 'feature/messenger-example-request-handling' into 'development'

Messenger example - auth requests handling

See merge request elixxir/elixxir-dapps-sdk-swift!76
parents 5e0a91e4 a9e3ade0
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!76Messenger example - auth requests handling
Showing
with 710 additions and 6 deletions
......@@ -57,6 +57,7 @@ let package = Package(
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
.product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXDatabase", package: "client-ios-db"),
.product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXModels", package: "client-ios-db"),
],
swiftSettings: swiftSettings
......
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
import XXModels
public struct AuthCallbackHandler {
public typealias OnError = (Error) -> Void
public var run: (@escaping OnError) -> Cancellable
public func callAsFunction(onError: @escaping OnError) -> Cancellable {
run(onError)
}
}
extension AuthCallbackHandler {
public static func live(
messenger: Messenger,
handleRequest: AuthCallbackHandlerRequest,
handleConfirm: AuthCallbackHandlerConfirm,
handleReset: AuthCallbackHandlerReset
) -> AuthCallbackHandler {
AuthCallbackHandler { onError in
messenger.registerAuthCallbacks(.init { callback in
do {
switch callback {
case .request(let contact, _, _, _):
try handleRequest(contact)
case .confirm(let contact, _, _, _):
try handleConfirm(contact)
case .reset(let contact, _, _, _):
try handleReset(contact)
}
} catch {
onError(error)
}
})
}
}
}
extension AuthCallbackHandler {
public static let unimplemented = AuthCallbackHandler(
run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {})
)
}
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXModels
public struct AuthCallbackHandlerConfirm {
public var run: (XXClient.Contact) throws -> Void
public func callAsFunction(_ contact: XXClient.Contact) throws {
try run(contact)
}
}
extension AuthCallbackHandlerConfirm {
public static func live(db: DBManagerGetDB) -> AuthCallbackHandlerConfirm {
AuthCallbackHandlerConfirm { xxContact in
let id = try xxContact.getId()
guard var dbContact = try db().fetchContacts(.init(id: [id])).first else {
return
}
dbContact.authStatus = .friend
dbContact = try db().saveContact(dbContact)
}
}
}
extension AuthCallbackHandlerConfirm {
public static let unimplemented = AuthCallbackHandlerConfirm(
run: XCTUnimplemented("\(Self.self)")
)
}
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
import XXModels
public struct AuthCallbackHandlerRequest {
public var run: (XXClient.Contact) throws -> Void
public func callAsFunction(_ contact: XXClient.Contact) throws {
try run(contact)
}
}
extension AuthCallbackHandlerRequest {
public static func live(
db: DBManagerGetDB,
messenger: Messenger,
now: @escaping () -> Date
) -> AuthCallbackHandlerRequest {
AuthCallbackHandlerRequest { xxContact in
let id = try xxContact.getId()
guard try db().fetchContacts(.init(id: [id])).isEmpty else {
return
}
var dbContact = XXModels.Contact(id: id)
dbContact.marshaled = xxContact.data
dbContact.username = try xxContact.getFact(.username)?.value
dbContact.email = try xxContact.getFact(.email)?.value
dbContact.phone = try xxContact.getFact(.phone)?.value
dbContact.authStatus = .verificationInProgress
dbContact.createdAt = now()
dbContact = try db().saveContact(dbContact)
do {
try messenger.waitForNetwork()
try messenger.waitForNodes()
let verified = try messenger.verifyContact(xxContact)
dbContact.authStatus = verified ? .verified : .verificationFailed
dbContact = try db().saveContact(dbContact)
} catch {
dbContact.authStatus = .verificationFailed
dbContact = try db().saveContact(dbContact)
throw error
}
}
}
}
extension AuthCallbackHandlerRequest {
public static let unimplemented = AuthCallbackHandlerRequest(
run: XCTUnimplemented("\(Self.self)")
)
}
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXModels
public struct AuthCallbackHandlerReset {
public var run: (XXClient.Contact) throws -> Void
public func callAsFunction(_ contact: XXClient.Contact) throws {
try run(contact)
}
}
extension AuthCallbackHandlerReset {
public static func live(db: DBManagerGetDB) -> AuthCallbackHandlerReset {
AuthCallbackHandlerReset { xxContact in
let id = try xxContact.getId()
guard var dbContact = try db().fetchContacts(.init(id: [id])).first else {
return
}
dbContact.authStatus = .stranger
dbContact = try db().saveContact(dbContact)
}
}
}
extension AuthCallbackHandlerReset {
public static let unimplemented = AuthCallbackHandlerReset(
run: XCTUnimplemented("\(Self.self)")
)
}
......@@ -16,6 +16,16 @@ extension AppEnvironment {
let dbManager = DBManager.live()
let messengerEnv = MessengerEnvironment.live()
let messenger = Messenger.live(messengerEnv)
let authHandler = AuthCallbackHandler.live(
messenger: messenger,
handleRequest: .live(
db: dbManager.getDB,
messenger: messenger,
now: Date.init
),
handleConfirm: .live(db: dbManager.getDB),
handleReset: .live(db: dbManager.getDB)
)
let mainQueue = DispatchQueue.main.eraseToAnyScheduler()
let bgQueue = DispatchQueue.global(qos: .background).eraseToAnyScheduler()
......@@ -53,6 +63,7 @@ extension AppEnvironment {
HomeEnvironment(
messenger: messenger,
dbManager: dbManager,
authHandler: authHandler,
mainQueue: mainQueue,
bgQueue: bgQueue,
register: {
......
......@@ -6,12 +6,15 @@ import ContactsFeature
import Foundation
import RegisterFeature
import UserSearchFeature
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
import XXModels
public struct HomeState: Equatable {
public init(
failure: String? = nil,
authFailure: String? = nil,
isNetworkHealthy: Bool? = nil,
networkNodesReport: NodeRegistrationReport? = nil,
isDeletingAccount: Bool = false,
......@@ -21,6 +24,7 @@ public struct HomeState: Equatable {
userSearch: UserSearchState? = nil
) {
self.failure = failure
self.authFailure = authFailure
self.isNetworkHealthy = isNetworkHealthy
self.isDeletingAccount = isDeletingAccount
self.alert = alert
......@@ -30,6 +34,7 @@ public struct HomeState: Equatable {
}
public var failure: String?
public var authFailure: String?
public var isNetworkHealthy: Bool?
public var networkNodesReport: NodeRegistrationReport?
public var isDeletingAccount: Bool
......@@ -47,6 +52,13 @@ public enum HomeAction: Equatable {
case failure(NSError)
}
public enum AuthHandler: Equatable {
case start
case stop
case failure(NSError)
case failureDismissed
}
public enum NetworkMonitor: Equatable {
case start
case stop
......@@ -62,6 +74,7 @@ public enum HomeAction: Equatable {
}
case messenger(Messenger)
case authHandler(AuthHandler)
case networkMonitor(NetworkMonitor)
case deleteAccount(DeleteAccount)
case didDismissAlert
......@@ -79,6 +92,7 @@ public struct HomeEnvironment {
public init(
messenger: Messenger,
dbManager: DBManager,
authHandler: AuthCallbackHandler,
mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue>,
register: @escaping () -> RegisterEnvironment,
......@@ -87,6 +101,7 @@ public struct HomeEnvironment {
) {
self.messenger = messenger
self.dbManager = dbManager
self.authHandler = authHandler
self.mainQueue = mainQueue
self.bgQueue = bgQueue
self.register = register
......@@ -96,6 +111,7 @@ public struct HomeEnvironment {
public var messenger: Messenger
public var dbManager: DBManager
public var authHandler: AuthCallbackHandler
public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue>
public var register: () -> RegisterEnvironment
......@@ -107,6 +123,7 @@ extension HomeEnvironment {
public static let unimplemented = HomeEnvironment(
messenger: .unimplemented,
dbManager: .unimplemented,
authHandler: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented,
register: { .unimplemented },
......@@ -119,10 +136,12 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
{ state, action, env in
enum NetworkHealthEffectId {}
enum NetworkNodesEffectId {}
enum AuthCallbacksEffectId {}
switch action {
case .messenger(.start):
return .merge(
Effect(value: .authHandler(.start)),
Effect(value: .networkMonitor(.stop)),
Effect.result {
do {
......@@ -160,6 +179,29 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
state.failure = error.localizedDescription
return .none
case .authHandler(.start):
return Effect.run { subscriber in
let cancellable = env.authHandler(onError: { error in
subscriber.send(.authHandler(.failure(error as NSError)))
})
return AnyCancellable { cancellable.cancel() }
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
.cancellable(id: AuthCallbacksEffectId.self, cancelInFlight: true)
case .authHandler(.stop):
return .cancel(id: AuthCallbacksEffectId.self)
case .authHandler(.failure(let error)):
state.authFailure = error.localizedDescription
return .none
case .authHandler(.failureDismissed):
state.authFailure = nil
return .none
case .networkMonitor(.start):
return .merge(
Effect.run { subscriber in
......
......@@ -15,12 +15,14 @@ public struct HomeView: View {
struct ViewState: Equatable {
var failure: String?
var authFailure: String?
var isNetworkHealthy: Bool?
var networkNodesReport: NodeRegistrationReport?
var isDeletingAccount: Bool
init(state: HomeState) {
failure = state.failure
authFailure = state.authFailure
isNetworkHealthy = state.isNetworkHealthy
isDeletingAccount = state.isDeletingAccount
networkNodesReport = state.networkNodesReport
......@@ -31,21 +33,32 @@ public struct HomeView: View {
WithViewStore(store, observe: ViewState.init) { viewStore in
NavigationView {
Form {
Section {
if let failure = viewStore.failure {
if let failure = viewStore.failure {
Section {
Text(failure)
Button {
viewStore.send(.messenger(.start))
} label: {
Text("Retry")
}
}
} header: {
if viewStore.failure != nil {
} header: {
Text("Error")
}
}
if let authFailure = viewStore.authFailure {
Section {
Text(authFailure)
Button {
viewStore.send(.authHandler(.failureDismissed))
} label: {
Text("Dismiss")
}
} header: {
Text("Auth Error")
}
}
Section {
HStack {
Text("Health")
......
import CustomDump
import XCTest
import XXModels
import XXClient
@testable import AppCore
final class AuthCallbackHandlerConfirmTests: XCTestCase {
func testConfirm() throws {
var didFetchContacts: [XXModels.Contact.Query] = []
var didSaveContact: [XXModels.Contact] = []
let dbContact = XXModels.Contact(
id: "id".data(using: .utf8)!,
authStatus: .requested
)
let confirm = AuthCallbackHandlerConfirm.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return [dbContact]
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
}
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try confirm(xxContact)
XCTAssertNoDifference(didFetchContacts, [.init(id: ["id".data(using: .utf8)!])])
var expectedSavedContact = dbContact
expectedSavedContact.authStatus = .friend
XCTAssertNoDifference(didSaveContact, [expectedSavedContact])
}
func testConfirmWhenContactNotInDatabase() throws {
let confirm = AuthCallbackHandlerConfirm.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { _ in [] }
return db
}
)
var contact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try confirm(contact)
}
}
import CustomDump
import XCTest
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
import XXModels
@testable import AppCore
final class AuthCallbackHandlerRequestTests: XCTestCase {
func testRequestFromNewContact() throws {
let now = Date()
var didFetchContacts: [XXModels.Contact.Query] = []
var didVerifyContact: [XXClient.Contact] = []
var didSaveContact: [XXModels.Contact] = []
var messenger: Messenger = .unimplemented
messenger.waitForNetwork.run = { _ in }
messenger.waitForNodes.run = { _, _, _, _ in }
messenger.verifyContact.run = { contact in
didVerifyContact.append(contact)
return true
}
let request = AuthCallbackHandlerRequest.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return []
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
},
messenger: messenger,
now: { now }
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
xxContact.getFactsFromContact.run = { _ in
[
Fact(type: .username, value: "username"),
Fact(type: .email, value: "email"),
Fact(type: .phone, value: "phone"),
]
}
try request(xxContact)
XCTAssertNoDifference(didFetchContacts, [.init(id: ["id".data(using: .utf8)!])])
XCTAssertNoDifference(didSaveContact, [
.init(
id: "id".data(using: .utf8)!,
marshaled: "contact".data(using: .utf8)!,
username: "username",
email: "email",
phone: "phone",
authStatus: .verificationInProgress,
createdAt: now
),
.init(
id: "id".data(using: .utf8)!,
marshaled: "contact".data(using: .utf8)!,
username: "username",
email: "email",
phone: "phone",
authStatus: .verified,
createdAt: now
)
])
}
func testRequestWhenContactInDatabase() throws {
let request = AuthCallbackHandlerRequest.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { _ in [.init(id: "id".data(using: .utf8)!)] }
return db
},
messenger: .unimplemented,
now: XCTUnimplemented("now", placeholder: Date())
)
var contact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try request(contact)
}
func testRequestFromNewContactVerificationFalse() throws {
let now = Date()
var didSaveContact: [XXModels.Contact] = []
var messenger: Messenger = .unimplemented
messenger.waitForNetwork.run = { _ in }
messenger.waitForNodes.run = { _, _, _, _ in }
messenger.verifyContact.run = { _ in false }
let request = AuthCallbackHandlerRequest.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in return [] }
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
},
messenger: messenger,
now: { now }
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
xxContact.getFactsFromContact.run = { _ in [] }
try request(xxContact)
XCTAssertNoDifference(didSaveContact, [
.init(
id: "id".data(using: .utf8)!,
marshaled: "contact".data(using: .utf8)!,
authStatus: .verificationInProgress,
createdAt: now
),
.init(
id: "id".data(using: .utf8)!,
marshaled: "contact".data(using: .utf8)!,
authStatus: .verificationFailed,
createdAt: now
)
])
}
func testRequestFromNewContactVerificationFailure() throws {
struct Failure: Error, Equatable {}
let failure = Failure()
let now = Date()
var didSaveContact: [XXModels.Contact] = []
var messenger: Messenger = .unimplemented
messenger.waitForNetwork.run = { _ in }
messenger.waitForNodes.run = { _, _, _, _ in }
messenger.verifyContact.run = { _ in throw failure }
let request = AuthCallbackHandlerRequest.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in return [] }
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
},
messenger: messenger,
now: { now }
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
xxContact.getFactsFromContact.run = { _ in [] }
XCTAssertThrowsError(try request(xxContact)) { error in
XCTAssertNoDifference(error as? Failure, failure)
}
XCTAssertNoDifference(didSaveContact, [
.init(
id: "id".data(using: .utf8)!,
marshaled: "contact".data(using: .utf8)!,
authStatus: .verificationInProgress,
createdAt: now
),
.init(
id: "id".data(using: .utf8)!,
marshaled: "contact".data(using: .utf8)!,
authStatus: .verificationFailed,
createdAt: now
)
])
}
}
import CustomDump
import XCTest
import XXModels
import XXClient
@testable import AppCore
final class AuthCallbackHandlerResetTests: XCTestCase {
func testReset() throws {
var didFetchContacts: [XXModels.Contact.Query] = []
var didSaveContact: [XXModels.Contact] = []
let dbContact = XXModels.Contact(
id: "id".data(using: .utf8)!,
authStatus: .friend
)
let reset = AuthCallbackHandlerReset.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return [dbContact]
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
}
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try reset(xxContact)
XCTAssertNoDifference(didFetchContacts, [.init(id: ["id".data(using: .utf8)!])])
var expectedSavedContact = dbContact
expectedSavedContact.authStatus = .stranger
XCTAssertNoDifference(didSaveContact, [expectedSavedContact])
}
func testResetWhenContactNotInDatabase() throws {
let reset = AuthCallbackHandlerReset.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { _ in [] }
return db
}
)
var contact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try reset(contact)
}
}
import CustomDump
import XCTest
import XXClient
import XXMessengerClient
@testable import AppCore
final class AuthCallbackHandlerTests: XCTestCase {
func testCallbackHandling() throws {
struct TestState: Equatable {
var didRegisterAuthCallbacks = 0
var didCancelAuthCallbacks = 0
var didHandleRequest: [Contact] = []
var didHandleConfirm: [Contact] = []
var didHandleReset: [Contact] = []
}
var registeredAuthCallbacks: [AuthCallbacks] = []
var state = TestState()
var expectedState = state
var messenger: Messenger = .unimplemented
messenger.registerAuthCallbacks.run = { callbacks in
state.didRegisterAuthCallbacks += 1
registeredAuthCallbacks.append(callbacks)
return Cancellable { state.didCancelAuthCallbacks += 1 }
}
let handle = AuthCallbackHandler.live(
messenger: messenger,
handleRequest: .init { state.didHandleRequest.append($0) },
handleConfirm: .init { state.didHandleConfirm.append($0) },
handleReset: .init { state.didHandleReset.append($0) }
)
var cancellable: Any? = handle(onError: { error in
XCTFail("Unexpected error: \(error)")
})
expectedState.didRegisterAuthCallbacks += 1
XCTAssertNoDifference(state, expectedState)
let contact1 = XXClient.Contact.unimplemented("1".data(using: .utf8)!)
registeredAuthCallbacks.first?.handle(
.request(contact: contact1, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
expectedState.didHandleRequest.append(contact1)
XCTAssertNoDifference(state, expectedState)
let contact2 = XXClient.Contact.unimplemented("2".data(using: .utf8)!)
registeredAuthCallbacks.first?.handle(
.confirm(contact: contact2, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
expectedState.didHandleConfirm.append(contact2)
XCTAssertNoDifference(state, expectedState)
let contact3 = XXClient.Contact.unimplemented("3".data(using: .utf8)!)
registeredAuthCallbacks.first?.handle(
.reset(contact: contact3, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
expectedState.didHandleReset.append(contact3)
XCTAssertNoDifference(state, expectedState)
cancellable = nil
expectedState.didCancelAuthCallbacks += 1
XCTAssertNoDifference(state, expectedState)
_ = cancellable
}
func testCallbackHandlingFailure() {
enum Failure: Error, Equatable {
case request
case confirm
case reset
}
var registeredAuthCallbacks: [AuthCallbacks] = []
var errors: [Error] = []
var messenger: Messenger = .unimplemented
messenger.registerAuthCallbacks.run = { callbacks in
registeredAuthCallbacks.append(callbacks)
return Cancellable {}
}
let handle = AuthCallbackHandler.live(
messenger: messenger,
handleRequest: .init { _ in throw Failure.request },
handleConfirm: .init { _ in throw Failure.confirm },
handleReset: .init { _ in throw Failure.reset }
)
let cancellable = handle(onError: { errors.append($0) })
registeredAuthCallbacks.first?.handle(
.request(contact: .unimplemented(Data()), receptionId: Data(), ephemeralId: 0, roundId: 0)
)
registeredAuthCallbacks.first?.handle(
.confirm(contact: .unimplemented(Data()), receptionId: Data(), ephemeralId: 0, roundId: 0)
)
registeredAuthCallbacks.first?.handle(
.reset(contact: .unimplemented(Data()), receptionId: Data(), ephemeralId: 0, roundId: 0)
)
XCTAssertNoDifference(
errors.map { $0 as? Failure },
[.request, .confirm, .reset]
)
_ = cancellable
}
}
import AppCore
import ComposableArchitecture
import ContactsFeature
import RegisterFeature
......@@ -21,6 +22,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate
store.environment.authHandler.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
store.environment.messenger.isConnected.run = { false }
store.environment.messenger.connect.run = { messengerDidConnect += 1 }
......@@ -32,10 +34,13 @@ final class HomeFeatureTests: XCTestCase {
XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000])
XCTAssertNoDifference(messengerDidConnect, 1)
store.receive(.authHandler(.start))
store.receive(.networkMonitor(.stop))
store.receive(.messenger(.didStartUnregistered)) {
$0.register = RegisterState()
}
store.send(.authHandler(.stop))
}
func testMessengerStartRegistered() {
......@@ -51,6 +56,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate
store.environment.authHandler.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
store.environment.messenger.isConnected.run = { false }
store.environment.messenger.connect.run = { messengerDidConnect += 1 }
......@@ -73,11 +79,13 @@ final class HomeFeatureTests: XCTestCase {
XCTAssertNoDifference(messengerDidConnect, 1)
XCTAssertNoDifference(messengerDidLogIn, 1)
store.receive(.authHandler(.start))
store.receive(.networkMonitor(.stop))
store.receive(.messenger(.didStartRegistered))
store.receive(.networkMonitor(.start))
store.send(.networkMonitor(.stop))
store.send(.authHandler(.stop))
}
func testRegisterFinished() {
......@@ -94,6 +102,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate
store.environment.authHandler.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
store.environment.messenger.isConnected.run = { true }
store.environment.messenger.isLoggedIn.run = { false }
......@@ -118,11 +127,13 @@ final class HomeFeatureTests: XCTestCase {
XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000])
XCTAssertNoDifference(messengerDidLogIn, 1)
store.receive(.authHandler(.start))
store.receive(.networkMonitor(.stop))
store.receive(.messenger(.didStartRegistered))
store.receive(.networkMonitor(.start))
store.send(.networkMonitor(.stop))
store.send(.authHandler(.stop))
}
func testMessengerStartFailure() {
......@@ -137,14 +148,18 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate
store.environment.authHandler.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in throw error }
store.send(.messenger(.start))
store.receive(.authHandler(.start))
store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription
}
store.send(.authHandler(.stop))
}
func testMessengerStartConnectFailure() {
......@@ -159,16 +174,20 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate
store.environment.authHandler.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in }
store.environment.messenger.isConnected.run = { false }
store.environment.messenger.connect.run = { throw error }
store.send(.messenger(.start))
store.receive(.authHandler(.start))
store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription
}
store.send(.authHandler(.stop))
}
func testMessengerStartIsRegisteredFailure() {
......@@ -183,6 +202,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate
store.environment.authHandler.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in }
store.environment.messenger.isConnected.run = { true }
store.environment.messenger.isLoggedIn.run = { false }
......@@ -190,10 +210,13 @@ final class HomeFeatureTests: XCTestCase {
store.send(.messenger(.start))
store.receive(.authHandler(.start))
store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription
}
store.send(.authHandler(.stop))
}
func testMessengerStartLogInFailure() {
......@@ -208,6 +231,7 @@ final class HomeFeatureTests: XCTestCase {
store.environment.bgQueue = .immediate
store.environment.mainQueue = .immediate
store.environment.authHandler.run = { _ in Cancellable {} }
store.environment.messenger.start.run = { _ in }
store.environment.messenger.isConnected.run = { true }
store.environment.messenger.isLoggedIn.run = { false }
......@@ -216,10 +240,13 @@ final class HomeFeatureTests: XCTestCase {
store.send(.messenger(.start))
store.receive(.authHandler(.start))
store.receive(.networkMonitor(.stop))
store.receive(.messenger(.failure(error as NSError))) {
$0.failure = error.localizedDescription
}
store.send(.authHandler(.stop))
}
func testNetworkMonitorStart() {
......@@ -491,4 +518,45 @@ final class HomeFeatureTests: XCTestCase {
$0.contacts = nil
}
}
func testAuthCallbacks() {
let store = TestStore(
initialState: HomeState(),
reducer: homeReducer,
environment: .unimplemented
)
var didRunAuthHandler = 0
var didCancelAuthHandler = 0
var authHandlerOnError: [AuthCallbackHandler.OnError] = []
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.authHandler.run = { onError in
didRunAuthHandler += 1
authHandlerOnError.append(onError)
return Cancellable { didCancelAuthHandler += 1 }
}
store.send(.authHandler(.start))
XCTAssertNoDifference(didRunAuthHandler, 1)
struct AuthHandlerError: Error { var id: Int }
authHandlerOnError.first?(AuthHandlerError(id: 1))
store.receive(.authHandler(.failure(AuthHandlerError(id: 1) as NSError))) {
$0.authFailure = AuthHandlerError(id: 1).localizedDescription
}
store.send(.authHandler(.failureDismissed)) {
$0.authFailure = nil
}
store.send(.authHandler(.stop))
XCTAssertNoDifference(didCancelAuthHandler, 1)
authHandlerOnError.first?(AuthHandlerError(id: 2))
}
}
......@@ -19,6 +19,6 @@ extension MessengerRegisterAuthCallbacks {
extension MessengerRegisterAuthCallbacks {
public static let unimplemented = MessengerRegisterAuthCallbacks(
run: XCTUnimplemented("\(Self.self)")
run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {})
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment