Skip to content
Snippets Groups Projects
HomeFeatureTests.swift 15.9 KiB
Newer Older
import AppCore
import BackupFeature
import ComposableArchitecture
import ContactsFeature
Dariusz Rybicki's avatar
Dariusz Rybicki committed
import CustomDump
Dariusz Rybicki's avatar
Dariusz Rybicki committed
import RegisterFeature
import UserSearchFeature
import XCTest
Dariusz Rybicki's avatar
Dariusz Rybicki committed
import XXClient
import XXMessengerClient
@testable import HomeFeature

final class HomeFeatureTests: XCTestCase {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  func testMessengerStartUnregistered() {
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    var messengerDidStartWithTimeout: [Int] = []
    var messengerDidConnect = 0
    var messengerDidListenForMessages = 0
    var messengerDidStartFileTransfer = 0
    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
    store.environment.messenger.isConnected.run = { false }
    store.environment.messenger.connect.run = { messengerDidConnect += 1 }
    store.environment.messenger.isListeningForMessages.run = { false }
    store.environment.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 }
    store.environment.messenger.isFileTransferRunning.run = { false }
    store.environment.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 }
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.isLoggedIn.run = { false }
    store.environment.messenger.isRegistered.run = { false }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.messenger(.start))
Dariusz Rybicki's avatar
Dariusz Rybicki committed

    XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000])
    XCTAssertNoDifference(messengerDidConnect, 1)
    XCTAssertNoDifference(messengerDidListenForMessages, 1)
    XCTAssertNoDifference(messengerDidStartFileTransfer, 1)
    store.receive(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.didStartUnregistered)) {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      $0.register = RegisterState()
    }
  }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
  func testMessengerStartRegistered() {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    var messengerDidStartWithTimeout: [Int] = []
    var messengerDidConnect = 0
    var messengerDidListenForMessages = 0
    var messengerDidStartFileTransfer = 0
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    var messengerDidLogIn = 0
    var messengerDidResumeBackup = 0
    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
    store.environment.messenger.isConnected.run = { false }
    store.environment.messenger.connect.run = { messengerDidConnect += 1 }
    store.environment.messenger.isListeningForMessages.run = { false }
    store.environment.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 }
    store.environment.messenger.isFileTransferRunning.run = { false }
    store.environment.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 }
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.isLoggedIn.run = { false }
    store.environment.messenger.isRegistered.run = { true }
    store.environment.messenger.logIn.run = { messengerDidLogIn += 1 }
    store.environment.messenger.isBackupRunning.run = { false }
    store.environment.messenger.resumeBackup.run = { messengerDidResumeBackup += 1 }
    store.environment.messenger.cMix.get = {
      var cMix: CMix = .unimplemented
      cMix.addHealthCallback.run = { _ in Cancellable {} }
      cMix.getNodeRegistrationStatus.run = {
        struct Unimplemented: Error {}
        throw Unimplemented()
      }
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.messenger(.start))
Dariusz Rybicki's avatar
Dariusz Rybicki committed

    XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000])
    XCTAssertNoDifference(messengerDidConnect, 1)
    XCTAssertNoDifference(messengerDidListenForMessages, 1)
    XCTAssertNoDifference(messengerDidStartFileTransfer, 1)
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    XCTAssertNoDifference(messengerDidLogIn, 1)
    XCTAssertNoDifference(messengerDidResumeBackup, 1)
    store.receive(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.didStartRegistered))
    store.receive(.networkMonitor(.start))

    store.send(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  }

  func testRegisterFinished() {
    let store = TestStore(
      initialState: HomeState(
        register: RegisterState()
      ),
      reducer: homeReducer,
      environment: .unimplemented
    )

    var messengerDidStartWithTimeout: [Int] = []
    var messengerDidLogIn = 0

    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) }
    store.environment.messenger.isConnected.run = { true }
    store.environment.messenger.isListeningForMessages.run = { true }
    store.environment.messenger.isFileTransferRunning.run = { true }
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.isLoggedIn.run = { false }
    store.environment.messenger.isRegistered.run = { true }
    store.environment.messenger.logIn.run = { messengerDidLogIn += 1 }
    store.environment.messenger.isBackupRunning.run = { true }
    store.environment.messenger.cMix.get = {
      var cMix: CMix = .unimplemented
      cMix.addHealthCallback.run = { _ in Cancellable {} }
      cMix.getNodeRegistrationStatus.run = {
        struct Unimplemented: Error {}
        throw Unimplemented()
      }
Dariusz Rybicki's avatar
Dariusz Rybicki committed

    store.send(.register(.finished)) {
      $0.register = nil
    }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.start))
