From c0220e52e51a0197cf8b857a4e67d109a98009eb Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 13:12:06 +0200
Subject: [PATCH 01/14] Add backup callbacks registry

---
 .../Utils/BackupCallbackRegistry.swift        | 36 ++++++++++++++++
 .../Utils/BackupCallbackRegistryTests.swift   | 43 +++++++++++++++++++
 2 files changed, 79 insertions(+)
 create mode 100644 Sources/XXMessengerClient/Utils/BackupCallbackRegistry.swift
 create mode 100644 Tests/XXMessengerClientTests/Utils/BackupCallbackRegistryTests.swift

diff --git a/Sources/XXMessengerClient/Utils/BackupCallbackRegistry.swift b/Sources/XXMessengerClient/Utils/BackupCallbackRegistry.swift
new file mode 100644
index 00000000..7c7fc9e8
--- /dev/null
+++ b/Sources/XXMessengerClient/Utils/BackupCallbackRegistry.swift
@@ -0,0 +1,36 @@
+import Foundation
+import XCTestDynamicOverlay
+import XXClient
+
+public struct BackupCallbacksRegistry {
+  public var register: (UpdateBackupFunc) -> Cancellable
+  public var registered: () -> UpdateBackupFunc
+}
+
+extension BackupCallbacksRegistry {
+  public static func live() -> BackupCallbacksRegistry {
+    class Registry {
+      var callbacks: [UUID: UpdateBackupFunc] = [:]
+    }
+    let registry = Registry()
+    return BackupCallbacksRegistry(
+      register: { callback in
+        let id = UUID()
+        registry.callbacks[id] = callback
+        return Cancellable { registry.callbacks[id] = nil }
+      },
+      registered: {
+        UpdateBackupFunc { data in
+          registry.callbacks.values.forEach { $0.handle(data) }
+        }
+      }
+    )
+  }
+}
+
+extension BackupCallbacksRegistry {
+  public static let unimplemented = BackupCallbacksRegistry(
+    register: XCTUnimplemented("\(Self.self).register", placeholder: Cancellable {}),
+    registered: XCTUnimplemented("\(Self.self).registered", placeholder: UpdateBackupFunc { _ in })
+  )
+}
diff --git a/Tests/XXMessengerClientTests/Utils/BackupCallbackRegistryTests.swift b/Tests/XXMessengerClientTests/Utils/BackupCallbackRegistryTests.swift
new file mode 100644
index 00000000..d3cba50c
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Utils/BackupCallbackRegistryTests.swift
@@ -0,0 +1,43 @@
+import CustomDump
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class BackupCallbackRegistryTests: XCTestCase {
+  func testRegistry() {
+    var firstCallbackDidHandle: [Data] = []
+    var secondCallbackDidHandle: [Data] = []
+
+    let firstCallback = UpdateBackupFunc { data in
+      firstCallbackDidHandle.append(data)
+    }
+    let secondCallback = UpdateBackupFunc { data in
+      secondCallbackDidHandle.append(data)
+    }
+    let callbackRegistry: BackupCallbacksRegistry = .live()
+    let registeredCallbacks = callbackRegistry.registered()
+    let firstCallbackCancellable = callbackRegistry.register(firstCallback)
+    let secondCallbackCancellable = callbackRegistry.register(secondCallback)
+
+    let firstData = "1".data(using: .utf8)!
+    registeredCallbacks.handle(firstData)
+
+    XCTAssertNoDifference(firstCallbackDidHandle, [firstData])
+    XCTAssertNoDifference(secondCallbackDidHandle, [firstData])
+
+    firstCallbackCancellable.cancel()
+    let secondData = "2".data(using: .utf8)!
+    registeredCallbacks.handle(secondData)
+
+    XCTAssertNoDifference(firstCallbackDidHandle, [firstData])
+    XCTAssertNoDifference(secondCallbackDidHandle, [firstData, secondData])
+
+    secondCallbackCancellable.cancel()
+
+    let thirdData = "3".data(using: .utf8)!
+    registeredCallbacks.handle(thirdData)
+
+    XCTAssertNoDifference(firstCallbackDidHandle, [firstData])
+    XCTAssertNoDifference(secondCallbackDidHandle, [firstData, secondData])
+  }
+}
-- 
GitLab


From 0f47f000e08722b6551b31cac3d8e09d14137332 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 13:18:31 +0200
Subject: [PATCH 02/14] Add MessengerRegisterBackupCallback function

---
 .../MessengerRegisterBackupCallback.swift     | 24 +++++++++++++
 .../Messenger/Messenger.swift                 |  7 ++--
 .../Messenger/MessengerEnvironment.swift      |  3 ++
 ...MessengerRegisterBackupCallbackTests.swift | 34 +++++++++++++++++++
 4 files changed, 66 insertions(+), 2 deletions(-)
 create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerRegisterBackupCallback.swift
 create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerRegisterBackupCallbackTests.swift

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerRegisterBackupCallback.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerRegisterBackupCallback.swift
new file mode 100644
index 00000000..f7c9a251
--- /dev/null
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerRegisterBackupCallback.swift
@@ -0,0 +1,24 @@
+import XCTestDynamicOverlay
+import XXClient
+
+public struct MessengerRegisterBackupCallback {
+  public var run: (UpdateBackupFunc) -> Cancellable
+
+  public func callAsFunction(_ callback: UpdateBackupFunc) -> Cancellable {
+    run(callback)
+  }
+}
+
+extension MessengerRegisterBackupCallback {
+  public static func live(_ env: MessengerEnvironment) -> MessengerRegisterBackupCallback {
+    MessengerRegisterBackupCallback { callback in
+      env.backupCallbacks.register(callback)
+    }
+  }
+}
+
+extension MessengerRegisterBackupCallback {
+  public static let unimplemented = MessengerRegisterBackupCallback(
+    run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {})
+  )
+}
diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift
index ea85852c..030aefb5 100644
--- a/Sources/XXMessengerClient/Messenger/Messenger.swift
+++ b/Sources/XXMessengerClient/Messenger/Messenger.swift
@@ -29,6 +29,7 @@ public struct Messenger {
   public var registerForNotifications: MessengerRegisterForNotifications
   public var verifyContact: MessengerVerifyContact
   public var sendMessage: MessengerSendMessage
+  public var registerBackupCallback: MessengerRegisterBackupCallback
 }
 
 extension Messenger {
@@ -61,7 +62,8 @@ extension Messenger {
       lookupContacts: .live(env),
       registerForNotifications: .live(env),
       verifyContact: .live(env),
-      sendMessage: .live(env)
+      sendMessage: .live(env),
+      registerBackupCallback: .live(env)
     )
   }
 }
@@ -95,6 +97,7 @@ extension Messenger {
     lookupContacts: .unimplemented,
     registerForNotifications: .unimplemented,
     verifyContact: .unimplemented,
-    sendMessage: .unimplemented
+    sendMessage: .unimplemented,
+    registerBackupCallback: .unimplemented
   )
 }
diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
index 641a8062..f6f9a826 100644
--- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
+++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
@@ -4,6 +4,7 @@ import XCTestDynamicOverlay
 
 public struct MessengerEnvironment {
   public var authCallbacks: AuthCallbacksRegistry
+  public var backupCallbacks: BackupCallbacksRegistry
   public var cMix: Stored<CMix?>
   public var downloadNDF: DownloadAndVerifySignedNdf
   public var e2e: Stored<E2E?>
@@ -45,6 +46,7 @@ extension MessengerEnvironment {
   public static func live() -> MessengerEnvironment {
     MessengerEnvironment(
       authCallbacks: .live(),
+      backupCallbacks: .live(),
       cMix: .inMemory(),
       downloadNDF: .live,
       e2e: .inMemory(),
@@ -81,6 +83,7 @@ extension MessengerEnvironment {
 extension MessengerEnvironment {
   public static let unimplemented = MessengerEnvironment(
     authCallbacks: .unimplemented,
+    backupCallbacks: .unimplemented,
     cMix: .unimplemented(),
     downloadNDF: .unimplemented,
     e2e: .unimplemented(),
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerRegisterBackupCallbackTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerRegisterBackupCallbackTests.swift
new file mode 100644
index 00000000..44e7e21c
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerRegisterBackupCallbackTests.swift
@@ -0,0 +1,34 @@
+import CustomDump
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class MessengerRegisterBackupCallbackTests: XCTestCase {
+  func testRegisterBackupCallback() {
+    var registeredCallbacks: [UpdateBackupFunc] = []
+    var didHandleData: [Data] = []
+    var didCancelRegisteredCallback = 0
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backupCallbacks.register = { callback in
+      registeredCallbacks.append(callback)
+      return Cancellable { didCancelRegisteredCallback += 1 }
+    }
+    let registerBackupCallback: MessengerRegisterBackupCallback = .live(env)
+    let cancellable = registerBackupCallback(UpdateBackupFunc { data in
+      didHandleData.append(data)
+    })
+
+    XCTAssertEqual(registeredCallbacks.count, 1)
+
+    registeredCallbacks.forEach { callback in
+      callback.handle("test".data(using: .utf8)!)
+    }
+
+    XCTAssertNoDifference(didHandleData, ["test".data(using: .utf8)!])
+
+    cancellable.cancel()
+
+    XCTAssertEqual(didCancelRegisteredCallback, 1)
+  }
+}
-- 
GitLab


From ef6c6113d3543851abeb0debdb1f3efece20ab36 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 13:23:30 +0200
Subject: [PATCH 03/14] Add MessengerIsBackupRunning function

---
 .../Functions/MessengerIsBackupRunning.swift  | 24 ++++++++++++
 .../Messenger/Messenger.swift                 |  7 +++-
 .../Messenger/MessengerEnvironment.swift      |  3 ++
 .../MessengerIsBackupRunningTests.swift       | 37 +++++++++++++++++++
 4 files changed, 69 insertions(+), 2 deletions(-)
 create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerIsBackupRunning.swift
 create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerIsBackupRunningTests.swift

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerIsBackupRunning.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerIsBackupRunning.swift
new file mode 100644
index 00000000..08453e23
--- /dev/null
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerIsBackupRunning.swift
@@ -0,0 +1,24 @@
+import XCTestDynamicOverlay
+import XXClient
+
+public struct MessengerIsBackupRunning {
+  public var run: () -> Bool
+
+  public func callAsFunction() -> Bool {
+    run()
+  }
+}
+
+extension MessengerIsBackupRunning {
+  public static func live(_ env: MessengerEnvironment) -> MessengerIsBackupRunning {
+    MessengerIsBackupRunning {
+      env.backup()?.isRunning() == true
+    }
+  }
+}
+
+extension MessengerIsBackupRunning {
+  public static let unimplemented = MessengerIsBackupRunning(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift
index 030aefb5..9e373e0b 100644
--- a/Sources/XXMessengerClient/Messenger/Messenger.swift
+++ b/Sources/XXMessengerClient/Messenger/Messenger.swift
@@ -30,6 +30,7 @@ public struct Messenger {
   public var verifyContact: MessengerVerifyContact
   public var sendMessage: MessengerSendMessage
   public var registerBackupCallback: MessengerRegisterBackupCallback
+  public var isBackupRunning: MessengerIsBackupRunning
 }
 
 extension Messenger {
@@ -63,7 +64,8 @@ extension Messenger {
       registerForNotifications: .live(env),
       verifyContact: .live(env),
       sendMessage: .live(env),
-      registerBackupCallback: .live(env)
+      registerBackupCallback: .live(env),
+      isBackupRunning: .live(env)
     )
   }
 }
@@ -98,6 +100,7 @@ extension Messenger {
     registerForNotifications: .unimplemented,
     verifyContact: .unimplemented,
     sendMessage: .unimplemented,
-    registerBackupCallback: .unimplemented
+    registerBackupCallback: .unimplemented,
+    isBackupRunning: .unimplemented
   )
 }
diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
index f6f9a826..d2ae4fac 100644
--- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
+++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
@@ -4,6 +4,7 @@ import XCTestDynamicOverlay
 
 public struct MessengerEnvironment {
   public var authCallbacks: AuthCallbacksRegistry
+  public var backup: Stored<Backup?>
   public var backupCallbacks: BackupCallbacksRegistry
   public var cMix: Stored<CMix?>
   public var downloadNDF: DownloadAndVerifySignedNdf
@@ -46,6 +47,7 @@ extension MessengerEnvironment {
   public static func live() -> MessengerEnvironment {
     MessengerEnvironment(
       authCallbacks: .live(),
+      backup: .inMemory(),
       backupCallbacks: .live(),
       cMix: .inMemory(),
       downloadNDF: .live,
@@ -83,6 +85,7 @@ extension MessengerEnvironment {
 extension MessengerEnvironment {
   public static let unimplemented = MessengerEnvironment(
     authCallbacks: .unimplemented,
+    backup: .unimplemented(),
     backupCallbacks: .unimplemented,
     cMix: .unimplemented(),
     downloadNDF: .unimplemented,
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerIsBackupRunningTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerIsBackupRunningTests.swift
new file mode 100644
index 00000000..7076aa64
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerIsBackupRunningTests.swift
@@ -0,0 +1,37 @@
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class MessengerIsBackupRunningTests: XCTestCase {
+  func testWithoutBackup() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    let isRunning: MessengerIsBackupRunning = .live(env)
+
+    XCTAssertFalse(isRunning())
+  }
+
+  func testWithBackupRunning() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.isRunning.run = { true }
+      return backup
+    }
+    let isRunning: MessengerIsBackupRunning = .live(env)
+
+    XCTAssertTrue(isRunning())
+  }
+
+  func testWithBackupNotRunning() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.isRunning.run = { false }
+      return backup
+    }
+    let isRunning: MessengerIsBackupRunning = .live(env)
+
+    XCTAssertFalse(isRunning())
+  }
+}
-- 
GitLab


From 5a258fcd289fb6a75c0ec5b585841cc09075c7b6 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 13:34:28 +0200
Subject: [PATCH 04/14] Add MessengerStopBackup function

---
 .../Functions/MessengerStopBackup.swift       | 26 +++++++++
 .../Messenger/Messenger.swift                 |  7 ++-
 .../Functions/MessengerStopBackupTests.swift  | 53 +++++++++++++++++++
 3 files changed, 84 insertions(+), 2 deletions(-)
 create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerStopBackup.swift
 create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerStopBackupTests.swift

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerStopBackup.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerStopBackup.swift
new file mode 100644
index 00000000..f5471166
--- /dev/null
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerStopBackup.swift
@@ -0,0 +1,26 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct MessengerStopBackup {
+  public var run: () throws -> Void
+
+  public func callAsFunction() throws {
+    try run()
+  }
+}
+
+extension MessengerStopBackup {
+  public static func live(_ env: MessengerEnvironment) -> MessengerStopBackup {
+    MessengerStopBackup {
+      guard let backup = env.backup() else { return }
+      try backup.stop()
+      env.backup.set(nil)
+    }
+  }
+}
+
+extension MessengerStopBackup {
+  public static let unimplemented = MessengerStopBackup(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift
index 9e373e0b..bc1c8bde 100644
--- a/Sources/XXMessengerClient/Messenger/Messenger.swift
+++ b/Sources/XXMessengerClient/Messenger/Messenger.swift
@@ -31,6 +31,7 @@ public struct Messenger {
   public var sendMessage: MessengerSendMessage
   public var registerBackupCallback: MessengerRegisterBackupCallback
   public var isBackupRunning: MessengerIsBackupRunning
+  public var stopBackup: MessengerStopBackup
 }
 
 extension Messenger {
@@ -65,7 +66,8 @@ extension Messenger {
       verifyContact: .live(env),
       sendMessage: .live(env),
       registerBackupCallback: .live(env),
-      isBackupRunning: .live(env)
+      isBackupRunning: .live(env),
+      stopBackup: .live(env)
     )
   }
 }
@@ -101,6 +103,7 @@ extension Messenger {
     verifyContact: .unimplemented,
     sendMessage: .unimplemented,
     registerBackupCallback: .unimplemented,
-    isBackupRunning: .unimplemented
+    isBackupRunning: .unimplemented,
+    stopBackup: .unimplemented
   )
 }
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStopBackupTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStopBackupTests.swift
new file mode 100644
index 00000000..306b6c2d
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStopBackupTests.swift
@@ -0,0 +1,53 @@
+import CustomDump
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class MessengerStopBackupTests: XCTestCase {
+  func testStop() throws {
+    var didStopBackup = 0
+    var didSetBackup: [Backup?] = []
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.stop.run = { didStopBackup += 1 }
+      return backup
+    }
+    env.backup.set = { backup in
+      didSetBackup.append(backup)
+    }
+    let stop: MessengerStopBackup = .live(env)
+
+    try stop()
+
+    XCTAssertEqual(didStopBackup, 1)
+    XCTAssertEqual(didSetBackup.count, 1)
+    XCTAssertNil(didSetBackup.first as? Backup)
+  }
+
+  func testStopFailure() {
+    struct Failure: Error, Equatable {}
+    let failure = Failure()
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.stop.run = { throw failure }
+      return backup
+    }
+    let stop: MessengerStopBackup = .live(env)
+
+    XCTAssertThrowsError(try stop()) { error in
+      XCTAssertNoDifference(error as NSError, failure as NSError)
+    }
+  }
+
+  func testStopWithoutBackup() throws {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    let stop: MessengerStopBackup = .live(env)
+
+    try stop()
+  }
+}
-- 
GitLab


From fa71cb6e08e84bfc5e107a0794653894ede15646 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 13:53:24 +0200
Subject: [PATCH 05/14] Add MessengerStartBackup function

---
 .../Functions/MessengerStartBakcup.swift      |  45 ++++++
 .../Messenger/Messenger.swift                 |   3 +
 .../Messenger/MessengerEnvironment.swift      |   3 +
 .../Functions/MessengerStartBackupTests.swift | 128 ++++++++++++++++++
 4 files changed, 179 insertions(+)
 create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerStartBakcup.swift
 create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBakcup.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBakcup.swift
new file mode 100644
index 00000000..366d87fc
--- /dev/null
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBakcup.swift
@@ -0,0 +1,45 @@
+import XCTestDynamicOverlay
+import XXClient
+
+public struct MessengerStartBackup {
+  public enum Error: Swift.Error, Equatable {
+    case isRunning
+    case notConnected
+    case notLoggedIn
+  }
+
+  public var run: (String) throws -> Void
+
+  public func callAsFunction(password: String) throws {
+    try run(password)
+  }
+}
+
+extension MessengerStartBackup {
+  public static func live(_ env: MessengerEnvironment) -> MessengerStartBackup {
+    MessengerStartBackup { password in
+      guard env.backup()?.isRunning() != true else {
+        throw Error.isRunning
+      }
+      guard let e2e = env.e2e() else {
+        throw Error.notConnected
+      }
+      guard let ud = env.ud() else {
+        throw Error.notLoggedIn
+      }
+      let backup = try env.initializeBackup(
+        e2eId: e2e.getId(),
+        udId: ud.getId(),
+        password: password,
+        callback: env.backupCallbacks.registered()
+      )
+      env.backup.set(backup)
+    }
+  }
+}
+
+extension MessengerStartBackup {
+  public static let unimplemented = MessengerStartBackup(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift
index bc1c8bde..7bccebdf 100644
--- a/Sources/XXMessengerClient/Messenger/Messenger.swift
+++ b/Sources/XXMessengerClient/Messenger/Messenger.swift
@@ -31,6 +31,7 @@ public struct Messenger {
   public var sendMessage: MessengerSendMessage
   public var registerBackupCallback: MessengerRegisterBackupCallback
   public var isBackupRunning: MessengerIsBackupRunning
+  public var startBackup: MessengerStartBackup
   public var stopBackup: MessengerStopBackup
 }
 
@@ -67,6 +68,7 @@ extension Messenger {
       sendMessage: .live(env),
       registerBackupCallback: .live(env),
       isBackupRunning: .live(env),
+      startBackup: .live(env),
       stopBackup: .live(env)
     )
   }
@@ -104,6 +106,7 @@ extension Messenger {
     sendMessage: .unimplemented,
     registerBackupCallback: .unimplemented,
     isBackupRunning: .unimplemented,
+    startBackup: .unimplemented,
     stopBackup: .unimplemented
   )
 }
diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
index d2ae4fac..5d940270 100644
--- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
+++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
@@ -14,6 +14,7 @@ public struct MessengerEnvironment {
   public var getCMixParams: GetCMixParams
   public var getE2EParams: GetE2EParams
   public var getSingleUseParams: GetSingleUseParams
+  public var initializeBackup: InitializeBackup
   public var isListeningForMessages: Stored<Bool>
   public var isRegisteredWithUD: IsRegisteredWithUD
   public var loadCMix: LoadCMix
@@ -57,6 +58,7 @@ extension MessengerEnvironment {
       getCMixParams: .liveDefault,
       getE2EParams: .liveDefault,
       getSingleUseParams: .liveDefault,
+      initializeBackup: .live,
       isListeningForMessages: .inMemory(false),
       isRegisteredWithUD: .live,
       loadCMix: .live,
@@ -95,6 +97,7 @@ extension MessengerEnvironment {
     getCMixParams: .unimplemented,
     getE2EParams: .unimplemented,
     getSingleUseParams: .unimplemented,
+    initializeBackup: .unimplemented,
     isListeningForMessages: .unimplemented(placeholder: false),
     isRegisteredWithUD: .unimplemented,
     loadCMix: .unimplemented,
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift
new file mode 100644
index 00000000..c1e703a6
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift
@@ -0,0 +1,128 @@
+import CustomDump
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class MessengerStartBackupTests: XCTestCase {
+  func testStart() throws {
+    struct InitBackupParams: Equatable {
+      var e2eId: Int
+      var udId: Int
+      var password: String
+    }
+    var didInitializeBackup: [InitBackupParams] = []
+    var backupCallbacks: [UpdateBackupFunc] = []
+    var didHandleCallback: [Data] = []
+    var didSetBackup: [Backup?] = []
+
+    let password = "test-password"
+    let e2eId = 123
+    let udId = 321
+    let data = "test-data".data(using: .utf8)!
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.backup.set = { didSetBackup.append($0) }
+    env.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.getId.run = { e2eId }
+      return e2e
+    }
+    env.ud.get = {
+      var ud: UserDiscovery = .unimplemented
+      ud.getId.run = { udId }
+      return ud
+    }
+    env.backupCallbacks.registered = {
+      UpdateBackupFunc { didHandleCallback.append($0) }
+    }
+    env.initializeBackup.run = { e2eId, udId, password, callback in
+      didInitializeBackup.append(.init(e2eId: e2eId, udId: udId, password: password))
+      backupCallbacks.append(callback)
+      return .unimplemented
+    }
+    let start: MessengerStartBackup = .live(env)
+
+    try start(password: password)
+
+    XCTAssertNoDifference(didInitializeBackup, [
+      .init(e2eId: e2eId, udId: udId, password: password)
+    ])
+    XCTAssertNoDifference(didSetBackup.map { $0 != nil }, [true])
+
+    backupCallbacks.forEach { $0.handle(data) }
+
+    XCTAssertNoDifference(didHandleCallback, [data])
+  }
+
+  func testStartWhenRunning() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.isRunning.run = { true }
+      return backup
+    }
+    let start: MessengerStartBackup = .live(env)
+
+    XCTAssertThrowsError(try start(password: "")) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerStartBackup.Error.isRunning as NSError
+      )
+    }
+  }
+
+  func testStartWhenNotConnected() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.e2e.get = { nil }
+    let start: MessengerStartBackup = .live(env)
+
+    XCTAssertThrowsError(try start(password: "")) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerStartBackup.Error.notConnected as NSError
+      )
+    }
+  }
+
+  func testStartWhenNotLoggedIn() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.e2e.get = { .unimplemented }
+    env.ud.get = { nil }
+    let start: MessengerStartBackup = .live(env)
+
+    XCTAssertThrowsError(try start(password: "")) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerStartBackup.Error.notLoggedIn as NSError
+      )
+    }
+  }
+
+  func testStartFailure() {
+    struct Failure: Error, Equatable {}
+    let failure = Failure()
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.getId.run = { 123 }
+      return e2e
+    }
+    env.ud.get = {
+      var ud: UserDiscovery = .unimplemented
+      ud.getId.run = { 321 }
+      return ud
+    }
+    env.backupCallbacks.registered = { UpdateBackupFunc { _ in  } }
+    env.initializeBackup.run = { _, _, _, _ in throw failure }
+    let start: MessengerStartBackup = .live(env)
+
+    XCTAssertThrowsError(try start(password: "abcd")) { error in
+      XCTAssertNoDifference(error as NSError, failure as NSError)
+    }
+  }
+}
-- 
GitLab


