import AppCore import ComposableArchitecture import CustomDump import HomeFeature import RestoreFeature import WelcomeFeature import XCTest import XXClient @testable import AppFeature final class AppComponentTests: XCTestCase { func testSetupLogging() { var actions: [Action] = [] let store = TestStore( initialState: AppComponent.State(), reducer: AppComponent() ) store.dependencies.app.messenger.setLogLevel.run = { level in actions.append(.didSetLogLevel(level)) return true } store.dependencies.app.messenger.startLogging.run = { actions.append(.didStartLogging) } store.send(.setupLogging) XCTAssertNoDifference(actions, [ .didSetLogLevel(.debug), .didStartLogging, ]) } func testStartWithoutMessengerCreated() { var actions: [Action]! let store = TestStore( initialState: AppComponent.State(), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { false } store.dependencies.app.messenger.isLoaded.run = { false } store.dependencies.app.messenger.isCreated.run = { false } store.dependencies.app.dbManager.makeDB.run = { actions.append(.didMakeDB) } store.dependencies.app.authHandler.run = { _ in actions.append(.didStartAuthHandler) return Cancellable {} } store.dependencies.app.messageListener.run = { _ in actions.append(.didStartMessageListener) return Cancellable {} } store.dependencies.app.receiveFileHandler.run = { _ in actions.append(.didStartReceiveFileHandler) return Cancellable {} } store.dependencies.app.messenger.registerBackupCallback.run = { _ in actions.append(.didRegisterBackupCallback) return Cancellable {} } store.dependencies.app.groupRequestHandler.run = { _ in actions.append(.didStartGroupRequestHandler) return Cancellable {} } store.dependencies.app.groupMessageHandler.run = { _ in actions.append(.didStartGroupMessageHandler) return Cancellable {} } actions = [] store.send(.start) store.receive(.set(\.$screen, .welcome(WelcomeComponent.State()))) { $0.screen = .welcome(WelcomeComponent.State()) } XCTAssertNoDifference(actions, [ .didMakeDB, .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, ]) store.send(.stop) } func testStartWithMessengerCreated() { var actions: [Action]! let store = TestStore( initialState: AppComponent.State(), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { false } store.dependencies.app.messenger.isLoaded.run = { false } store.dependencies.app.messenger.isCreated.run = { true } store.dependencies.app.dbManager.makeDB.run = { actions.append(.didMakeDB) } store.dependencies.app.messenger.load.run = { actions.append(.didLoadMessenger) } store.dependencies.app.authHandler.run = { _ in actions.append(.didStartAuthHandler) return Cancellable {} } store.dependencies.app.messageListener.run = { _ in actions.append(.didStartMessageListener) return Cancellable {} } store.dependencies.app.receiveFileHandler.run = { _ in actions.append(.didStartReceiveFileHandler) return Cancellable {} } store.dependencies.app.messenger.registerBackupCallback.run = { _ in actions.append(.didRegisterBackupCallback) return Cancellable {} } store.dependencies.app.groupRequestHandler.run = { _ in actions.append(.didStartGroupRequestHandler) return Cancellable {} } store.dependencies.app.groupMessageHandler.run = { _ in actions.append(.didStartGroupMessageHandler) return Cancellable {} } actions = [] store.send(.start) store.receive(.set(\.$screen, .home(HomeComponent.State()))) { $0.screen = .home(HomeComponent.State()) } XCTAssertNoDifference(actions, [ .didMakeDB, .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, .didLoadMessenger, ]) store.send(.stop) } func testWelcomeFinished() { var actions: [Action]! let store = TestStore( initialState: AppComponent.State( screen: .welcome(WelcomeComponent.State()) ), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { true } store.dependencies.app.messenger.isLoaded.run = { false } store.dependencies.app.messenger.isCreated.run = { true } store.dependencies.app.messenger.load.run = { actions.append(.didLoadMessenger) } store.dependencies.app.authHandler.run = { _ in actions.append(.didStartAuthHandler) return Cancellable {} } store.dependencies.app.messageListener.run = { _ in actions.append(.didStartMessageListener) return Cancellable {} } store.dependencies.app.receiveFileHandler.run = { _ in actions.append(.didStartReceiveFileHandler) return Cancellable {} } store.dependencies.app.messenger.registerBackupCallback.run = { _ in actions.append(.didRegisterBackupCallback) return Cancellable {} } store.dependencies.app.groupRequestHandler.run = { _ in actions.append(.didStartGroupRequestHandler) return Cancellable {} } store.dependencies.app.groupMessageHandler.run = { _ in actions.append(.didStartGroupMessageHandler) return Cancellable {} } actions = [] store.send(.welcome(.finished)) { $0.screen = .loading } store.receive(.set(\.$screen, .home(HomeComponent.State()))) { $0.screen = .home(HomeComponent.State()) } XCTAssertNoDifference(actions, [ .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, .didLoadMessenger, ]) store.send(.stop) } func testRestoreFinished() { var actions: [Action]! let store = TestStore( initialState: AppComponent.State( screen: .restore(RestoreComponent.State()) ), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { true } store.dependencies.app.messenger.isLoaded.run = { false } store.dependencies.app.messenger.isCreated.run = { true } store.dependencies.app.messenger.load.run = { actions.append(.didLoadMessenger) } store.dependencies.app.authHandler.run = { _ in actions.append(.didStartAuthHandler) return Cancellable {} } store.dependencies.app.messageListener.run = { _ in actions.append(.didStartMessageListener) return Cancellable {} } store.dependencies.app.receiveFileHandler.run = { _ in actions.append(.didStartReceiveFileHandler) return Cancellable {} } store.dependencies.app.messenger.registerBackupCallback.run = { _ in actions.append(.didRegisterBackupCallback) return Cancellable {} } store.dependencies.app.groupRequestHandler.run = { _ in actions.append(.didStartGroupRequestHandler) return Cancellable {} } store.dependencies.app.groupMessageHandler.run = { _ in actions.append(.didStartGroupMessageHandler) return Cancellable {} } actions = [] store.send(.restore(.finished)) { $0.screen = .loading } store.receive(.set(\.$screen, .home(HomeComponent.State()))) { $0.screen = .home(HomeComponent.State()) } XCTAssertNoDifference(actions, [ .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, .didLoadMessenger, ]) store.send(.stop) } func testHomeDidDeleteAccount() { var actions: [Action]! let store = TestStore( initialState: AppComponent.State( screen: .home(HomeComponent.State()) ), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { true } store.dependencies.app.messenger.isLoaded.run = { false } store.dependencies.app.messenger.isCreated.run = { false } store.dependencies.app.authHandler.run = { _ in actions.append(.didStartAuthHandler) return Cancellable {} } store.dependencies.app.messageListener.run = { _ in actions.append(.didStartMessageListener) return Cancellable {} } store.dependencies.app.receiveFileHandler.run = { _ in actions.append(.didStartReceiveFileHandler) return Cancellable {} } store.dependencies.app.messenger.registerBackupCallback.run = { _ in actions.append(.didRegisterBackupCallback) return Cancellable {} } store.dependencies.app.groupRequestHandler.run = { _ in actions.append(.didStartGroupRequestHandler) return Cancellable {} } store.dependencies.app.groupMessageHandler.run = { _ in actions.append(.didStartGroupMessageHandler) return Cancellable {} } actions = [] store.send(.home(.deleteAccount(.success))) { $0.screen = .loading } store.receive(.set(\.$screen, .welcome(WelcomeComponent.State()))) { $0.screen = .welcome(WelcomeComponent.State()) } XCTAssertNoDifference(actions, [ .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, ]) store.send(.stop) } func testWelcomeRestoreTapped() { let store = TestStore( initialState: AppComponent.State( screen: .welcome(WelcomeComponent.State()) ), reducer: AppComponent() ) store.send(.welcome(.restoreTapped)) { $0.screen = .restore(RestoreComponent.State()) } } func testWelcomeFailed() { let failure = "Something went wrong" let store = TestStore( initialState: AppComponent.State( screen: .welcome(WelcomeComponent.State()) ), reducer: AppComponent() ) store.send(.welcome(.failed(failure))) { $0.screen = .failure(failure) } } func testStartDatabaseMakeFailure() { struct Failure: Error {} let error = Failure() let store = TestStore( initialState: AppComponent.State(), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { false } store.dependencies.app.dbManager.makeDB.run = { throw error } store.send(.start) store.receive(.set(\.$screen, .failure(error.localizedDescription))) { $0.screen = .failure(error.localizedDescription) } store.send(.stop) } func testStartMessengerLoadFailure() { struct Failure: Error {} let error = Failure() var actions: [Action]! let store = TestStore( initialState: AppComponent.State(), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { true } store.dependencies.app.messenger.isLoaded.run = { false } store.dependencies.app.messenger.isCreated.run = { true } store.dependencies.app.messenger.load.run = { throw error } store.dependencies.app.authHandler.run = { _ in actions.append(.didStartAuthHandler) return Cancellable {} } store.dependencies.app.messageListener.run = { _ in actions.append(.didStartMessageListener) return Cancellable {} } store.dependencies.app.receiveFileHandler.run = { _ in actions.append(.didStartReceiveFileHandler) return Cancellable {} } store.dependencies.app.messenger.registerBackupCallback.run = { _ in actions.append(.didRegisterBackupCallback) return Cancellable {} } store.dependencies.app.groupRequestHandler.run = { _ in actions.append(.didStartGroupRequestHandler) return Cancellable {} } store.dependencies.app.groupMessageHandler.run = { _ in actions.append(.didStartGroupMessageHandler) return Cancellable {} } actions = [] store.send(.start) store.receive(.set(\.$screen, .failure(error.localizedDescription))) { $0.screen = .failure(error.localizedDescription) } XCTAssertNoDifference(actions, [ .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, ]) store.send(.stop) } func testStartHandlersAndListeners() { var actions: [Action]! var authHandlerOnError: [AuthCallbackHandler.OnError] = [] var messageListenerOnError: [MessageListenerHandler.OnError] = [] var fileHandlerOnError: [ReceiveFileHandler.OnError] = [] var backupCallback: [UpdateBackupFunc] = [] let store = TestStore( initialState: AppComponent.State(), reducer: AppComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.dbManager.hasDB.run = { true } store.dependencies.app.messenger.isLoaded.run = { true } store.dependencies.app.messenger.isCreated.run = { true } store.dependencies.app.authHandler.run = { onError in authHandlerOnError.append(onError) actions.append(.didStartAuthHandler) return Cancellable { actions.append(.didCancelAuthHandler) } } store.dependencies.app.messageListener.run = { onError in messageListenerOnError.append(onError) actions.append(.didStartMessageListener) return Cancellable { actions.append(.didCancelMessageListener) } } store.dependencies.app.receiveFileHandler.run = { onError in fileHandlerOnError.append(onError) actions.append(.didStartReceiveFileHandler) return Cancellable { actions.append(.didCancelReceiveFileHandler) } } store.dependencies.app.messenger.registerBackupCallback.run = { callback in backupCallback.append(callback) actions.append(.didRegisterBackupCallback) return Cancellable { actions.append(.didCancelBackupCallback) } } store.dependencies.app.log.run = { msg, _, _, _ in actions.append(.didLog(msg)) } store.dependencies.app.backupStorage.store = { data in actions.append(.didStoreBackup(data)) } store.dependencies.app.groupRequestHandler.run = { _ in actions.append(.didStartGroupRequestHandler) return Cancellable { actions.append(.didCancelGroupRequestHandler) } } store.dependencies.app.groupMessageHandler.run = { _ in actions.append(.didStartGroupMessageHandler) return Cancellable { actions.append(.didCancelGroupMessageHandler) } } actions = [] store.send(.start) store.receive(.set(\.$screen, .home(HomeComponent.State()))) { $0.screen = .home(HomeComponent.State()) } XCTAssertNoDifference(actions, [ .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, ]) actions = [] store.send(.start) { $0.screen = .loading } store.receive(.set(\.$screen, .home(HomeComponent.State()))) { $0.screen = .home(HomeComponent.State()) } XCTAssertNoDifference(actions, [ .didCancelAuthHandler, .didCancelMessageListener, .didCancelReceiveFileHandler, .didCancelGroupRequestHandler, .didCancelGroupMessageHandler, .didCancelBackupCallback, .didStartAuthHandler, .didStartMessageListener, .didStartReceiveFileHandler, .didStartGroupRequestHandler, .didStartGroupMessageHandler, .didRegisterBackupCallback, ]) actions = [] let authError = NSError(domain: "auth-handler-error", code: 1) authHandlerOnError.first?(authError) XCTAssertNoDifference(actions, [ .didLog(.error(authError)) ]) actions = [] let messageError = NSError(domain: "message-listener-error", code: 2) messageListenerOnError.first?(messageError) XCTAssertNoDifference(actions, [ .didLog(.error(messageError)) ]) actions = [] let fileError = NSError(domain: "receive-file-error", code: 3) fileHandlerOnError.first?(fileError) XCTAssertNoDifference(actions, [ .didLog(.error(fileError)) ]) actions = [] let backupData = "backup".data(using: .utf8)! backupCallback.first?.handle(backupData) XCTAssertNoDifference(actions, [ .didStoreBackup(backupData), ]) actions = [] store.send(.stop) XCTAssertNoDifference(actions, [ .didCancelAuthHandler, .didCancelMessageListener, .didCancelReceiveFileHandler, .didCancelGroupRequestHandler, .didCancelGroupMessageHandler, .didCancelBackupCallback, ]) } } private enum Action: Equatable { case didMakeDB case didStartAuthHandler case didStartMessageListener case didStartReceiveFileHandler case didRegisterBackupCallback case didLoadMessenger case didCancelAuthHandler case didCancelMessageListener case didCancelReceiveFileHandler case didCancelBackupCallback case didLog(Logger.Message) case didStoreBackup(Data) case didSetLogLevel(LogLevel) case didStartLogging case didStartGroupRequestHandler case didCancelGroupRequestHandler case didStartGroupMessageHandler case didCancelGroupMessageHandler }