Dariusz Rybicki's avatar
Dariusz Rybicki committed

    XCTAssertNoDifference(messengerDidStartWithTimeout, [30_000])
    XCTAssertNoDifference(messengerDidLogIn, 1)

    store.receive(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.didStartRegistered))
    store.receive(.networkMonitor(.start))

    store.send(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  func testMessengerStartFailure() {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    struct Failure: Error {}
    let error = Failure()

    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
    store.environment.messenger.start.run = { _ in throw error }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.messenger(.start))
    store.receive(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.failure(error as NSError))) {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      $0.failure = error.localizedDescription
    }
  }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
  func testMessengerStartConnectFailure() {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    struct Failure: Error {}
    let error = Failure()

    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
    store.environment.messenger.start.run = { _ in }
    store.environment.messenger.isConnected.run = { false }
    store.environment.messenger.connect.run = { throw error }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.messenger(.start))
    store.receive(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.failure(error as NSError))) {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      $0.failure = error.localizedDescription
    }
  }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
  func testMessengerStartIsRegisteredFailure() {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    struct Failure: Error {}
    let error = Failure()

    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
    store.environment.messenger.start.run = { _ in }
    store.environment.messenger.isConnected.run = { true }
    store.environment.messenger.isListeningForMessages.run = { true }
    store.environment.messenger.isFileTransferRunning.run = { true }
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.isLoggedIn.run = { false }
    store.environment.messenger.isRegistered.run = { throw error }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.messenger(.start))
    store.receive(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.failure(error as NSError))) {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      $0.failure = error.localizedDescription
    }
  }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
  func testMessengerStartLogInFailure() {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    struct Failure: Error {}
    let error = Failure()

    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
    store.environment.messenger.start.run = { _ in }
    store.environment.messenger.isConnected.run = { true }
    store.environment.messenger.isListeningForMessages.run = { true }
    store.environment.messenger.isFileTransferRunning.run = { true }
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.environment.messenger.isLoggedIn.run = { false }
    store.environment.messenger.isRegistered.run = { true }
    store.environment.messenger.logIn.run = { throw error }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.messenger(.start))
    store.receive(.networkMonitor(.stop))
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.messenger(.failure(error as NSError))) {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      $0.failure = error.localizedDescription
    }
  func testNetworkMonitorStart() {
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    let bgQueue = DispatchQueue.test
    let mainQueue = DispatchQueue.test

    var cMixDidAddHealthCallback: [HealthCallback] = []
    var healthCallbackDidCancel = 0
    var nodeRegistrationStatusIndex = 0
    let nodeRegistrationStatus: [NodeRegistrationReport] = [
      .init(registered: 0, total: 10),
      .init(registered: 1, total: 11),
      .init(registered: 2, total: 12),
    ]

    store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
    store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
    store.environment.messenger.cMix.get = {
      var cMix: CMix = .unimplemented
      cMix.addHealthCallback.run = { callback in
        cMixDidAddHealthCallback.append(callback)
        return Cancellable { healthCallbackDidCancel += 1 }
      }
      cMix.getNodeRegistrationStatus.run = {
        defer { nodeRegistrationStatusIndex += 1 }
        return nodeRegistrationStatus[nodeRegistrationStatusIndex]
      }
      return cMix
    }

    store.send(.networkMonitor(.start))

    XCTAssertNoDifference(cMixDidAddHealthCallback.count, 1)

    cMixDidAddHealthCallback.first?.handle(true)
    mainQueue.advance()

    store.receive(.networkMonitor(.health(true))) {
      $0.isNetworkHealthy = true
    }

    cMixDidAddHealthCallback.first?.handle(false)
    mainQueue.advance()

    store.receive(.networkMonitor(.health(false))) {
      $0.isNetworkHealthy = false
    }

    bgQueue.advance(by: 2)
    mainQueue.advance()

    store.receive(.networkMonitor(.nodes(nodeRegistrationStatus[0]))) {
      $0.networkNodesReport = nodeRegistrationStatus[0]
    }

    bgQueue.advance(by: 2)
    mainQueue.advance()

    store.receive(.networkMonitor(.nodes(nodeRegistrationStatus[1]))) {
      $0.networkNodesReport = nodeRegistrationStatus[1]
    }

    bgQueue.advance(by: 2)
    mainQueue.advance()

    store.receive(.networkMonitor(.nodes(nodeRegistrationStatus[2]))) {
      $0.networkNodesReport = nodeRegistrationStatus[2]
    }

    store.send(.networkMonitor(.stop)) {
      $0.isNetworkHealthy = nil
      $0.networkNodesReport = nil
    }

    XCTAssertNoDifference(healthCallbackDidCancel, 1)
  func testAccountDeletion() {
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    var dbDidFetchContacts: [XXModels.Contact.Query] = []
    var udDidPermanentDeleteAccount: [Fact] = []
    var messengerDidDestroy = 0
    var didRemoveDB = 0

    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
    store.environment.messenger.e2e.get = {
      var e2e: E2E = .unimplemented
      e2e.getContact.run = {
        var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
        contact.getIdFromContact.run = { _ in "contact-id".data(using: .utf8)! }
        return contact
      }
      return e2e
    }
    store.environment.dbManager.getDB.run = {
      var db: Database = .unimplemented
      db.fetchContacts.run = { query in
        dbDidFetchContacts.append(query)
        return [
          XXModels.Contact(
            id: "contact-id".data(using: .utf8)!,
            marshaled: "contact-data".data(using: .utf8)!,
            username: "MyUsername"
          )
        ]
      }
      return db
    }
    store.environment.dbManager.removeDB.run = {
      didRemoveDB += 1
    }
    store.environment.messenger.ud.get = {
      var ud: UserDiscovery = .unimplemented
      ud.permanentDeleteAccount.run = { usernameFact in
        udDidPermanentDeleteAccount.append(usernameFact)
      }
      return ud
    }
    store.environment.messenger.destroy.run = {
      messengerDidDestroy += 1
    }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.deleteAccount(.buttonTapped)) {
      $0.alert = .confirmAccountDeletion()
    }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.didDismissAlert) {
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.deleteAccount(.confirmed)) {
      $0.isDeletingAccount = true
    }

    XCTAssertNoDifference(dbDidFetchContacts, [.init(id: ["contact-id".data(using: .utf8)!])])
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    XCTAssertNoDifference(udDidPermanentDeleteAccount, [Fact(type: .username, value: "MyUsername")])
    XCTAssertNoDifference(messengerDidDestroy, 1)
    XCTAssertNoDifference(didRemoveDB, 1)
Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.deleteAccount(.success)) {
      $0.isDeletingAccount = false
    }
  }

  func testAccountDeletionFailure() {
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    struct Failure: Error {}
    let error = Failure()

    store.environment.bgQueue = .immediate
    store.environment.mainQueue = .immediate
    store.environment.messenger.e2e.get = {
      var e2e: E2E = .unimplemented
      e2e.getContact.run = {
        var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
        contact.getIdFromContact.run = { _ in throw error }
        return contact
      }
      return e2e
    }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.send(.deleteAccount(.confirmed)) {
      $0.isDeletingAccount = true
    }

Dariusz Rybicki's avatar
Dariusz Rybicki committed
    store.receive(.deleteAccount(.failure(error as NSError))) {
      $0.isDeletingAccount = false
Dariusz Rybicki's avatar
Dariusz Rybicki committed
      $0.alert = .accountDeletionFailed(error)
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  }
Dariusz Rybicki's avatar
Dariusz Rybicki committed
  func testDidDismissAlert() {
    let store = TestStore(
      initialState: HomeState(
        alert: AlertState(title: TextState(""))
      ),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.didDismissAlert) {
      $0.alert = nil
    }
  }

  func testDidDismissRegister() {
    let store = TestStore(
      initialState: HomeState(
        register: RegisterState()
      ),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.didDismissRegister) {
      $0.register = nil

  func testUserSearchButtonTapped() {
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.userSearchButtonTapped) {
      $0.userSearch = UserSearchState()
    }
  }

  func testDidDismissUserSearch() {
    let store = TestStore(
      initialState: HomeState(
        userSearch: UserSearchState()
      ),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.didDismissUserSearch) {
      $0.userSearch = nil
    }
  }

  func testContactsButtonTapped() {
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.contactsButtonTapped) {
      $0.contacts = ContactsState()
    }
  }

  func testDidDismissContacts() {
    let store = TestStore(
      initialState: HomeState(
        contacts: ContactsState()
      ),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.didDismissContacts) {
      $0.contacts = nil
    }
  }

  func testBackupButtonTapped() {
    let store = TestStore(
      initialState: HomeState(),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.backupButtonTapped) {
      $0.backup = BackupState()
    }
  }

  func testDidDismissBackup() {
    let store = TestStore(
      initialState: HomeState(
        backup: BackupState()
      ),
      reducer: homeReducer,
      environment: .unimplemented
    )

    store.send(.didDismissBackup) {
      $0.backup = nil
    }
  }