From d932ed9ca21a543e3e8a6ad50c2c25f5893304ef Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 13:59:33 +0200
Subject: [PATCH 06/14] Add MessengerResumeBackup function

---
 .../Functions/MessengerResumeBackup.swift     |  44 ++++++
 .../Messenger/Messenger.swift                 |   3 +
 .../Messenger/MessengerEnvironment.swift      |   3 +
 .../MessengerResumeBackupTests.swift          | 126 ++++++++++++++++++
 4 files changed, 176 insertions(+)
 create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerResumeBackup.swift
 create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerResumeBackupTests.swift

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerResumeBackup.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerResumeBackup.swift
new file mode 100644
index 00000000..9c4d62a0
--- /dev/null
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerResumeBackup.swift
@@ -0,0 +1,44 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct MessengerResumeBackup {
+  public enum Error: Swift.Error, Equatable {
+    case isRunning
+    case notConnected
+    case notLoggedIn
+  }
+
+  public var run: () throws -> Void
+
+  public func callAsFunction() throws {
+    try run()
+  }
+}
+
+extension MessengerResumeBackup {
+  public static func live(_ env: MessengerEnvironment) -> MessengerResumeBackup {
+    MessengerResumeBackup {
+      guard env.backup()?.isRunning() != true else {
+        throw Error.isRunning
+      }
+      guard let e2e = env.e2e() else {
+        throw Error.notConnected
+      }
+      guard let ud = env.ud() else {
+        throw Error.notLoggedIn
+      }
+      let backup = try env.resumeBackup(
+        e2eId: e2e.getId(),
+        udId: ud.getId(),
+        callback: env.backupCallbacks.registered()
+      )
+      env.backup.set(backup)
+    }
+  }
+}
+
+extension MessengerResumeBackup {
+  public static let unimplemented = MessengerResumeBackup(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift
index 7bccebdf..087bc40d 100644
--- a/Sources/XXMessengerClient/Messenger/Messenger.swift
+++ b/Sources/XXMessengerClient/Messenger/Messenger.swift
@@ -32,6 +32,7 @@ public struct Messenger {
   public var registerBackupCallback: MessengerRegisterBackupCallback
   public var isBackupRunning: MessengerIsBackupRunning
   public var startBackup: MessengerStartBackup
+  public var resumeBackup: MessengerResumeBackup
   public var stopBackup: MessengerStopBackup
 }
 
@@ -69,6 +70,7 @@ extension Messenger {
       registerBackupCallback: .live(env),
       isBackupRunning: .live(env),
       startBackup: .live(env),
+      resumeBackup: .live(env),
       stopBackup: .live(env)
     )
   }
@@ -107,6 +109,7 @@ extension Messenger {
     registerBackupCallback: .unimplemented,
     isBackupRunning: .unimplemented,
     startBackup: .unimplemented,
+    resumeBackup: .unimplemented,
     stopBackup: .unimplemented
   )
 }
