Initialization refactored & covered by tests
This commit is contained in:
parent
88e31f6b14
commit
d0cca8121c
|
@ -102,6 +102,7 @@
|
||||||
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; };
|
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; };
|
||||||
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; };
|
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; };
|
||||||
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; };
|
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; };
|
||||||
|
9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppReducerTests.swift */; };
|
||||||
9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */; };
|
9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */; };
|
||||||
9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */; };
|
9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */; };
|
||||||
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */; };
|
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */; };
|
||||||
|
@ -256,6 +257,7 @@
|
||||||
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
|
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
|
||||||
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
|
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
|
||||||
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
|
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
|
||||||
|
9EAFEB812805793200199FC9 /* AppReducerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducerTests.swift; sourceTree = "<group>"; };
|
||||||
9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = "<group>"; };
|
9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = "<group>"; };
|
||||||
9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; };
|
9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; };
|
||||||
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = "<group>"; };
|
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = "<group>"; };
|
||||||
|
@ -451,6 +453,7 @@
|
||||||
0D4E7A1926B364180058B01E /* secantTests */ = {
|
0D4E7A1926B364180058B01E /* secantTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
9EAFEB802805791400199FC9 /* AppReducer */,
|
||||||
9EF8135927ECC25E0075AF48 /* Util */,
|
9EF8135927ECC25E0075AF48 /* Util */,
|
||||||
0DFE93E4272CB6D0000FCCA5 /* RecoveryPhraseValidationTests */,
|
0DFE93E4272CB6D0000FCCA5 /* RecoveryPhraseValidationTests */,
|
||||||
0DFE93DD272C6D4B000FCCA5 /* BackupFlowTests */,
|
0DFE93DD272C6D4B000FCCA5 /* BackupFlowTests */,
|
||||||
|
@ -754,6 +757,14 @@
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
9EAFEB802805791400199FC9 /* AppReducer */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9EAFEB812805793200199FC9 /* AppReducerTests.swift */,
|
||||||
|
);
|
||||||
|
path = AppReducer;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9EBEF87827CE365D00B4F343 /* Preamble */ = {
|
9EBEF87827CE365D00B4F343 /* Preamble */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1242,6 +1253,7 @@
|
||||||
6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */,
|
6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */,
|
||||||
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */,
|
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */,
|
||||||
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
||||||
|
9EAFEB822805793200199FC9 /* AppReducerTests.swift in Sources */,
|
||||||
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */,
|
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */,
|
||||||
0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */,
|
0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */,
|
||||||
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */,
|
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */,
|
||||||
|
|
|
@ -30,6 +30,7 @@ enum AppAction: Equatable {
|
||||||
case onboarding(OnboardingAction)
|
case onboarding(OnboardingAction)
|
||||||
case phraseDisplay(RecoveryPhraseDisplayAction)
|
case phraseDisplay(RecoveryPhraseDisplayAction)
|
||||||
case phraseValidation(RecoveryPhraseValidationAction)
|
case phraseValidation(RecoveryPhraseValidationAction)
|
||||||
|
case respondToWalletInitializationState(InitializationState)
|
||||||
case updateRoute(AppState.Route)
|
case updateRoute(AppState.Route)
|
||||||
case welcome(WelcomeAction)
|
case welcome(WelcomeAction)
|
||||||
}
|
}
|
||||||
|
@ -78,59 +79,33 @@ extension AppReducer {
|
||||||
|
|
||||||
private static let appReducer = AppReducer { state, action, environment in
|
private static let appReducer = AppReducer { state, action, environment in
|
||||||
switch action {
|
switch action {
|
||||||
case .createNewWallet:
|
case .appDelegate(.didFinishLaunching):
|
||||||
let randomPhraseWords: [String]
|
/// We need to fetch data from keychain, in order to be 100% sure the kecyhain can be read we delay the check a bit
|
||||||
do {
|
return Effect(value: .checkWalletInitialization)
|
||||||
let randomPhrase = try environment.mnemonicSeedPhraseProvider.randomMnemonic()
|
.delay(for: 0.02, scheduler: environment.scheduler)
|
||||||
randomPhraseWords = try environment.mnemonicSeedPhraseProvider.asWords(randomPhrase)
|
.eraseToEffect()
|
||||||
// TODO: - Get birthday from the integrated SDK, issue 228 (https://github.com/zcash/secant-ios-wallet/issues/228)
|
|
||||||
let birthday = BlockHeight(12345678)
|
|
||||||
|
|
||||||
try environment.walletStorage.importWallet(randomPhrase, birthday, .english, false)
|
/// Evaluate the wallet's state based on keychain keys and database files presence
|
||||||
} catch {
|
|
||||||
// TODO: - merge with issue 201 (https://github.com/zcash/secant-ios-wallet/issues/201) and its Error States
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
|
|
||||||
let recoveryPhrase = RecoveryPhrase(words: randomPhraseWords)
|
|
||||||
state.phraseDisplayState.phrase = recoveryPhrase
|
|
||||||
state.phraseValidationState = RecoveryPhraseValidationState.random(phrase: recoveryPhrase)
|
|
||||||
|
|
||||||
return Effect(value: .phraseValidation(.displayBackedUpPhrase))
|
|
||||||
|
|
||||||
/// Checking presense of stored wallet in the keychain and presense of database files in documents directory.
|
|
||||||
case .checkWalletInitialization:
|
case .checkWalletInitialization:
|
||||||
var keysPresent = false
|
let walletState = walletInitializationState(environment)
|
||||||
do {
|
return Effect(value: .respondToWalletInitializationState(walletState))
|
||||||
// TODO: replace the hardcoded network with the environmental value, issue 239 (https://github.com/zcash/secant-ios-wallet/issues/239)
|
|
||||||
keysPresent = try environment.walletStorage.areKeysPresent()
|
|
||||||
let databaseFilesPresent = try environment.databaseFiles.areDbFilesPresentFor("mainnet")
|
|
||||||
|
|
||||||
switch (keysPresent, databaseFilesPresent) {
|
/// Respond to all possible states of the wallet and initiate appropriate side effects including errors handling
|
||||||
case (false, false):
|
case .respondToWalletInitializationState(let walletState):
|
||||||
state.appInitializationState = .uninitialized
|
switch walletState {
|
||||||
case (false, true):
|
case .failed:
|
||||||
state.appInitializationState = .keysMissing
|
|
||||||
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
|
||||||
case (true, false), (true, true):
|
|
||||||
return Effect(value: .initializeApp)
|
|
||||||
}
|
|
||||||
} catch DatabaseFiles.DatabaseFilesError.filesPresentCheck {
|
|
||||||
if keysPresent {
|
|
||||||
/// This state is not an error as long as wallet keys are present. The process to initialize the missing files is handled by `.initializeApp` action.
|
|
||||||
state.appInitializationState = .filesMissing
|
|
||||||
return Effect(value: .initializeApp)
|
|
||||||
} else {
|
|
||||||
state.appInitializationState = .uninitialized
|
|
||||||
}
|
|
||||||
} catch WalletStorage.WalletStorageError.uninitializedWallet {
|
|
||||||
state.appInitializationState = .uninitialized
|
|
||||||
} catch {
|
|
||||||
state.appInitializationState = .failed
|
|
||||||
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||||
}
|
state.appInitializationState = .failed
|
||||||
|
case .initialized:
|
||||||
if state.appInitializationState == .uninitialized {
|
return Effect(value: .initializeApp)
|
||||||
|
case .keysMissing:
|
||||||
|
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||||
|
state.appInitializationState = .keysMissing
|
||||||
|
case .filesMissing:
|
||||||
|
state.appInitializationState = .filesMissing
|
||||||
|
return Effect(value: .initializeApp)
|
||||||
|
case .uninitialized:
|
||||||
|
state.appInitializationState = .uninitialized
|
||||||
return Effect(value: .updateRoute(.onboarding))
|
return Effect(value: .updateRoute(.onboarding))
|
||||||
.delay(for: 3, scheduler: environment.scheduler)
|
.delay(for: 3, scheduler: environment.scheduler)
|
||||||
.eraseToEffect()
|
.eraseToEffect()
|
||||||
|
@ -179,6 +154,30 @@ extension AppReducer {
|
||||||
.eraseToEffect()
|
.eraseToEffect()
|
||||||
.cancellable(id: ListenerId(), cancelInFlight: true)
|
.cancellable(id: ListenerId(), cancelInFlight: true)
|
||||||
|
|
||||||
|
case .createNewWallet:
|
||||||
|
do {
|
||||||
|
// get the random english mnemonic
|
||||||
|
let randomPhrase = try environment.mnemonicSeedPhraseProvider.randomMnemonic()
|
||||||
|
let randomPhraseWords = try environment.mnemonicSeedPhraseProvider.asWords(randomPhrase)
|
||||||
|
// TODO: - Get birthday from the integrated SDK, issue 228 (https://github.com/zcash/secant-ios-wallet/issues/228)
|
||||||
|
// get the latest block height
|
||||||
|
let birthday = BlockHeight(12345678)
|
||||||
|
|
||||||
|
// store the wallet to the keychain
|
||||||
|
try environment.walletStorage.importWallet(randomPhrase, birthday, .english, false)
|
||||||
|
|
||||||
|
// start the backup phrase validation test
|
||||||
|
let recoveryPhrase = RecoveryPhrase(words: randomPhraseWords)
|
||||||
|
state.phraseDisplayState.phrase = recoveryPhrase
|
||||||
|
state.phraseValidationState = RecoveryPhraseValidationState.random(phrase: recoveryPhrase)
|
||||||
|
|
||||||
|
return Effect(value: .phraseValidation(.displayBackedUpPhrase))
|
||||||
|
} catch {
|
||||||
|
// TODO: - merge with issue 201 (https://github.com/zcash/secant-ios-wallet/issues/201) and its Error States
|
||||||
|
}
|
||||||
|
|
||||||
|
return .none
|
||||||
|
|
||||||
case .phraseValidation(.succeed):
|
case .phraseValidation(.succeed):
|
||||||
do {
|
do {
|
||||||
try environment.walletStorage.markUserPassedPhraseBackupTest()
|
try environment.walletStorage.markUserPassedPhraseBackupTest()
|
||||||
|
@ -278,6 +277,47 @@ extension AppReducer {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - AppReducer Helper Functions
|
||||||
|
|
||||||
|
extension AppReducer {
|
||||||
|
static func walletInitializationState(_ environment: AppEnvironment) -> InitializationState {
|
||||||
|
var keysPresent = false
|
||||||
|
do {
|
||||||
|
keysPresent = try environment.walletStorage.areKeysPresent()
|
||||||
|
// TODO: replace the hardcoded network with the environmental value, issue 239 (https://github.com/zcash/secant-ios-wallet/issues/239)
|
||||||
|
let databaseFilesPresent = try environment.databaseFiles.areDbFilesPresentFor("mainnet")
|
||||||
|
|
||||||
|
switch (keysPresent, databaseFilesPresent) {
|
||||||
|
case (false, false):
|
||||||
|
return .uninitialized
|
||||||
|
case (false, true):
|
||||||
|
return .keysMissing
|
||||||
|
case (true, false):
|
||||||
|
return .filesMissing
|
||||||
|
case (true, true):
|
||||||
|
return .initialized
|
||||||
|
}
|
||||||
|
} catch DatabaseFiles.DatabaseFilesError.filesPresentCheck {
|
||||||
|
if keysPresent {
|
||||||
|
return .filesMissing
|
||||||
|
}
|
||||||
|
} catch WalletStorage.WalletStorageError.uninitializedWallet {
|
||||||
|
do {
|
||||||
|
// TODO: replace the hardcoded network with the environmental value, issue 239 (https://github.com/zcash/secant-ios-wallet/issues/239)
|
||||||
|
_ = try environment.databaseFiles.areDbFilesPresentFor("mainnet")
|
||||||
|
|
||||||
|
return .keysMissing
|
||||||
|
} catch {
|
||||||
|
return .uninitialized
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return .failed
|
||||||
|
}
|
||||||
|
|
||||||
|
return .uninitialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - AppStore
|
// MARK: - AppStore
|
||||||
|
|
||||||
typealias AppStore = Store<AppState, AppAction>
|
typealias AppStore = Store<AppState, AppAction>
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
//
|
||||||
|
// AppReducerTests.swift
|
||||||
|
// secantTests
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 12.04.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import secant_testnet
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
class AppReducerTests: XCTestCase {
|
||||||
|
static let testScheduler = DispatchQueue.test
|
||||||
|
|
||||||
|
let testEnvironment = AppEnvironment(
|
||||||
|
databaseFiles: .throwing,
|
||||||
|
scheduler: testScheduler.eraseToAnyScheduler(),
|
||||||
|
mnemonicSeedPhraseProvider: .mock,
|
||||||
|
walletStorage: .throwing
|
||||||
|
)
|
||||||
|
|
||||||
|
func testWalletInitializationState_Uninitialized() throws {
|
||||||
|
let uninitializedEnvironment = AppEnvironment(
|
||||||
|
databaseFiles: .throwing,
|
||||||
|
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
|
||||||
|
mnemonicSeedPhraseProvider: .mock,
|
||||||
|
walletStorage: .throwing
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletState = AppReducer.walletInitializationState(uninitializedEnvironment)
|
||||||
|
|
||||||
|
XCTAssertEqual(walletState, .uninitialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWalletInitializationState_KeysMissing() throws {
|
||||||
|
let wfmMock = WrappedFileManager(
|
||||||
|
url: { _, _, _, _ in URL(fileURLWithPath: "") },
|
||||||
|
fileExists: { _ in return true },
|
||||||
|
removeItem: { _ in }
|
||||||
|
)
|
||||||
|
|
||||||
|
let keysMissingEnvironment = AppEnvironment(
|
||||||
|
databaseFiles: .live(databaseFiles: DatabaseFiles(fileManager: wfmMock)),
|
||||||
|
scheduler: Self.testScheduler.eraseToAnyScheduler(),
|
||||||
|
mnemonicSeedPhraseProvider: .mock,
|
||||||
|
walletStorage: .throwing
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletState = AppReducer.walletInitializationState(keysMissingEnvironment)
|
||||||
|
|
||||||
|
XCTAssertEqual(walletState, .keysMissing)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: - Implement testWalletInitializationState_FilesMissing when WalletStorage mock is available, issue 231 (https://github.com/zcash/secant-ios-wallet/issues/231)
|
||||||
|
|
||||||
|
// TODO: - Implement testWalletInitializationState_Initialized when WalletStorage mock is available, issue 231 (https://github.com/zcash/secant-ios-wallet/issues/231)
|
||||||
|
|
||||||
|
func testRespondToWalletInitializationState_Uninitialized() throws {
|
||||||
|
let store = TestStore(
|
||||||
|
initialState: .placeholder,
|
||||||
|
reducer: AppReducer.default,
|
||||||
|
environment: testEnvironment
|
||||||
|
)
|
||||||
|
|
||||||
|
store.send(.respondToWalletInitializationState(.uninitialized))
|
||||||
|
|
||||||
|
Self.testScheduler.advance(by: 3)
|
||||||
|
|
||||||
|
store.receive(.updateRoute(.onboarding)) {
|
||||||
|
$0.route = .onboarding
|
||||||
|
$0.appInitializationState = .uninitialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRespondToWalletInitializationState_KeysMissing() throws {
|
||||||
|
let store = TestStore(
|
||||||
|
initialState: .placeholder,
|
||||||
|
reducer: AppReducer.default,
|
||||||
|
environment: testEnvironment
|
||||||
|
)
|
||||||
|
|
||||||
|
store.send(.respondToWalletInitializationState(.keysMissing)) { state in
|
||||||
|
state.appInitializationState = .keysMissing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRespondToWalletInitializationState_FilesMissing() throws {
|
||||||
|
let store = TestStore(
|
||||||
|
initialState: .placeholder,
|
||||||
|
reducer: AppReducer.default,
|
||||||
|
environment: testEnvironment
|
||||||
|
)
|
||||||
|
|
||||||
|
store.send(.respondToWalletInitializationState(.filesMissing)) { state in
|
||||||
|
state.appInitializationState = .filesMissing
|
||||||
|
}
|
||||||
|
|
||||||
|
store.receive(.initializeApp) { state in
|
||||||
|
// failed is expected because environment is throwing errors
|
||||||
|
state.appInitializationState = .failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRespondToWalletInitializationState_Initialized() throws {
|
||||||
|
let store = TestStore(
|
||||||
|
initialState: .placeholder,
|
||||||
|
reducer: AppReducer.default,
|
||||||
|
environment: testEnvironment
|
||||||
|
)
|
||||||
|
|
||||||
|
store.send(.respondToWalletInitializationState(.initialized))
|
||||||
|
|
||||||
|
store.receive(.initializeApp) { state in
|
||||||
|
// failed is expected because environment is throwing errors
|
||||||
|
state.appInitializationState = .failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue