diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift index 8740c744732e28e1843772a450ffb6a622b5187c..684e344a914e4478f4657010fa72da310cad1d8f 100644 --- a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift +++ b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift @@ -37,6 +37,8 @@ public struct SendRequestState: Equatable { public enum SendRequestAction: Equatable, BindableAction { case start case sendTapped + case sendSucceeded + case sendFailed(String) case binding(BindingAction<SendRequestState>) case myContactFetched(XXClient.Contact?) } @@ -95,6 +97,51 @@ public let sendRequestReducer = Reducer<SendRequestState, SendRequestAction, Sen return .none case .sendTapped: + state.isSending = true + state.failure = nil + return .result { [state] in + func updateAuthStatus(_ authStatus: XXModels.Contact.AuthStatus) throws { + try env.db().bulkUpdateContacts( + .init(id: [try state.contact.getId()]), + .init(authStatus: authStatus) + ) + } + do { + try updateAuthStatus(.requesting) + let myFacts = try state.myContact?.getFacts() ?? [] + var includedFacts: [Fact] = [] + if state.sendUsername, let fact = myFacts.first(where: { $0.type == 0 }) { + includedFacts.append(fact) + } + if state.sendEmail, let fact = myFacts.first(where: { $0.type == 1 }) { + includedFacts.append(fact) + } + if state.sendPhone, let fact = myFacts.first(where: { $0.type == 2 }) { + includedFacts.append(fact) + } + _ = try env.messenger.e2e.tryGet().requestAuthenticatedChannel( + partner: state.contact, + myFacts: includedFacts + ) + try updateAuthStatus(.requested) + return .success(.sendSucceeded) + } catch { + try? updateAuthStatus(.requestFailed) + return .success(.sendFailed(error.localizedDescription)) + } + } + .subscribe(on: env.bgQueue) + .receive(on: env.mainQueue) + .eraseToEffect() + + case .sendSucceeded: + state.isSending = false + state.failure = nil + return .none + + case .sendFailed(let failure): + state.isSending = false + state.failure = failure return .none case .binding(_): diff --git a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift index a0a7750f278166629885b939c8e7428b270d15af..1dbe26be1d99196abe4217b23f5daeba46fe15c7 100644 --- a/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift +++ b/Examples/xx-messenger/Tests/SendRequestFeatureTests/SendRequestFeatureTests.swift @@ -58,28 +58,128 @@ final class SendRequestFeatureTests: XCTestCase { } func testSendRequest() { + var contact: XXClient.Contact = .unimplemented("contact-data".data(using: .utf8)!) + contact.getIdFromContact.run = { _ in "contact-id".data(using: .utf8)! } + var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!) - myContact.getFactsFromContact.run = { _ in - [ - Fact(fact: "my-username", type: 0), - Fact(fact: "my-email", type: 1), - Fact(fact: "my-phone", type: 2), - ] + let myFacts = [ + Fact(fact: "my-username", type: 0), + Fact(fact: "my-email", type: 1), + Fact(fact: "my-phone", type: 2), + ] + myContact.getFactsFromContact.run = { _ in myFacts } + + let store = TestStore( + initialState: SendRequestState( + contact: contact, + myContact: myContact + ), + reducer: sendRequestReducer, + environment: .unimplemented + ) + + struct DidBulkUpdateContacts: Equatable { + var query: XXModels.Contact.Query + var assignments: XXModels.Contact.Assignments + } + struct DidRequestAuthChannel: Equatable { + var partner: XXClient.Contact + var myFacts: [XXClient.Fact] + } + + var didBulkUpdateContacts: [DidBulkUpdateContacts] = [] + var didRequestAuthChannel: [DidRequestAuthChannel] = [] + + store.environment.mainQueue = .immediate + store.environment.bgQueue = .immediate + store.environment.db.run = { + var db: Database = .failing + db.bulkUpdateContacts.run = { query, assignments in + didBulkUpdateContacts.append(.init(query: query, assignments: assignments)) + return 0 + } + return db + } + store.environment.messenger.e2e.get = { + var e2e: E2E = .unimplemented + e2e.requestAuthenticatedChannel.run = { partner, myFacts in + didRequestAuthChannel.append(.init(partner: partner, myFacts: myFacts)) + return 0 + } + return e2e } + store.send(.sendTapped) { + $0.isSending = true + } + + XCTAssertNoDifference(didBulkUpdateContacts, [ + .init( + query: .init(id: ["contact-id".data(using: .utf8)!]), + assignments: .init(authStatus: .requesting) + ), + .init( + query: .init(id: ["contact-id".data(using: .utf8)!]), + assignments: .init(authStatus: .requested) + ) + ]) + + XCTAssertNoDifference(didRequestAuthChannel, [ + .init( + partner: contact, + myFacts: myFacts + ) + ]) + + store.receive(.sendSucceeded) { + $0.isSending = false + } + } + + func testSendRequestFailure() { + var contact: XXClient.Contact = .unimplemented("contact-data".data(using: .utf8)!) + contact.getIdFromContact.run = { _ in "contact-id".data(using: .utf8)! } + + var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!) + let myFacts = [ + Fact(fact: "my-username", type: 0), + Fact(fact: "my-email", type: 1), + Fact(fact: "my-phone", type: 2), + ] + myContact.getFactsFromContact.run = { _ in myFacts } + let store = TestStore( initialState: SendRequestState( - contact: .unimplemented("contact-data".data(using: .utf8)!), + contact: contact, myContact: myContact ), reducer: sendRequestReducer, environment: .unimplemented ) - store.send(.set(\.$sendPhone, false)) { - $0.sendPhone = false + struct Failure: Error {} + let failure = Failure() + + store.environment.mainQueue = .immediate + store.environment.bgQueue = .immediate + store.environment.db.run = { + var db: Database = .failing + db.bulkUpdateContacts.run = { _, _ in return 0 } + return db + } + store.environment.messenger.e2e.get = { + var e2e: E2E = .unimplemented + e2e.requestAuthenticatedChannel.run = { _, _ in throw failure } + return e2e } - store.send(.sendTapped) + store.send(.sendTapped) { + $0.isSending = true + } + + store.receive(.sendFailed(failure.localizedDescription)) { + $0.isSending = false + $0.failure = failure.localizedDescription + } } }