diff --git a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
index 5d940270..f019df16 100644
--- a/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
+++ b/Sources/XXMessengerClient/Messenger/MessengerEnvironment.swift
@@ -29,6 +29,7 @@ public struct MessengerEnvironment {
   public var newUdManagerFromBackup: NewUdManagerFromBackup
   public var passwordStorage: PasswordStorage
   public var registerForNotifications: RegisterForNotifications
+  public var resumeBackup: ResumeBackup
   public var searchUD: SearchUD
   public var sleep: (TimeInterval) -> Void
   public var storageDir: String
@@ -73,6 +74,7 @@ extension MessengerEnvironment {
       newUdManagerFromBackup: .live,
       passwordStorage: .keychain,
       registerForNotifications: .live,
+      resumeBackup: .live,
       searchUD: .live,
       sleep: { Thread.sleep(forTimeInterval: $0) },
       storageDir: MessengerEnvironment.defaultStorageDir,
@@ -112,6 +114,7 @@ extension MessengerEnvironment {
     newUdManagerFromBackup: .unimplemented,
     passwordStorage: .unimplemented,
     registerForNotifications: .unimplemented,
+    resumeBackup: .unimplemented,
     searchUD: .unimplemented,
     sleep: XCTUnimplemented("\(Self.self).sleep"),
     storageDir: "unimplemented",
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerResumeBackupTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerResumeBackupTests.swift
new file mode 100644
index 00000000..7979ffbd
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerResumeBackupTests.swift
@@ -0,0 +1,126 @@
+import CustomDump
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class MessengerResumeBackupTests: XCTestCase {
+  func testResume() throws {
+    struct ResumeBackupParams: Equatable {
+      var e2eId: Int
+      var udId: Int
+    }
+    var didResumeBackup: [ResumeBackupParams] = []
+    var backupCallbacks: [UpdateBackupFunc] = []
+    var didHandleCallback: [Data] = []
+    var didSetBackup: [Backup?] = []
+
+    let e2eId = 123
+    let udId = 321
+    let data = "test-data".data(using: .utf8)!
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.backup.set = { didSetBackup.append($0) }
+    env.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.getId.run = { e2eId }
+      return e2e
+    }
+    env.ud.get = {
+      var ud: UserDiscovery = .unimplemented
+      ud.getId.run = { udId }
+      return ud
+    }
+    env.backupCallbacks.registered = {
+      UpdateBackupFunc { didHandleCallback.append($0) }
+    }
+    env.resumeBackup.run = { e2eId, udId, callback in
+      didResumeBackup.append(.init(e2eId: e2eId, udId: udId))
+      backupCallbacks.append(callback)
+      return .unimplemented
+    }
+    let resume: MessengerResumeBackup = .live(env)
+
+    try resume()
+
+    XCTAssertNoDifference(didResumeBackup, [
+      .init(e2eId: e2eId, udId: udId)
+    ])
+    XCTAssertNoDifference(didSetBackup.map { $0 != nil }, [true])
+
+    backupCallbacks.forEach { $0.handle(data) }
+
+    XCTAssertNoDifference(didHandleCallback, [data])
+  }
+
+  func testResumeWhenRunning() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.isRunning.run = { true }
+      return backup
+    }
+    let resume: MessengerResumeBackup = .live(env)
+
+    XCTAssertThrowsError(try resume()) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerResumeBackup.Error.isRunning as NSError
+      )
+    }
+  }
+
+  func testResumeWhenNotConnected() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.e2e.get = { nil }
+    let resume: MessengerResumeBackup = .live(env)
+
+    XCTAssertThrowsError(try resume()) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerResumeBackup.Error.notConnected as NSError
+      )
+    }
+  }
+
+  func testResumeWhenNotLoggedIn() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.e2e.get = { .unimplemented }
+    env.ud.get = { nil }
+    let resume: MessengerResumeBackup = .live(env)
+
+    XCTAssertThrowsError(try resume()) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerResumeBackup.Error.notLoggedIn as NSError
+      )
+    }
+  }
+
+  func testResumeFailure() {
+    struct Failure: Error, Equatable {}
+    let failure = Failure()
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    env.e2e.get = {
+      var e2e: E2E = .unimplemented
+      e2e.getId.run = { 123 }
+      return e2e
+    }
+    env.ud.get = {
+      var ud: UserDiscovery = .unimplemented
+      ud.getId.run = { 321 }
+      return ud
+    }
+    env.backupCallbacks.registered = { UpdateBackupFunc { _ in  } }
+    env.resumeBackup.run = { _, _ , _ in throw failure }
+    let resume: MessengerResumeBackup = .live(env)
+
+    XCTAssertThrowsError(try resume()) { error in
+      XCTAssertNoDifference(error as NSError, failure as NSError)
+    }
+  }
+}
-- 
GitLab


