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

Merge branch 'feature/messenger-backup' into 'development'

Messenger - backup

See merge request elixxir/elixxir-dapps-sdk-swift!107
parents d0112814 2a38d681
No related branches found
No related tags found
2 merge requests!107Messenger - backup,!102Release 1.0.0
Showing
with 838 additions and 9 deletions
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
## ▶️ Instantiate messenger ## ▶️ Instantiate messenger
Example: ### Example
```swift ```swift
// setup environment: // setup environment:
...@@ -24,7 +24,7 @@ let messenger: Messenger = .live(environment) ...@@ -24,7 +24,7 @@ let messenger: Messenger = .live(environment)
## 🚀 Start messenger ## 🚀 Start messenger
Example: ### Example
```swift ```swift
// allow cancellation of callbacks: // allow cancellation of callbacks:
...@@ -84,7 +84,7 @@ func start(messenger: Messenger) throws { ...@@ -84,7 +84,7 @@ func start(messenger: Messenger) throws {
## 🛠 Use client components directly ## 🛠 Use client components directly
Example: ### Example
```swift ```swift
// get cMix: // get cMix:
...@@ -95,4 +95,60 @@ let e2e = messenger.e2e() ...@@ -95,4 +95,60 @@ let e2e = messenger.e2e()
// get UserDicovery: // get UserDicovery:
let ud = messenger.ud() let ud = messenger.ud()
// 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:
let params: BackupParams = ...
try messenger.startBackup(
password: "backup-passphrase",
params: params
)
}
}
// update params in the backup:
let params: BackupParams = ...
try messenger.backupParams(params)
// stop the backup:
try messenger.stopBackup()
// optionally stop receiving backup data
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
```swift
let result = try messenger.restoreBackup(
backupData: ...,
backupPassphrase: "backup-passphrase"
)
// 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
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)")
)
}
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)")
)
}
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 {})
)
}
...@@ -42,9 +42,8 @@ extension MessengerRestoreBackup { ...@@ -42,9 +42,8 @@ extension MessengerRestoreBackup {
sessionPassword: password, sessionPassword: password,
backupFileContents: backupData backupFileContents: backupData
) )
let decoder = JSONDecoder()
let paramsData = report.params.data(using: .utf8)! 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( let cMix = try env.loadCMix(
storageDir: storageDir, storageDir: storageDir,
password: password, password: password,
......
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)")
)
}
import XCTestDynamicOverlay
import XXClient
public struct MessengerStartBackup {
public enum Error: Swift.Error, Equatable {
case isRunning
case notConnected
case notLoggedIn
}
public var run: (String, BackupParams) throws -> Void
public func callAsFunction(
password: String,
params: BackupParams
) throws {
try run(password, params)
}
}
extension MessengerStartBackup {
public static func live(_ env: MessengerEnvironment) -> MessengerStartBackup {
MessengerStartBackup { password, params 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 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: .init { data in
if !didAddParams {
addParams()
} else {
env.backupCallbacks.registered().handle(data)
}
}
)
env.backup.set(backup)
}
}
}
extension MessengerStartBackup {
public static let unimplemented = MessengerStartBackup(
run: XCTUnimplemented("\(Self.self)")
)
}
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)")
)
}
...@@ -4,6 +4,7 @@ public struct Messenger { ...@@ -4,6 +4,7 @@ public struct Messenger {
public var cMix: Stored<CMix?> public var cMix: Stored<CMix?>
public var e2e: Stored<E2E?> public var e2e: Stored<E2E?>
public var ud: Stored<UserDiscovery?> public var ud: Stored<UserDiscovery?>
public var backup: Stored<Backup?>
public var isCreated: MessengerIsCreated public var isCreated: MessengerIsCreated
public var create: MessengerCreate public var create: MessengerCreate
public var restoreBackup: MessengerRestoreBackup public var restoreBackup: MessengerRestoreBackup
...@@ -29,6 +30,12 @@ public struct Messenger { ...@@ -29,6 +30,12 @@ public struct Messenger {
public var registerForNotifications: MessengerRegisterForNotifications public var registerForNotifications: MessengerRegisterForNotifications
public var verifyContact: MessengerVerifyContact public var verifyContact: MessengerVerifyContact
public var sendMessage: MessengerSendMessage public var sendMessage: MessengerSendMessage
public var registerBackupCallback: MessengerRegisterBackupCallback
public var isBackupRunning: MessengerIsBackupRunning
public var startBackup: MessengerStartBackup
public var resumeBackup: MessengerResumeBackup
public var backupParams: MessengerBackupParams
public var stopBackup: MessengerStopBackup
} }
extension Messenger { extension Messenger {
...@@ -37,6 +44,7 @@ extension Messenger { ...@@ -37,6 +44,7 @@ extension Messenger {
cMix: env.cMix, cMix: env.cMix,
e2e: env.e2e, e2e: env.e2e,
ud: env.ud, ud: env.ud,
backup: env.backup,
isCreated: .live(env), isCreated: .live(env),
create: .live(env), create: .live(env),
restoreBackup: .live(env), restoreBackup: .live(env),
...@@ -61,7 +69,13 @@ extension Messenger { ...@@ -61,7 +69,13 @@ extension Messenger {
lookupContacts: .live(env), lookupContacts: .live(env),
registerForNotifications: .live(env), registerForNotifications: .live(env),
verifyContact: .live(env), verifyContact: .live(env),
sendMessage: .live(env) sendMessage: .live(env),
registerBackupCallback: .live(env),
isBackupRunning: .live(env),
startBackup: .live(env),
resumeBackup: .live(env),
backupParams: .live(env),
stopBackup: .live(env)
) )
} }
} }
...@@ -71,6 +85,7 @@ extension Messenger { ...@@ -71,6 +85,7 @@ extension Messenger {
cMix: .unimplemented(), cMix: .unimplemented(),
e2e: .unimplemented(), e2e: .unimplemented(),
ud: .unimplemented(), ud: .unimplemented(),
backup: .unimplemented(),
isCreated: .unimplemented, isCreated: .unimplemented,
create: .unimplemented, create: .unimplemented,
restoreBackup: .unimplemented, restoreBackup: .unimplemented,
...@@ -95,6 +110,12 @@ extension Messenger { ...@@ -95,6 +110,12 @@ extension Messenger {
lookupContacts: .unimplemented, lookupContacts: .unimplemented,
registerForNotifications: .unimplemented, registerForNotifications: .unimplemented,
verifyContact: .unimplemented, verifyContact: .unimplemented,
sendMessage: .unimplemented sendMessage: .unimplemented,
registerBackupCallback: .unimplemented,
isBackupRunning: .unimplemented,
startBackup: .unimplemented,
resumeBackup: .unimplemented,
backupParams: .unimplemented,
stopBackup: .unimplemented
) )
} }
...@@ -4,6 +4,8 @@ import XCTestDynamicOverlay ...@@ -4,6 +4,8 @@ import XCTestDynamicOverlay
public struct MessengerEnvironment { public struct MessengerEnvironment {
public var authCallbacks: AuthCallbacksRegistry public var authCallbacks: AuthCallbacksRegistry
public var backup: Stored<Backup?>
public var backupCallbacks: BackupCallbacksRegistry
public var cMix: Stored<CMix?> public var cMix: Stored<CMix?>
public var downloadNDF: DownloadAndVerifySignedNdf public var downloadNDF: DownloadAndVerifySignedNdf
public var e2e: Stored<E2E?> public var e2e: Stored<E2E?>
...@@ -12,6 +14,7 @@ public struct MessengerEnvironment { ...@@ -12,6 +14,7 @@ public struct MessengerEnvironment {
public var getCMixParams: GetCMixParams public var getCMixParams: GetCMixParams
public var getE2EParams: GetE2EParams public var getE2EParams: GetE2EParams
public var getSingleUseParams: GetSingleUseParams public var getSingleUseParams: GetSingleUseParams
public var initializeBackup: InitializeBackup
public var isListeningForMessages: Stored<Bool> public var isListeningForMessages: Stored<Bool>
public var isRegisteredWithUD: IsRegisteredWithUD public var isRegisteredWithUD: IsRegisteredWithUD
public var loadCMix: LoadCMix public var loadCMix: LoadCMix
...@@ -26,6 +29,7 @@ public struct MessengerEnvironment { ...@@ -26,6 +29,7 @@ public struct MessengerEnvironment {
public var newUdManagerFromBackup: NewUdManagerFromBackup public var newUdManagerFromBackup: NewUdManagerFromBackup
public var passwordStorage: PasswordStorage public var passwordStorage: PasswordStorage
public var registerForNotifications: RegisterForNotifications public var registerForNotifications: RegisterForNotifications
public var resumeBackup: ResumeBackup
public var searchUD: SearchUD public var searchUD: SearchUD
public var sleep: (TimeInterval) -> Void public var sleep: (TimeInterval) -> Void
public var storageDir: String public var storageDir: String
...@@ -45,6 +49,8 @@ extension MessengerEnvironment { ...@@ -45,6 +49,8 @@ extension MessengerEnvironment {
public static func live() -> MessengerEnvironment { public static func live() -> MessengerEnvironment {
MessengerEnvironment( MessengerEnvironment(
authCallbacks: .live(), authCallbacks: .live(),
backup: .inMemory(),
backupCallbacks: .live(),
cMix: .inMemory(), cMix: .inMemory(),
downloadNDF: .live, downloadNDF: .live,
e2e: .inMemory(), e2e: .inMemory(),
...@@ -53,6 +59,7 @@ extension MessengerEnvironment { ...@@ -53,6 +59,7 @@ extension MessengerEnvironment {
getCMixParams: .liveDefault, getCMixParams: .liveDefault,
getE2EParams: .liveDefault, getE2EParams: .liveDefault,
getSingleUseParams: .liveDefault, getSingleUseParams: .liveDefault,
initializeBackup: .live,
isListeningForMessages: .inMemory(false), isListeningForMessages: .inMemory(false),
isRegisteredWithUD: .live, isRegisteredWithUD: .live,
loadCMix: .live, loadCMix: .live,
...@@ -67,6 +74,7 @@ extension MessengerEnvironment { ...@@ -67,6 +74,7 @@ extension MessengerEnvironment {
newUdManagerFromBackup: .live, newUdManagerFromBackup: .live,
passwordStorage: .keychain, passwordStorage: .keychain,
registerForNotifications: .live, registerForNotifications: .live,
resumeBackup: .live,
searchUD: .live, searchUD: .live,
sleep: { Thread.sleep(forTimeInterval: $0) }, sleep: { Thread.sleep(forTimeInterval: $0) },
storageDir: MessengerEnvironment.defaultStorageDir, storageDir: MessengerEnvironment.defaultStorageDir,
...@@ -81,6 +89,8 @@ extension MessengerEnvironment { ...@@ -81,6 +89,8 @@ extension MessengerEnvironment {
extension MessengerEnvironment { extension MessengerEnvironment {
public static let unimplemented = MessengerEnvironment( public static let unimplemented = MessengerEnvironment(
authCallbacks: .unimplemented, authCallbacks: .unimplemented,
backup: .unimplemented(),
backupCallbacks: .unimplemented,
cMix: .unimplemented(), cMix: .unimplemented(),
downloadNDF: .unimplemented, downloadNDF: .unimplemented,
e2e: .unimplemented(), e2e: .unimplemented(),
...@@ -89,6 +99,7 @@ extension MessengerEnvironment { ...@@ -89,6 +99,7 @@ extension MessengerEnvironment {
getCMixParams: .unimplemented, getCMixParams: .unimplemented,
getE2EParams: .unimplemented, getE2EParams: .unimplemented,
getSingleUseParams: .unimplemented, getSingleUseParams: .unimplemented,
initializeBackup: .unimplemented,
isListeningForMessages: .unimplemented(placeholder: false), isListeningForMessages: .unimplemented(placeholder: false),
isRegisteredWithUD: .unimplemented, isRegisteredWithUD: .unimplemented,
loadCMix: .unimplemented, loadCMix: .unimplemented,
...@@ -103,6 +114,7 @@ extension MessengerEnvironment { ...@@ -103,6 +114,7 @@ extension MessengerEnvironment {
newUdManagerFromBackup: .unimplemented, newUdManagerFromBackup: .unimplemented,
passwordStorage: .unimplemented, passwordStorage: .unimplemented,
registerForNotifications: .unimplemented, registerForNotifications: .unimplemented,
resumeBackup: .unimplemented,
searchUD: .unimplemented, searchUD: .unimplemented,
sleep: XCTUnimplemented("\(Self.self).sleep"), sleep: XCTUnimplemented("\(Self.self).sleep"),
storageDir: "unimplemented", storageDir: "unimplemented",
......
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 })
)
}
import Foundation import Foundation
public struct BackupParams: Equatable, Codable { public struct BackupParams: Equatable {
public init( public init(
username: String, username: String,
email: String?, email: String?,
...@@ -15,3 +15,13 @@ public struct BackupParams: Equatable, Codable { ...@@ -15,3 +15,13 @@ public struct BackupParams: Equatable, Codable {
public var email: String? public var email: String?
public var phone: 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)
}
}
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
)
}
}
}
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())
}
}
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)
}
}
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)
}
}
}
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?] = []
var didAddJSON: [String] = []
let password = "test-password"
let e2eId = 123
let udId = 321
let dataWithoutParams = "backup-without-params".data(using: .utf8)!
let dataWithParams = "backup-with-params".data(using: .utf8)!
var env: MessengerEnvironment = .unimplemented
env.backup.get = { didSetBackup.last as? Backup }
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)
var backup: Backup = .unimplemented
backup.addJSON.run = { string in
didAddJSON.append(string)
}
return backup
}
let start: MessengerStartBackup = .live(env)
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(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.map(StringData.init),
[dataWithParams].map(StringData.init)
)
}
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: "", params: .stub)) { 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: "", params: .stub)) { 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: "", params: .stub)) { 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: "", params: .stub)) { error in
XCTAssertNoDifference(error as NSError, failure as NSError)
}
}
}
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()
}
}
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
}
}
}
import XXClient import XXClient
import XXMessengerClient
extension Message { extension Message {
static func stub(_ stubId: Int) -> Message { static func stub(_ stubId: Int) -> Message {
...@@ -16,3 +17,11 @@ extension Message { ...@@ -16,3 +17,11 @@ extension Message {
) )
} }
} }
extension BackupParams {
static let stub = BackupParams(
username: "stub-username",
email: "stub-email",
phone: "stub-phone"
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment