From b65e5aeac07a46effe203028ca824415642d8dac Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 7 Sep 2022 15:50:03 +0200
Subject: [PATCH] Send auth request

---
 .../SendRequestFeature.swift                  |  47 +++++++
 .../SendRequestFeatureTests.swift             | 120 ++++++++++++++++--
 2 files changed, 157 insertions(+), 10 deletions(-)

diff --git a/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift b/Examples/xx-messenger/Sources/SendRequestFeature/SendRequestFeature.swift
index 8740c744..684e344a 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 a0a7750f..1dbe26be 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
+    }
   }
 }
-- 
GitLab