From b4d89289e55ae90b8b8709987b5ad4a83d86abb9 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 14:22:00 +0200
Subject: [PATCH 07/14] Refactor BackupParams coding

---
 .../Messenger/Functions/MessengerRestoreBackup.swift |  3 +--
 Sources/XXMessengerClient/Utils/BackupParams.swift   | 12 +++++++++++-
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerRestoreBackup.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerRestoreBackup.swift
index 66f50dd5..b196faca 100644
--- a/Sources/XXMessengerClient/Messenger/Functions/MessengerRestoreBackup.swift
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerRestoreBackup.swift
@@ -42,9 +42,8 @@ extension MessengerRestoreBackup {
         sessionPassword: password,
         backupFileContents: backupData
       )
-      let decoder = JSONDecoder()
       let paramsData = report.params.data(using: .utf8)!
-      let params = try decoder.decode(BackupParams.self, from: paramsData)
+      let params = try BackupParams.decode(paramsData)
       let cMix = try env.loadCMix(
         storageDir: storageDir,
         password: password,
diff --git a/Sources/XXMessengerClient/Utils/BackupParams.swift b/Sources/XXMessengerClient/Utils/BackupParams.swift
index 02fdc595..c3bd8fc0 100644
--- a/Sources/XXMessengerClient/Utils/BackupParams.swift
+++ b/Sources/XXMessengerClient/Utils/BackupParams.swift
@@ -1,6 +1,6 @@
 import Foundation
 
-public struct BackupParams: Equatable, Codable {
+public struct BackupParams: Equatable {
   public init(
     username: String,
     email: String?,
@@ -15,3 +15,13 @@ public struct BackupParams: Equatable, Codable {
   public var email: String?
   public var phone: String?
 }
+
+extension BackupParams: Codable {
+  public static func decode(_ data: Data) throws -> Self {
+    try JSONDecoder().decode(Self.self, from: data)
+  }
+
+  public func encode() throws -> Data {
+    try JSONEncoder().encode(self)
+  }
+}
-- 
GitLab


From 712a6c512a392d7e7c76f32274c2910f4ffedcd4 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 14:28:41 +0200
Subject: [PATCH 08/14] Add MessengerBackupParams function

---
 .../Functions/MessengerBackupParams.swift     | 33 ++++++++++
 .../Messenger/Messenger.swift                 |  3 +
 .../MessengerBackupParamsTests.swift          | 62 +++++++++++++++++++
 3 files changed, 98 insertions(+)
 create mode 100644 Sources/XXMessengerClient/Messenger/Functions/MessengerBackupParams.swift
 create mode 100644 Tests/XXMessengerClientTests/Messenger/Functions/MessengerBackupParamsTests.swift

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerBackupParams.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerBackupParams.swift
new file mode 100644
index 00000000..f5ac71f0
--- /dev/null
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerBackupParams.swift
@@ -0,0 +1,33 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct MessengerBackupParams {
+  public enum Error: Swift.Error, Equatable {
+    case notRunning
+  }
+
+  public var run: (BackupParams) throws -> Void
+
+  public func callAsFunction(_ params: BackupParams) throws {
+    try run(params)
+  }
+}
+
+extension MessengerBackupParams {
+  public static func live(_ env: MessengerEnvironment) -> MessengerBackupParams {
+    MessengerBackupParams { params in
+      guard let backup = env.backup(), backup.isRunning() else {
+        throw Error.notRunning
+      }
+      let paramsData = try params.encode()
+      let paramsString = String(data: paramsData, encoding: .utf8)!
+      backup.addJSON(paramsString)
+    }
+  }
+}
+
+extension MessengerBackupParams {
+  public static let unimplemented = MessengerBackupParams(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift
index 087bc40d..dab59a2f 100644
--- a/Sources/XXMessengerClient/Messenger/Messenger.swift
+++ b/Sources/XXMessengerClient/Messenger/Messenger.swift
@@ -33,6 +33,7 @@ public struct Messenger {
   public var isBackupRunning: MessengerIsBackupRunning
   public var startBackup: MessengerStartBackup
   public var resumeBackup: MessengerResumeBackup
+  public var backupParams: MessengerBackupParams
   public var stopBackup: MessengerStopBackup
 }
 
@@ -71,6 +72,7 @@ extension Messenger {
       isBackupRunning: .live(env),
       startBackup: .live(env),
       resumeBackup: .live(env),
+      backupParams: .live(env),
       stopBackup: .live(env)
     )
   }
@@ -110,6 +112,7 @@ extension Messenger {
     isBackupRunning: .unimplemented,
     startBackup: .unimplemented,
     resumeBackup: .unimplemented,
+    backupParams: .unimplemented,
     stopBackup: .unimplemented
   )
 }
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerBackupParamsTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerBackupParamsTests.swift
new file mode 100644
index 00000000..e0af7129
--- /dev/null
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerBackupParamsTests.swift
@@ -0,0 +1,62 @@
+import CustomDump
+import XCTest
+import XXClient
+@testable import XXMessengerClient
+
+final class MessengerBackupParamsTests: XCTestCase {
+  func testBackupParams() throws {
+    var didAddJSON: [String] = []
+
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.isRunning.run = { true }
+      backup.addJSON.run = { didAddJSON.append($0) }
+      return backup
+    }
+    let backup: MessengerBackupParams = .live(env)
+    let params = BackupParams(
+      username: "test-username",
+      email: "test-email",
+      phone: "test-phone"
+    )
+
+    try backup(params)
+
+    XCTAssertNoDifference(didAddJSON, [
+      String(data: try params.encode(), encoding: .utf8)!
+    ])
+  }
+
+  func testBackupParamsWhenNoBackup() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = { nil }
+    let backup: MessengerBackupParams = .live(env)
+    let params = BackupParams(username: "test", email: nil, phone: nil)
+
+    XCTAssertThrowsError(try backup(params)) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerBackupParams.Error.notRunning as NSError
+      )
+    }
+  }
+
+  func testBackupParamsWhenBackupNotRunning() {
+    var env: MessengerEnvironment = .unimplemented
+    env.backup.get = {
+      var backup: Backup = .unimplemented
+      backup.isRunning.run = { false }
+      return backup
+    }
+    let backup: MessengerBackupParams = .live(env)
+    let params = BackupParams(username: "test", email: nil, phone: nil)
+
+    XCTAssertThrowsError(try backup(params)) { error in
+      XCTAssertNoDifference(
+        error as NSError,
+        MessengerBackupParams.Error.notRunning as NSError
+      )
+    }
+  }
+}
-- 
GitLab


From c87b61ae6e869bb7d4db44701b89d96001bcc036 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 14:54:20 +0200
Subject: [PATCH 09/14] Expose backup in Messenger interface

---
 Sources/XXMessengerClient/Messenger/Messenger.swift | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Sources/XXMessengerClient/Messenger/Messenger.swift b/Sources/XXMessengerClient/Messenger/Messenger.swift
index dab59a2f..ce598c54 100644
--- a/Sources/XXMessengerClient/Messenger/Messenger.swift
+++ b/Sources/XXMessengerClient/Messenger/Messenger.swift
@@ -4,6 +4,7 @@ public struct Messenger {
   public var cMix: Stored<CMix?>
   public var e2e: Stored<E2E?>
   public var ud: Stored<UserDiscovery?>
+  public var backup: Stored<Backup?>
   public var isCreated: MessengerIsCreated
   public var create: MessengerCreate
   public var restoreBackup: MessengerRestoreBackup
@@ -43,6 +44,7 @@ extension Messenger {
       cMix: env.cMix,
       e2e: env.e2e,
       ud: env.ud,
+      backup: env.backup,
       isCreated: .live(env),
       create: .live(env),
       restoreBackup: .live(env),
@@ -83,6 +85,7 @@ extension Messenger {
     cMix: .unimplemented(),
     e2e: .unimplemented(),
     ud: .unimplemented(),
+    backup: .unimplemented(),
     isCreated: .unimplemented,
     create: .unimplemented,
     restoreBackup: .unimplemented,
-- 
GitLab


From 791ac12554c9dab9711e64dc6f7e2bb648391e6f Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 15:00:29 +0200
Subject: [PATCH 10/14] Update docs

---
 Docs/XXMessengerClient.md | 52 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/Docs/XXMessengerClient.md b/Docs/XXMessengerClient.md
index 05afa4aa..46ded0e7 100644
--- a/Docs/XXMessengerClient.md
+++ b/Docs/XXMessengerClient.md
@@ -95,4 +95,54 @@ let e2e = messenger.e2e()
 
 // get UserDicovery:
 let ud = messenger.ud()
-```
\ No newline at end of file
+
+// get Backup:
+let backup = messenger.backup()
+```
+
+## 💾 Backup
+
+Make backup:
+
+```swift
+// start receiving backup data before starting or resuming backup:
+let cancellable = messenger.registerBackupCallback(.init { data in
+  // handle backup data, save on disk, upload to cloud, etc.
+})
+
+// check if backup is already running:
+if messenger.isBackupRunning() == false {
+  do {
+    // try to resume previous backup:
+    try messenger.resumeBackup()
+  } catch {
+    // try to start a new backup:
+    try messenger.startBackup(password: "backup-passphrase")
+  }
+}
+
+// add backup params to the backup:
+let params: BackupParams = ...
+try messenger.backupParams(params)
+
+// stop the backup:
+try messenger.stopBackup()
+
+// optionally stop receiving backup data
+cancellable.cancel()
+```
+
+The registered backup callback can be reused later when a new backup is started. There is no need to cancel it and register a new callback in such a case.
+
+Restore from backup:
+
+```swift
+let result = try messenger.restoreBackup(
+  backupData: ...,
+  backupPassphrase: "backup-passphrase"
+)
+
+// handle restore result
+```
+
+If no error was thrown during restoration, the `Messenger` is already loaded, started, connected, and logged in.
\ No newline at end of file
-- 
GitLab


From 30ad9d7315c0592c2ee16de5e14d8a72c0c39bd1 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 15:07:06 +0200
Subject: [PATCH 11/14] Update docs

---
 Docs/XXMessengerClient.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Docs/XXMessengerClient.md b/Docs/XXMessengerClient.md
index 46ded0e7..17699f7a 100644
--- a/Docs/XXMessengerClient.md
+++ b/Docs/XXMessengerClient.md
@@ -142,7 +142,7 @@ let result = try messenger.restoreBackup(
   backupPassphrase: "backup-passphrase"
 )
 
-// handle restore result
+// handle restoration result
 ```
 
 If no error was thrown during restoration, the `Messenger` is already loaded, started, connected, and logged in.
\ No newline at end of file
-- 
GitLab


From 6077fe469bb287efaaf8871e38b9655e08773220 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 15:58:40 +0200
Subject: [PATCH 12/14] Fix typo

---
 .../{MessengerStartBakcup.swift => MessengerStartBackup.swift}    | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename Sources/XXMessengerClient/Messenger/Functions/{MessengerStartBakcup.swift => MessengerStartBackup.swift} (100%)

diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBakcup.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBackup.swift
similarity index 100%
rename from Sources/XXMessengerClient/Messenger/Functions/MessengerStartBakcup.swift
rename to Sources/XXMessengerClient/Messenger/Functions/MessengerStartBackup.swift
-- 
GitLab


From e985b2e16eba606284d39b1609d98ca71352ed1a Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 16:58:16 +0200
Subject: [PATCH 13/14] Update MessengerStartBackup function

- When first data arrive, ignore it and add params to the backup
- When following data arrive (that included params), handle it
---
 Docs/XXMessengerClient.md                     | 10 ++++-
 .../Functions/MessengerStartBackup.swift      | 27 ++++++++++---
 .../Functions/MessengerStartBackupTests.swift | 39 ++++++++++++++-----
 .../TestHelpers/StringData.swift              | 14 +++++++
 ...{Message+stubs.swift => TestDoubles.swift} |  9 +++++
 5 files changed, 82 insertions(+), 17 deletions(-)
 create mode 100644 Tests/XXMessengerClientTests/TestHelpers/StringData.swift
 rename Tests/XXMessengerClientTests/TestHelpers/{Message+stubs.swift => TestDoubles.swift} (74%)

diff --git a/Docs/XXMessengerClient.md b/Docs/XXMessengerClient.md
index 17699f7a..6b599f7f 100644
--- a/Docs/XXMessengerClient.md
+++ b/Docs/XXMessengerClient.md
@@ -117,11 +117,15 @@ if messenger.isBackupRunning() == false {
     try messenger.resumeBackup()
   } catch {
     // try to start a new backup:
-    try messenger.startBackup(password: "backup-passphrase")
+    let params: BackupParams = ...
+    try messenger.startBackup(
+      password: "backup-passphrase",
+      params: params
+    )
   }
 }
 
-// add backup params to the backup:
+// update params in the backup:
 let params: BackupParams = ...
 try messenger.backupParams(params)
 
@@ -132,6 +136,8 @@ try messenger.stopBackup()
 cancellable.cancel()
 ```
 
+When starting a new backup you must provide `BackupParams` to prevent creating backups that does not contain it.
+
 The registered backup callback can be reused later when a new backup is started. There is no need to cancel it and register a new callback in such a case.
 
 Restore from backup:
diff --git a/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBackup.swift b/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBackup.swift
index 366d87fc..254bc6b9 100644
--- a/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBackup.swift
+++ b/Sources/XXMessengerClient/Messenger/Functions/MessengerStartBackup.swift
@@ -8,16 +8,19 @@ public struct MessengerStartBackup {
     case notLoggedIn
   }
 
-  public var run: (String) throws -> Void
+  public var run: (String, BackupParams) throws -> Void
 
-  public func callAsFunction(password: String) throws {
-    try run(password)
+  public func callAsFunction(
+    password: String,
+    params: BackupParams
+  ) throws {
+    try run(password, params)
   }
 }
 
 extension MessengerStartBackup {
   public static func live(_ env: MessengerEnvironment) -> MessengerStartBackup {
-    MessengerStartBackup { password in
+    MessengerStartBackup { password, params in
       guard env.backup()?.isRunning() != true else {
         throw Error.isRunning
       }
@@ -27,11 +30,25 @@ extension MessengerStartBackup {
       guard let ud = env.ud() else {
         throw Error.notLoggedIn
       }
+      let paramsData = try params.encode()
+      let paramsString = String(data: paramsData, encoding: .utf8)!
+      var didAddParams = false
+      func addParams() {
+        guard let backup = env.backup() else { return }
+        backup.addJSON(paramsString)
+        didAddParams = true
+      }
       let backup = try env.initializeBackup(
         e2eId: e2e.getId(),
         udId: ud.getId(),
         password: password,
-        callback: env.backupCallbacks.registered()
+        callback: .init { data in
+          if !didAddParams {
+            addParams()
+          } else {
+            env.backupCallbacks.registered().handle(data)
+          }
+        }
       )
       env.backup.set(backup)
     }
diff --git a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift
index c1e703a6..b92bd1ee 100644
--- a/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift
+++ b/Tests/XXMessengerClientTests/Messenger/Functions/MessengerStartBackupTests.swift
@@ -14,14 +14,16 @@ final class MessengerStartBackupTests: XCTestCase {
     var backupCallbacks: [UpdateBackupFunc] = []
     var didHandleCallback: [Data] = []
     var didSetBackup: [Backup?] = []
+    var didAddJSON: [String] = []
 
     let password = "test-password"
     let e2eId = 123
     let udId = 321
-    let data = "test-data".data(using: .utf8)!
+    let dataWithoutParams = "backup-without-params".data(using: .utf8)!
+    let dataWithParams = "backup-with-params".data(using: .utf8)!
 
     var env: MessengerEnvironment = .unimplemented
-    env.backup.get = { nil }
+    env.backup.get = { didSetBackup.last as? Backup }
     env.backup.set = { didSetBackup.append($0) }
     env.e2e.get = {
       var e2e: E2E = .unimplemented
@@ -39,20 +41,37 @@ final class MessengerStartBackupTests: XCTestCase {
     env.initializeBackup.run = { e2eId, udId, password, callback in
       didInitializeBackup.append(.init(e2eId: e2eId, udId: udId, password: password))
       backupCallbacks.append(callback)
-      return .unimplemented
+      var backup: Backup = .unimplemented
+      backup.addJSON.run = { string in
+        didAddJSON.append(string)
+      }
+      return backup
     }
     let start: MessengerStartBackup = .live(env)
 
-    try start(password: password)
+    try start(password: password, params: .stub)
 
     XCTAssertNoDifference(didInitializeBackup, [
       .init(e2eId: e2eId, udId: udId, password: password)
     ])
     XCTAssertNoDifference(didSetBackup.map { $0 != nil }, [true])
 
-    backupCallbacks.forEach { $0.handle(data) }
+    backupCallbacks.forEach { $0.handle(dataWithoutParams) }
+
+    XCTAssertNoDifference(
+      didHandleCallback.map(StringData.init),
+      [].map(StringData.init)
+    )
+    XCTAssertNoDifference(didAddJSON, [
+      String(data: try BackupParams.stub.encode(), encoding: .utf8)!
+    ])
+
+    backupCallbacks.forEach { $0.handle(dataWithParams) }
 
-    XCTAssertNoDifference(didHandleCallback, [data])
+    XCTAssertNoDifference(
+      didHandleCallback.map(StringData.init),
+      [dataWithParams].map(StringData.init)
+    )
   }
 
   func testStartWhenRunning() {
@@ -64,7 +83,7 @@ final class MessengerStartBackupTests: XCTestCase {
     }
     let start: MessengerStartBackup = .live(env)
 
-    XCTAssertThrowsError(try start(password: "")) { error in
+    XCTAssertThrowsError(try start(password: "", params: .stub)) { error in
       XCTAssertNoDifference(
         error as NSError,
         MessengerStartBackup.Error.isRunning as NSError
@@ -78,7 +97,7 @@ final class MessengerStartBackupTests: XCTestCase {
     env.e2e.get = { nil }
     let start: MessengerStartBackup = .live(env)
 
-    XCTAssertThrowsError(try start(password: "")) { error in
+    XCTAssertThrowsError(try start(password: "", params: .stub)) { error in
       XCTAssertNoDifference(
         error as NSError,
         MessengerStartBackup.Error.notConnected as NSError
@@ -93,7 +112,7 @@ final class MessengerStartBackupTests: XCTestCase {
     env.ud.get = { nil }
     let start: MessengerStartBackup = .live(env)
 
-    XCTAssertThrowsError(try start(password: "")) { error in
+    XCTAssertThrowsError(try start(password: "", params: .stub)) { error in
       XCTAssertNoDifference(
         error as NSError,
         MessengerStartBackup.Error.notLoggedIn as NSError
@@ -121,7 +140,7 @@ final class MessengerStartBackupTests: XCTestCase {
     env.initializeBackup.run = { _, _, _, _ in throw failure }
     let start: MessengerStartBackup = .live(env)
 
-    XCTAssertThrowsError(try start(password: "abcd")) { error in
+    XCTAssertThrowsError(try start(password: "", params: .stub)) { error in
       XCTAssertNoDifference(error as NSError, failure as NSError)
     }
   }
diff --git a/Tests/XXMessengerClientTests/TestHelpers/StringData.swift b/Tests/XXMessengerClientTests/TestHelpers/StringData.swift
new file mode 100644
index 00000000..6987a4ea
--- /dev/null
+++ b/Tests/XXMessengerClientTests/TestHelpers/StringData.swift
@@ -0,0 +1,14 @@
+import CustomDump
+import Foundation
+
+struct StringData: Equatable, CustomDumpStringConvertible {
+  var data: Data
+
+  var customDumpDescription: String {
+    if let string = String(data: data, encoding: .utf8) {
+      return #"Data(string: "\#(string)", encoding: .utf8)"#
+    } else {
+      return data.customDumpDescription
+    }
+  }
+}
diff --git a/Tests/XXMessengerClientTests/TestHelpers/Message+stubs.swift b/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift
similarity index 74%
rename from Tests/XXMessengerClientTests/TestHelpers/Message+stubs.swift
rename to Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift
index e10bea4a..4d57e293 100644
--- a/Tests/XXMessengerClientTests/TestHelpers/Message+stubs.swift
+++ b/Tests/XXMessengerClientTests/TestHelpers/TestDoubles.swift
@@ -1,4 +1,5 @@
 import XXClient
+import XXMessengerClient
 
 extension Message {
   static func stub(_ stubId: Int) -> Message {
@@ -16,3 +17,11 @@ extension Message {
     )
   }
 }
+
+extension BackupParams {
+  static let stub = BackupParams(
+    username: "stub-username",
+    email: "stub-email",
+    phone: "stub-phone"
+  )
+}
-- 
GitLab


From 2a38d681b9e168c930ab0acd3bed11db5db294e9 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Wed, 28 Sep 2022 17:06:13 +0200
Subject: [PATCH 14/14] Update docs

---
 Docs/XXMessengerClient.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Docs/XXMessengerClient.md b/Docs/XXMessengerClient.md
index 6b599f7f..971aefd9 100644
--- a/Docs/XXMessengerClient.md
+++ b/Docs/XXMessengerClient.md
@@ -4,7 +4,7 @@
 
 ## ▶️ Instantiate messenger
 
-Example:
+### Example
 
 ```swift
 // setup environment:
@@ -24,7 +24,7 @@ let messenger: Messenger = .live(environment)
 
 ## 🚀 Start messenger
 
-Example:
+### Example
 
 ```swift
 // allow cancellation of callbacks:
@@ -84,7 +84,7 @@ func start(messenger: Messenger) throws {
 
 ## 🛠 Use client components directly
 
-Example:
+### Example
 
 ```swift
 // get cMix:
@@ -102,7 +102,7 @@ let backup = messenger.backup()
 
 ## 💾 Backup
 
-Make backup:
+### Make backup
 
 ```swift
 // start receiving backup data before starting or resuming backup:
@@ -140,7 +140,7 @@ When starting a new backup you must provide `BackupParams` to prevent creating b
 
 The registered backup callback can be reused later when a new backup is started. There is no need to cancel it and register a new callback in such a case.
 
-Restore from backup:
+### Restore from backup
 
 ```swift
 let result = try messenger.restoreBackup(
-- 
GitLab