diff --git a/.swiftlint.yml b/.swiftlint.yml index 40798e96..0d01815a 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -149,6 +149,7 @@ identifier_name: - nf - tx - as + - di indentation_width: indentation_width: 4 diff --git a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift index 3f3893fa..b290741f 100644 --- a/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift +++ b/Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift @@ -330,41 +330,22 @@ actor CompactBlockProcessor { /// - backend: a class that complies to `ZcashRustBackendWelding` /// - config: `Configuration` struct for this processor init( - service: LightWalletService, - storage: CompactBlockRepository, - rustBackend: ZcashRustBackendWelding, - config: Configuration, - metrics: SDKMetrics, - logger: Logger, - latestBlocksDataProvider: LatestBlocksDataProvider + container: DIContainer, + config: Configuration ) { self.init( - service: service, - storage: storage, - rustBackend: rustBackend, + container: container, config: config, - repository: TransactionRepositoryBuilder.build(dataDbURL: config.dataDb), - accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true, logger: logger), - metrics: metrics, - logger: logger, - latestBlocksDataProvider: latestBlocksDataProvider + accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true, logger: container.resolve(Logger.self)) ) } /// Initializes a CompactBlockProcessor instance from an Initialized object /// - Parameters: /// - initializer: an instance that complies to CompactBlockDownloading protocol - init( - initializer: Initializer, - metrics: SDKMetrics, - logger: Logger, - latestBlocksDataProvider: LatestBlocksDataProvider, - walletBirthdayProvider: @escaping () -> BlockHeight - ) { + init(initializer: Initializer, walletBirthdayProvider: @escaping () -> BlockHeight) { self.init( - service: initializer.lightWalletService, - storage: initializer.storage, - rustBackend: initializer.rustBackend, + container: initializer.container, config: Configuration( alias: initializer.alias, fsBlockCacheRoot: initializer.fsBlockDbRoot, @@ -375,97 +356,37 @@ actor CompactBlockProcessor { walletBirthdayProvider: walletBirthdayProvider, network: initializer.network ), - repository: initializer.transactionRepository, - accountRepository: initializer.accountRepository, - metrics: metrics, - logger: logger, - latestBlocksDataProvider: latestBlocksDataProvider + accountRepository: initializer.accountRepository ) } internal init( - service: LightWalletService, - storage: CompactBlockRepository, - rustBackend: ZcashRustBackendWelding, + container: DIContainer, config: Configuration, - repository: TransactionRepository, - accountRepository: AccountRepository, - metrics: SDKMetrics, - logger: Logger, - latestBlocksDataProvider: LatestBlocksDataProvider + accountRepository: AccountRepository ) { - self.metrics = metrics - self.logger = logger - self.latestBlocksDataProvider = latestBlocksDataProvider - let internalSyncProgress = InternalSyncProgress(alias: config.alias, storage: UserDefaults.standard, logger: logger) - self.internalSyncProgress = internalSyncProgress - let blockDownloaderService = BlockDownloaderServiceImpl(service: service, storage: storage) - self.blockDownloader = BlockDownloaderImpl( - service: service, - downloaderService: blockDownloaderService, - storage: storage, - internalSyncProgress: internalSyncProgress, - metrics: metrics, - logger: logger + Dependencies.setupCompactBlockProcessor( + in: container, + config: config, + accountRepository: accountRepository ) - self.blockDownloaderService = blockDownloaderService - - self.blockValidator = BlockValidatorImpl( - rustBackend: rustBackend, - metrics: metrics, - logger: logger - ) - - let blockScannerConfig = BlockScannerConfig( - networkType: config.network.networkType, - scanningBatchSize: config.scanningBatchSize - ) - self.blockScanner = BlockScannerImpl( - config: blockScannerConfig, - rustBackend: rustBackend, - transactionRepository: repository, - metrics: metrics, - logger: logger, - latestBlocksDataProvider: latestBlocksDataProvider - ) - - self.blockEnhancer = BlockEnhancerImpl( - blockDownloaderService: blockDownloaderService, - internalSyncProgress: internalSyncProgress, - rustBackend: rustBackend, - transactionRepository: repository, - metrics: metrics, - logger: logger - ) - - let utxoFetcherConfig = UTXOFetcherConfig(walletBirthdayProvider: config.walletBirthdayProvider) - self.utxoFetcher = UTXOFetcherImpl( - accountRepository: accountRepository, - blockDownloaderService: blockDownloaderService, - config: utxoFetcherConfig, - internalSyncProgress: internalSyncProgress, - rustBackend: rustBackend, - metrics: metrics, - logger: logger - ) - - let saplingParametersHandlerConfig = SaplingParametersHandlerConfig( - outputParamsURL: config.outputParamsURL, - spendParamsURL: config.spendParamsURL, - saplingParamsSourceURL: config.saplingParamsSourceURL - ) - self.saplingParametersHandler = SaplingParametersHandlerImpl( - config: saplingParametersHandlerConfig, - rustBackend: rustBackend, - logger: logger - ) - - self.service = service - self.rustBackend = rustBackend - self.storage = storage + self.metrics = container.resolve(SDKMetrics.self) + self.logger = container.resolve(Logger.self) + self.latestBlocksDataProvider = container.resolve(LatestBlocksDataProvider.self) + self.internalSyncProgress = container.resolve(InternalSyncProgress.self) + self.blockDownloaderService = container.resolve(BlockDownloaderService.self) + self.blockDownloader = container.resolve(BlockDownloader.self) + self.blockValidator = container.resolve(BlockValidator.self) + self.blockScanner = container.resolve(BlockScanner.self) + self.blockEnhancer = container.resolve(BlockEnhancer.self) + self.utxoFetcher = container.resolve(UTXOFetcher.self) + self.saplingParametersHandler = container.resolve(SaplingParametersHandler.self) + self.service = container.resolve(LightWalletService.self) + self.rustBackend = container.resolve(ZcashRustBackendWelding.self) + self.storage = container.resolve(CompactBlockRepository.self) self.config = config - self.transactionRepository = repository + self.transactionRepository = container.resolve(TransactionRepository.self) self.accountRepository = accountRepository } diff --git a/Sources/ZcashLightClientKit/Initializer.swift b/Sources/ZcashLightClientKit/Initializer.swift index 5a7e0e7b..cdc47879 100644 --- a/Sources/ZcashLightClientKit/Initializer.swift +++ b/Sources/ZcashLightClientKit/Initializer.swift @@ -85,6 +85,7 @@ class initializes the Rust backend and the supporting data required to exercise The [cash.z.wallet.sdk.block.CompactBlockProcessor] handles all the remaining Rust backend functionality, related to processing blocks. */ +// swiftlint:disable:next type_body_length public class Initializer { struct URLs { let fsBlockDbRoot: URL @@ -107,6 +108,7 @@ public class Initializer { // This is used to uniquely identify instance of the SDKSynchronizer. It's used when checking if the Alias is already used or not. let id = UUID() + let container: DIContainer let alias: ZcashSynchronizerAlias let endpoint: LightWalletEndpoint let fsBlockDbRoot: URL @@ -156,87 +158,94 @@ public class Initializer { alias: ZcashSynchronizerAlias = .default, loggingPolicy: LoggingPolicy = .default(.debug) ) { - let urls = URLs( - fsBlockDbRoot: fsBlockDbRoot, - dataDbURL: dataDbURL, - spendParamsURL: spendParamsURL, - outputParamsURL: outputParamsURL - ) - + let container = DIContainer() + // It's not possible to fail from constructor. Technically it's possible but it can be pain for the client apps to handle errors thrown // from constructor. So `parsingError` is just stored in initializer and `SDKSynchronizer.prepare()` throw this error if it exists. - let (updatedURLs, parsingError) = Self.tryToUpdateURLs(with: alias, urls: urls) - - let logger: Logger - switch loggingPolicy { - case let .default(logLevel): - logger = OSLogger(logLevel: logLevel, alias: alias) - case let .custom(customLogger): - logger = customLogger - case .noLogging: - logger = NullLogger() - } - - let rustBackend = ZcashRustBackend( - dbData: updatedURLs.dataDbURL, - fsBlockDbRoot: updatedURLs.fsBlockDbRoot, - spendParamsPath: updatedURLs.spendParamsURL, - outputParamsPath: updatedURLs.outputParamsURL, - networkType: network.networkType - ) - - self.init( - rustBackend: rustBackend, + let (updatedURLs, parsingError) = Self.setup( + container: container, + cacheDbURL: cacheDbURL, + fsBlockDbRoot: fsBlockDbRoot, + dataDbURL: dataDbURL, + endpoint: endpoint, network: network, + spendParamsURL: spendParamsURL, + outputParamsURL: outputParamsURL, + saplingParamsSourceURL: saplingParamsSourceURL, + alias: alias, + loggingPolicy: loggingPolicy + ) + + self.init( + container: container, cacheDbURL: cacheDbURL, urls: updatedURLs, endpoint: endpoint, - service: Self.makeLightWalletServiceFactory(endpoint: endpoint).make(), - repository: TransactionRepositoryBuilder.build(dataDbURL: updatedURLs.dataDbURL), - accountRepository: AccountRepositoryBuilder.build( - dataDbURL: updatedURLs.dataDbURL, - readOnly: true, - caching: true, - logger: logger - ), - storage: FSCompactBlockRepository( - fsBlockDbRoot: updatedURLs.fsBlockDbRoot, - metadataStore: .live( - fsBlockDbRoot: updatedURLs.fsBlockDbRoot, - rustBackend: rustBackend, - logger: logger - ), - blockDescriptor: .live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ), + network: network, saplingParamsSourceURL: saplingParamsSourceURL, alias: alias, urlsParsingError: parsingError, - logger: logger + loggingPolicy: loggingPolicy ) } /// Internal for dependency injection purposes. - /// - /// !!! It's expected that URLs put here are already update with the Alias. - init( - rustBackend: ZcashRustBackendWelding, + convenience init( + container: DIContainer, + cacheDbURL: URL?, + fsBlockDbRoot: URL, + dataDbURL: URL, + endpoint: LightWalletEndpoint, network: ZcashNetwork, + spendParamsURL: URL, + outputParamsURL: URL, + saplingParamsSourceURL: SaplingParamsSourceURL, + alias: ZcashSynchronizerAlias = .default, + loggingPolicy: LoggingPolicy = .default(.debug) + ) { + // It's not possible to fail from constructor. Technically it's possible but it can be pain for the client apps to handle errors thrown + // from constructor. So `parsingError` is just stored in initializer and `SDKSynchronizer.prepare()` throw this error if it exists. + let (updatedURLs, parsingError) = Self.setup( + container: container, + cacheDbURL: cacheDbURL, + fsBlockDbRoot: fsBlockDbRoot, + dataDbURL: dataDbURL, + endpoint: endpoint, + network: network, + spendParamsURL: spendParamsURL, + outputParamsURL: outputParamsURL, + saplingParamsSourceURL: saplingParamsSourceURL, + alias: alias, + loggingPolicy: loggingPolicy + ) + + self.init( + container: container, + cacheDbURL: cacheDbURL, + urls: updatedURLs, + endpoint: endpoint, + network: network, + saplingParamsSourceURL: saplingParamsSourceURL, + alias: alias, + urlsParsingError: parsingError, + loggingPolicy: loggingPolicy + ) + } + + private init( + container: DIContainer, cacheDbURL: URL?, urls: URLs, endpoint: LightWalletEndpoint, - service: LightWalletService, - repository: TransactionRepository, - accountRepository: AccountRepository, - storage: CompactBlockRepository, + network: ZcashNetwork, saplingParamsSourceURL: SaplingParamsSourceURL, alias: ZcashSynchronizerAlias, urlsParsingError: ZcashError?, - logger: Logger + loggingPolicy: LoggingPolicy = .default(.debug) ) { + self.container = container self.cacheDbURL = cacheDbURL - self.rustBackend = rustBackend + self.rustBackend = container.resolve(ZcashRustBackendWelding.self) self.fsBlockDbRoot = urls.fsBlockDbRoot self.dataDbURL = urls.dataDbURL self.endpoint = endpoint @@ -244,20 +253,62 @@ public class Initializer { self.outputParamsURL = urls.outputParamsURL self.saplingParamsSourceURL = saplingParamsSourceURL self.alias = alias - self.lightWalletService = service - self.transactionRepository = repository - self.accountRepository = accountRepository - self.storage = storage - self.blockDownloaderService = BlockDownloaderServiceImpl(service: service, storage: storage) + self.lightWalletService = container.resolve(LightWalletService.self) + self.transactionRepository = container.resolve(TransactionRepository.self) + self.accountRepository = AccountRepositoryBuilder.build( + dataDbURL: urls.dataDbURL, + readOnly: true, + caching: true, + logger: container.resolve(Logger.self) + ) + self.storage = container.resolve(CompactBlockRepository.self) + self.blockDownloaderService = container.resolve(BlockDownloaderService.self) self.network = network self.walletBirthday = Checkpoint.birthday(with: 0, network: network).height self.urlsParsingError = urlsParsingError - self.logger = logger + self.logger = container.resolve(Logger.self) } - - static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory { + + private static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory { return LightWalletServiceFactory(endpoint: endpoint) } + + // swiftlint:disable:next function_parameter_count + private static func setup( + container: DIContainer, + cacheDbURL: URL?, + fsBlockDbRoot: URL, + dataDbURL: URL, + endpoint: LightWalletEndpoint, + network: ZcashNetwork, + spendParamsURL: URL, + outputParamsURL: URL, + saplingParamsSourceURL: SaplingParamsSourceURL, + alias: ZcashSynchronizerAlias, + loggingPolicy: LoggingPolicy = .default(.debug) + ) -> (URLs, ZcashError?) { + let urls = URLs( + fsBlockDbRoot: fsBlockDbRoot, + dataDbURL: dataDbURL, + spendParamsURL: spendParamsURL, + outputParamsURL: outputParamsURL + ) + + // It's not possible to fail from constructor. Technically it's possible but it can be pain for the client apps to handle errors thrown + // from constructor. So `parsingError` is just stored in initializer and `SDKSynchronizer.prepare()` throw this error if it exists. + let (updatedURLs, parsingError) = Self.tryToUpdateURLs(with: alias, urls: urls) + + Dependencies.setup( + in: container, + urls: updatedURLs, + alias: alias, + networkType: network.networkType, + endpoint: endpoint, + loggingPolicy: loggingPolicy + ) + + return (updatedURLs, parsingError) + } /// Try to update URLs with `alias`. /// diff --git a/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift b/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift new file mode 100644 index 00000000..d51963de --- /dev/null +++ b/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift @@ -0,0 +1,208 @@ +// +// Dependencies.swift +// +// +// Created by Michal Fousek on 01.05.2023. +// + +import Foundation + +enum Dependencies { + // swiftlint:disable:next function_parameter_count + static func setup( + in container: DIContainer, + urls: Initializer.URLs, + alias: ZcashSynchronizerAlias, + networkType: NetworkType, + endpoint: LightWalletEndpoint, + loggingPolicy: Initializer.LoggingPolicy = .default(.debug) + ) { + container.register(type: Logger.self, isSingleton: true) { _ in + let logger: Logger + switch loggingPolicy { + case let .default(logLevel): + logger = OSLogger(logLevel: logLevel, alias: alias) + case let .custom(customLogger): + logger = customLogger + case .noLogging: + logger = NullLogger() + } + + return logger + } + + container.register(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in + return ZcashRustBackend( + dbData: urls.dataDbURL, + fsBlockDbRoot: urls.fsBlockDbRoot, + spendParamsPath: urls.spendParamsURL, + outputParamsPath: urls.outputParamsURL, + networkType: networkType + ) + } + + container.register(type: LightWalletService.self, isSingleton: true) { _ in + return LightWalletGRPCService(endpoint: endpoint) + } + + container.register(type: TransactionRepository.self, isSingleton: true) { _ in + TransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: urls.dataDbURL.path, readonly: true)) + } + + container.register(type: CompactBlockRepository.self, isSingleton: true) { di in + let logger = di.resolve(Logger.self) + let rustBackend = di.resolve(ZcashRustBackendWelding.self) + + return FSCompactBlockRepository( + fsBlockDbRoot: urls.fsBlockDbRoot, + metadataStore: .live( + fsBlockDbRoot: urls.fsBlockDbRoot, + rustBackend: rustBackend, + logger: logger + ), + blockDescriptor: .live, + contentProvider: DirectoryListingProviders.defaultSorted, + logger: logger + ) + } + + container.register(type: BlockDownloaderService.self, isSingleton: true) { di in + let service = di.resolve(LightWalletService.self) + let storage = di.resolve(CompactBlockRepository.self) + + return BlockDownloaderServiceImpl(service: service, storage: storage) + } + + container.register(type: SDKMetrics.self, isSingleton: true) { _ in + SDKMetrics() + } + + container.register(type: LatestBlocksDataProvider.self, isSingleton: true) { di in + let service = di.resolve(LightWalletService.self) + let transactionRepository = di.resolve(TransactionRepository.self) + + return LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository) + } + + container.register(type: SyncSessionIDGenerator.self, isSingleton: false) { _ in + UniqueSyncSessionIDGenerator() + } + + container.register(type: InternalSyncProgress.self, isSingleton: true) { di in + let logger = di.resolve(Logger.self) + return InternalSyncProgress(alias: alias, storage: UserDefaults.standard, logger: logger) + } + } + + static func setupCompactBlockProcessor( + in container: DIContainer, + config: CompactBlockProcessor.Configuration, + accountRepository: AccountRepository + ) { + container.register(type: BlockDownloader.self, isSingleton: true) { di in + let service = di.resolve(LightWalletService.self) + let blockDownloaderService = di.resolve(BlockDownloaderService.self) + let storage = di.resolve(CompactBlockRepository.self) + let internalSyncProgress = di.resolve(InternalSyncProgress.self) + let metrics = di.resolve(SDKMetrics.self) + let logger = di.resolve(Logger.self) + + return BlockDownloaderImpl( + service: service, + downloaderService: blockDownloaderService, + storage: storage, + internalSyncProgress: internalSyncProgress, + metrics: metrics, + logger: logger + ) + } + + container.register(type: BlockValidator.self, isSingleton: true) { di in + let rustBackend = di.resolve(ZcashRustBackendWelding.self) + let metrics = di.resolve(SDKMetrics.self) + let logger = di.resolve(Logger.self) + + return BlockValidatorImpl( + rustBackend: rustBackend, + metrics: metrics, + logger: logger + ) + } + + container.register(type: BlockScanner.self, isSingleton: true) { di in + let rustBackend = di.resolve(ZcashRustBackendWelding.self) + let transactionRepository = di.resolve(TransactionRepository.self) + let metrics = di.resolve(SDKMetrics.self) + let logger = di.resolve(Logger.self) + let latestBlocksDataProvider = di.resolve(LatestBlocksDataProvider.self) + + let blockScannerConfig = BlockScannerConfig( + networkType: config.network.networkType, + scanningBatchSize: config.scanningBatchSize + ) + + return BlockScannerImpl( + config: blockScannerConfig, + rustBackend: rustBackend, + transactionRepository: transactionRepository, + metrics: metrics, + logger: logger, + latestBlocksDataProvider: latestBlocksDataProvider + ) + } + + container.register(type: BlockEnhancer.self, isSingleton: true) { di in + let blockDownloaderService = di.resolve(BlockDownloaderService.self) + let internalSyncProgress = di.resolve(InternalSyncProgress.self) + let rustBackend = di.resolve(ZcashRustBackendWelding.self) + let transactionRepository = di.resolve(TransactionRepository.self) + let metrics = di.resolve(SDKMetrics.self) + let logger = di.resolve(Logger.self) + + return BlockEnhancerImpl( + blockDownloaderService: blockDownloaderService, + internalSyncProgress: internalSyncProgress, + rustBackend: rustBackend, + transactionRepository: transactionRepository, + metrics: metrics, + logger: logger + ) + } + + container.register(type: UTXOFetcher.self, isSingleton: true) { di in + let blockDownloaderService = di.resolve(BlockDownloaderService.self) + let utxoFetcherConfig = UTXOFetcherConfig(walletBirthdayProvider: config.walletBirthdayProvider) + let internalSyncProgress = di.resolve(InternalSyncProgress.self) + let rustBackend = di.resolve(ZcashRustBackendWelding.self) + let metrics = di.resolve(SDKMetrics.self) + let logger = di.resolve(Logger.self) + + return UTXOFetcherImpl( + accountRepository: accountRepository, + blockDownloaderService: blockDownloaderService, + config: utxoFetcherConfig, + internalSyncProgress: internalSyncProgress, + rustBackend: rustBackend, + metrics: metrics, + logger: logger + ) + } + + container.register(type: SaplingParametersHandler.self, isSingleton: true) { di in + let rustBackend = di.resolve(ZcashRustBackendWelding.self) + let logger = di.resolve(Logger.self) + + let saplingParametersHandlerConfig = SaplingParametersHandlerConfig( + outputParamsURL: config.outputParamsURL, + spendParamsURL: config.spendParamsURL, + saplingParamsSourceURL: config.saplingParamsSourceURL + ) + + return SaplingParametersHandlerImpl( + config: saplingParametersHandlerConfig, + rustBackend: rustBackend, + logger: logger + ) + } + } +} diff --git a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift index 07b9805b..bc60cc2f 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift @@ -50,11 +50,6 @@ public class SDKSynchronizer: Synchronizer { /// Creates an SDKSynchronizer instance /// - Parameter initializer: a wallet Initializer object public convenience init(initializer: Initializer) { - let metrics = SDKMetrics() - let latestBlocksDataProvider = LatestBlocksDataProviderImpl( - service: initializer.lightWalletService, - transactionRepository: initializer.transactionRepository - ) self.init( status: .unprepared, initializer: initializer, @@ -63,15 +58,9 @@ public class SDKSynchronizer: Synchronizer { utxoRepository: UTXORepositoryBuilder.build(initializer: initializer), blockProcessor: CompactBlockProcessor( initializer: initializer, - metrics: metrics, - logger: initializer.logger, - latestBlocksDataProvider: latestBlocksDataProvider, walletBirthdayProvider: { initializer.walletBirthday } ), - metrics: metrics, - syncSessionIDGenerator: UniqueSyncSessionIDGenerator(), - syncSessionTicker: .live, - latestBlocksDataProvider: latestBlocksDataProvider + syncSessionTicker: .live ) } @@ -82,10 +71,7 @@ public class SDKSynchronizer: Synchronizer { transactionRepository: TransactionRepository, utxoRepository: UnspentTransactionOutputRepository, blockProcessor: CompactBlockProcessor, - metrics: SDKMetrics, - syncSessionIDGenerator: SyncSessionIDGenerator, - syncSessionTicker: SessionTicker, - latestBlocksDataProvider: LatestBlocksDataProvider + syncSessionTicker: SessionTicker ) { self.connectionState = .idle self.underlyingStatus = GenericActor(status) @@ -95,12 +81,12 @@ public class SDKSynchronizer: Synchronizer { self.utxoRepository = utxoRepository self.blockProcessor = blockProcessor self.network = initializer.network - self.metrics = metrics + self.metrics = initializer.container.resolve(SDKMetrics.self) self.logger = initializer.logger - self.syncSessionIDGenerator = syncSessionIDGenerator + self.syncSessionIDGenerator = initializer.container.resolve(SyncSessionIDGenerator.self) self.syncSession = SyncSession(.nullID) self.syncSessionTicker = syncSessionTicker - self.latestBlocksDataProvider = latestBlocksDataProvider + self.latestBlocksDataProvider = initializer.container.resolve(LatestBlocksDataProvider.self) initializer.lightWalletService.connectionStateChange = { [weak self] oldState, newState in self?.connectivityStateChanged(oldState: oldState, newState: newState) diff --git a/Sources/ZcashLightClientKit/Utils/DIContainer.swift b/Sources/ZcashLightClientKit/Utils/DIContainer.swift new file mode 100644 index 00000000..d8c431d6 --- /dev/null +++ b/Sources/ZcashLightClientKit/Utils/DIContainer.swift @@ -0,0 +1,86 @@ +// +// DIContainer.swift +// +// +// Created by Michal Fousek on 01.05.2023. +// + +import Foundation + +/// This class represents depedency injection containers. +class DIContainer { + /// Structure that represents one registered dependency. + struct Dependency { + /// Closure which creates instance of the dependency. + let factory: (DIContainer) -> Any + /// Indicates if dependency is singleton. If this is `true` then `DIContainer` creates only one instance of this dependency and returns + /// it every time `resolve()` for the dependency is called. + let isSingleton: Bool + /// If the dependency is singleton then instance is stored here. + let instance: Any? + } + + /// If this is `true` then `mockedDependencies` is used first to resolve dependencies. This is used for mocking in tests. + var isTestEnvironment = false + + private let lock = NSRecursiveLock() + /// Dependencies are stored here. + private var dependencies: [String: Dependency] = [:] + /// Mocked dependencies are stored here. + private var mockedDependencies: [String: Dependency] = [:] + + init() { } + + private func key(for type: T.Type) -> String { + return String(describing: T.self) + } + + func register(type: T.Type, isSingleton: Bool, factory: @escaping (DIContainer) -> T) { + lock.lock() + let key = self.key(for: type) + let depedency = Dependency(factory: factory, isSingleton: isSingleton, instance: nil) + dependencies[key] = depedency + lock.unlock() + } + + func mock(type: T.Type, isSingleton: Bool, factory: @escaping (DIContainer) -> T) { + lock.lock() + let key = self.key(for: type) + let depedency = Dependency(factory: factory, isSingleton: isSingleton, instance: nil) + mockedDependencies[key] = depedency + lock.unlock() + } + + func resolve(_ type: T.Type) -> T { + lock.lock() + defer { lock.unlock() } + let key = self.key(for: type) + + let possibleDependency = (isTestEnvironment ? mockedDependencies[key] : dependencies[key]) ?? dependencies[key] + guard let dependency = possibleDependency else { + // When dependency is resolved before it's registered then the app crashes. It would be possible to not crash and throw some error here. + // But it complicates the rest of the code. And maybe it doesn't make sense because this kind of error is not recoverable. It can be fixed + // only by updating the code. + fatalError("Doesn't have registered dependency for type \(type).") + } + + let instance: Any + if dependency.isSingleton, let singleton = dependency.instance { + instance = singleton + } else { + instance = dependency.factory(self) + } + + if dependency.isSingleton && dependency.instance == nil { + dependencies[key] = Dependency(factory: dependency.factory, isSingleton: dependency.isSingleton, instance: instance) + } + + guard let instance = instance as? T else { + // When dependency is resolved but instance of created depedency is different than expected type the app crashes. It would be possible to + // not crash and throw some error here. But it complicates the rest of the code. And maybe it doesn't make sense because this kind of + // error is not recoverable. It can be fixed only by updating the code. + fatalError("Getting dependency for type \(type) but created instance is \(instance)") + } + return instance + } +} diff --git a/Tests/AliasDarksideTests/SDKSynchronizerAliasDarksideTests.swift b/Tests/AliasDarksideTests/SDKSynchronizerAliasDarksideTests.swift index 7f373706..76dd3b3a 100644 --- a/Tests/AliasDarksideTests/SDKSynchronizerAliasDarksideTests.swift +++ b/Tests/AliasDarksideTests/SDKSynchronizerAliasDarksideTests.swift @@ -25,7 +25,7 @@ import XCTest 5. Run this test. 6. When you are done use `kill_and_clean_servers.zsh` script to shutdown servers and clean all the data (pidfile, rundirs, logs). */ -class SDKSynchronizerAliasDarksideTests: XCTestCase { +class SDKSynchronizerAliasDarksideTests: ZcashTestCase { // Test creates instance of the `SDKSynchronizer` for each of these aliases. let aliases: [ZcashSynchronizerAlias] = [.default, .custom("custom-1"), .custom("custom-2"), .custom("custom-3"), .custom("custom-4")] // First instance of the `SDKSynchronizer` uses this port. Second one uses startPort + 1, thirs one uses startPort + 2 and so on. @@ -52,6 +52,7 @@ class SDKSynchronizerAliasDarksideTests: XCTestCase { let coordinator = try await TestCoordinator( alias: alias, + container: mockContainer, walletBirthday: birthday, network: network, callPrepareInConstructor: true, diff --git a/Tests/DarksideTests/AdvancedReOrgTests.swift b/Tests/DarksideTests/AdvancedReOrgTests.swift index bfe9543f..7bb5adf6 100644 --- a/Tests/DarksideTests/AdvancedReOrgTests.swift +++ b/Tests/DarksideTests/AdvancedReOrgTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class AdvancedReOrgTests: XCTestCase { +class AdvancedReOrgTests: ZcashTestCase { let sendAmount = Zatoshi(1000) var birthday: BlockHeight = 663150 let defaultLatestHeight: BlockHeight = 663175 @@ -29,11 +29,10 @@ class AdvancedReOrgTests: XCTestCase { try await super.setUp() // don't use an exact birthday, users never do. self.coordinator = try await TestCoordinator( + container: mockContainer, walletBirthday: birthday + 50, - network: network, - dbTracingClosure: { logger.debug($0) } + network: network ) - try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) } diff --git a/Tests/DarksideTests/BalanceTests.swift b/Tests/DarksideTests/BalanceTests.swift index 78c0e169..c95f181b 100644 --- a/Tests/DarksideTests/BalanceTests.swift +++ b/Tests/DarksideTests/BalanceTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class BalanceTests: XCTestCase { +class BalanceTests: ZcashTestCase { let sendAmount = Zatoshi(1000) let defaultLatestHeight: BlockHeight = 663188 let branchID = "2bb40e60" @@ -25,7 +25,11 @@ class BalanceTests: XCTestCase { override func setUp() async throws { try await super.setUp() - self.coordinator = try await TestCoordinator(walletBirthday: birthday, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday, + network: network + ) try coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") } diff --git a/Tests/DarksideTests/DarksideSanityCheckTests.swift b/Tests/DarksideTests/DarksideSanityCheckTests.swift index 521b9ec9..df4e61b2 100644 --- a/Tests/DarksideTests/DarksideSanityCheckTests.swift +++ b/Tests/DarksideTests/DarksideSanityCheckTests.swift @@ -10,7 +10,7 @@ import Foundation import XCTest @testable import ZcashLightClientKit -class DarksideSanityCheckTests: XCTestCase { +class DarksideSanityCheckTests: ZcashTestCase { let sendAmount: Int64 = 1000 var birthday: BlockHeight = 663150 let defaultLatestHeight: BlockHeight = 663175 @@ -27,7 +27,11 @@ class DarksideSanityCheckTests: XCTestCase { override func setUp() async throws { try await super.setUp() - self.coordinator = try await TestCoordinator(walletBirthday: birthday, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday, + network: network + ) try self.coordinator.reset(saplingActivation: self.birthday, branchID: self.branchID, chainName: self.chainName) try self.coordinator.resetBlocks(dataset: .default) diff --git a/Tests/DarksideTests/InternalStateConsistencyTests.swift b/Tests/DarksideTests/InternalStateConsistencyTests.swift index 92a89e59..18c6e35b 100644 --- a/Tests/DarksideTests/InternalStateConsistencyTests.swift +++ b/Tests/DarksideTests/InternalStateConsistencyTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -final class InternalStateConsistencyTests: XCTestCase { +final class InternalStateConsistencyTests: ZcashTestCase { let sendAmount = Zatoshi(1000) var birthday: BlockHeight = 663150 let defaultLatestHeight: BlockHeight = 663175 @@ -28,7 +28,12 @@ final class InternalStateConsistencyTests: XCTestCase { try await super.setUp() // don't use an exact birthday, users never do. - self.coordinator = try await TestCoordinator(walletBirthday: birthday + 50, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday + 50, + network: network + ) + try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) } diff --git a/Tests/DarksideTests/PendingTransactionUpdatesTest.swift b/Tests/DarksideTests/PendingTransactionUpdatesTest.swift index b8ea9bd8..a0e8e126 100644 --- a/Tests/DarksideTests/PendingTransactionUpdatesTest.swift +++ b/Tests/DarksideTests/PendingTransactionUpdatesTest.swift @@ -9,7 +9,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class PendingTransactionUpdatesTest: XCTestCase { +class PendingTransactionUpdatesTest: ZcashTestCase { let sendAmount: Int64 = 1000 var birthday: BlockHeight = 663150 let defaultLatestHeight: BlockHeight = 663175 @@ -25,7 +25,11 @@ class PendingTransactionUpdatesTest: XCTestCase { override func setUp() async throws { try await super.setUp() - self.coordinator = try await TestCoordinator(walletBirthday: birthday, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday, + network: network + ) try self.coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") } diff --git a/Tests/DarksideTests/ReOrgTests.swift b/Tests/DarksideTests/ReOrgTests.swift index 76670625..6cd8a50a 100644 --- a/Tests/DarksideTests/ReOrgTests.swift +++ b/Tests/DarksideTests/ReOrgTests.swift @@ -22,7 +22,7 @@ basic reorg test. Scan, get a reorg and then reach latest height. * observe that the prev hash of that block does not match the hash that we have for 663250 * rewind 10 blocks and request blocks 663241 to 663251 */ -class ReOrgTests: XCTestCase { +class ReOrgTests: ZcashTestCase { let sendAmount: Int64 = 1000 let defaultLatestHeight: BlockHeight = 663175 let network = DarksideWalletDNetwork() @@ -44,7 +44,11 @@ class ReOrgTests: XCTestCase { override func setUp() async throws { try await super.setUp() - self.coordinator = try await TestCoordinator(walletBirthday: self.birthday, network: self.network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: self.birthday, + network: self.network + ) try self.coordinator.reset(saplingActivation: self.birthday, branchID: self.branchID, chainName: self.chainName) diff --git a/Tests/DarksideTests/RewindRescanTests.swift b/Tests/DarksideTests/RewindRescanTests.swift index bd19863e..77db70ce 100644 --- a/Tests/DarksideTests/RewindRescanTests.swift +++ b/Tests/DarksideTests/RewindRescanTests.swift @@ -11,7 +11,7 @@ import XCTest @testable import ZcashLightClientKit // FIXME: [#586] disabled until this is resolved https://github.com/zcash/ZcashLightClientKit/issues/586 -class RewindRescanTests: XCTestCase { +class RewindRescanTests: ZcashTestCase { let sendAmount: Int64 = 1000 let defaultLatestHeight: BlockHeight = 663175 let branchID = "2bb40e60" @@ -30,7 +30,11 @@ class RewindRescanTests: XCTestCase { override func setUp() async throws { try await super.setUp() - self.coordinator = try await TestCoordinator(walletBirthday: birthday, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday, + network: network + ) try self.coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") } diff --git a/Tests/DarksideTests/ShieldFundsTests.swift b/Tests/DarksideTests/ShieldFundsTests.swift index 5007d755..a1047788 100644 --- a/Tests/DarksideTests/ShieldFundsTests.swift +++ b/Tests/DarksideTests/ShieldFundsTests.swift @@ -11,7 +11,7 @@ import XCTest @testable import ZcashLightClientKit // FIXME: [#587] disabled until https://github.com/zcash/ZcashLightClientKit/issues/587 fixed -class ShieldFundsTests: XCTestCase { +class ShieldFundsTests: ZcashTestCase { let sendAmount = Zatoshi(1000) var birthday: BlockHeight = 1631000 var coordinator: TestCoordinator! @@ -25,7 +25,11 @@ class ShieldFundsTests: XCTestCase { override func setUp() async throws { try await super.setUp() - self.coordinator = try await TestCoordinator(walletBirthday: birthday, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday, + network: network + ) try coordinator.reset(saplingActivation: birthday, branchID: self.branchID, chainName: self.chainName) try coordinator.service.clearAddedUTXOs() } diff --git a/Tests/DarksideTests/SynchronizerDarksideTests.swift b/Tests/DarksideTests/SynchronizerDarksideTests.swift index a6a50440..e3b48ca2 100644 --- a/Tests/DarksideTests/SynchronizerDarksideTests.swift +++ b/Tests/DarksideTests/SynchronizerDarksideTests.swift @@ -10,7 +10,7 @@ import Combine @testable import TestUtils @testable import ZcashLightClientKit -class SynchronizerDarksideTests: XCTestCase { +class SynchronizerDarksideTests: ZcashTestCase { let sendAmount: Int64 = 1000 let defaultLatestHeight: BlockHeight = 663175 let branchID = "2bb40e60" @@ -30,8 +30,16 @@ class SynchronizerDarksideTests: XCTestCase { override func setUp() async throws { try await super.setUp() - idGenerator = MockSyncSessionIDGenerator(ids: [.deadbeef]) - self.coordinator = try await TestCoordinator(walletBirthday: birthday, network: network, syncSessionIDGenerator: idGenerator) + let idGenerator = MockSyncSessionIDGenerator(ids: [.deadbeef]) + mockContainer.mock(type: SyncSessionIDGenerator.self, isSingleton: false) { _ in idGenerator } + self.idGenerator = idGenerator + + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday, + network: network + ) + try self.coordinator.reset(saplingActivation: 663150, branchID: "e9ff75a6", chainName: "main") } @@ -295,14 +303,11 @@ class SynchronizerDarksideTests: XCTestCase { ] XCTAssertEqual(states.count, expectedStates.count) - XCTAssertEqual(states[0], expectedStates[0]) - XCTAssertEqual(states[1], expectedStates[1]) - XCTAssertEqual(states[2], expectedStates[2]) - XCTAssertEqual(states[3], expectedStates[3]) - XCTAssertEqual(states[4], expectedStates[4]) - XCTAssertEqual(states[5], expectedStates[5]) - XCTAssertEqual(states[6], expectedStates[6]) - XCTAssertEqual(states[7], expectedStates[7]) + + for (index, state) in states.enumerated() { + let expectedState = expectedStates[index] + XCTAssertEqual(state, expectedState, "Failed state comparison at index \(index).") + } } func testSyncSessionUpdates() async throws { @@ -458,13 +463,11 @@ class SynchronizerDarksideTests: XCTestCase { ] XCTAssertEqual(states.count, expectedStates.count) - XCTAssertEqual(states[0], expectedStates[0]) - XCTAssertEqual(states[1], expectedStates[1]) - XCTAssertEqual(states[2], expectedStates[2]) - XCTAssertEqual(states[3], expectedStates[3]) - XCTAssertEqual(states[4], expectedStates[4]) - XCTAssertEqual(states[5], expectedStates[5]) - XCTAssertEqual(states[7], expectedStates[7]) + + for (index, state) in states.enumerated() { + let expectedState = expectedStates[index] + XCTAssertEqual(state, expectedState, "Failed state comparison at index \(index).") + } try coordinator.service.applyStaged(nextLatestHeight: 663_200) @@ -532,11 +535,11 @@ class SynchronizerDarksideTests: XCTestCase { ] XCTAssertEqual(states.count, secondBatchOfExpectedStates.count) - XCTAssertEqual(states[0], secondBatchOfExpectedStates[0]) - XCTAssertEqual(states[1], secondBatchOfExpectedStates[1]) - XCTAssertEqual(states[2], secondBatchOfExpectedStates[2]) - XCTAssertEqual(states[3], secondBatchOfExpectedStates[3]) - XCTAssertEqual(states[4], secondBatchOfExpectedStates[4]) + + for (index, state) in states.enumerated() { + let expectedState = secondBatchOfExpectedStates[index] + XCTAssertEqual(state, expectedState, "Failed state comparison at index \(index).") + } } func testSyncAfterWipeWorks() async throws { diff --git a/Tests/DarksideTests/SynchronizerTests.swift b/Tests/DarksideTests/SynchronizerTests.swift index 918097c8..f19562ec 100644 --- a/Tests/DarksideTests/SynchronizerTests.swift +++ b/Tests/DarksideTests/SynchronizerTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -final class SynchronizerTests: XCTestCase { +final class SynchronizerTests: ZcashTestCase { let sendAmount = Zatoshi(1000) var birthday: BlockHeight = 663150 let defaultLatestHeight: BlockHeight = 663175 @@ -28,7 +28,11 @@ final class SynchronizerTests: XCTestCase { try await super.setUp() // don't use an exact birthday, users never do. - self.coordinator = try await TestCoordinator(walletBirthday: birthday + 50, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday + 50, + network: network + ) try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) let eventClosure: CompactBlockProcessor.EventClosure = { [weak self] event in diff --git a/Tests/DarksideTests/TransactionEnhancementTests.swift b/Tests/DarksideTests/TransactionEnhancementTests.swift index 106860a6..9d577d34 100644 --- a/Tests/DarksideTests/TransactionEnhancementTests.swift +++ b/Tests/DarksideTests/TransactionEnhancementTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class TransactionEnhancementTests: XCTestCase { +class TransactionEnhancementTests: ZcashTestCase { var cancellables: [AnyCancellable] = [] var processorEventHandler: CompactBlockProcessorEventHandler! = CompactBlockProcessorEventHandler() let mockLatestHeight = BlockHeight(663250) @@ -134,14 +134,29 @@ class TransactionEnhancementTests: XCTestCase { ) downloader = BlockDownloaderServiceImpl(service: service, storage: storage) + + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: pathProvider.dataDbURL, + spendParamsURL: pathProvider.spendParamsURL, + outputParamsURL: pathProvider.outputParamsURL + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) + ) + + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in + LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository) + } + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in self.rustBackend } + processor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: rustBackend, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository) + container: mockContainer, + config: processorConfig ) let eventClosure: CompactBlockProcessor.EventClosure = { [weak self] event in diff --git a/Tests/DarksideTests/Z2TReceiveTests.swift b/Tests/DarksideTests/Z2TReceiveTests.swift index 304cf930..7019d093 100644 --- a/Tests/DarksideTests/Z2TReceiveTests.swift +++ b/Tests/DarksideTests/Z2TReceiveTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class Z2TReceiveTests: XCTestCase { +class Z2TReceiveTests: ZcashTestCase { let testRecipientAddress = "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz" let sendAmount: Int64 = 1000 var birthday: BlockHeight = 663150 @@ -28,7 +28,11 @@ class Z2TReceiveTests: XCTestCase { override func setUp() async throws { try await super.setUp() - self.coordinator = try await TestCoordinator(walletBirthday: birthday, network: network) + self.coordinator = try await TestCoordinator( + container: mockContainer, + walletBirthday: birthday, + network: network + ) try coordinator.reset(saplingActivation: 663150, branchID: self.branchID, chainName: self.chainName) } diff --git a/Tests/NetworkTests/BlockScanTests.swift b/Tests/NetworkTests/BlockScanTests.swift index 98a64245..94d210f3 100644 --- a/Tests/NetworkTests/BlockScanTests.swift +++ b/Tests/NetworkTests/BlockScanTests.swift @@ -12,7 +12,7 @@ import SQLite @testable import TestUtils @testable import ZcashLightClientKit -class BlockScanTests: XCTestCase { +class BlockScanTests: ZcashTestCase { var cancelables: [AnyCancellable] = [] var dataDbURL: URL! @@ -51,6 +51,23 @@ class BlockScanTests: XCTestCase { ) deleteDBs() + + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: dataDbURL, + spendParamsURL: spendParamsURL, + outputParamsURL: outputParamsURL + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) + ) + + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in LatestBlocksDataProviderMock() } + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in self.rustBackend } } private func deleteDBs() { @@ -73,24 +90,9 @@ class BlockScanTests: XCTestCase { _ = try await rustBackend.initDataDb(seed: nil) let endpoint = LightWalletEndpoint(address: "lightwalletd.testnet.electriccoin.co", port: 9067) - let service = LightWalletServiceFactory(endpoint: endpoint).make() let blockCount = 100 let range = network.constants.saplingActivationHeight ... network.constants.saplingActivationHeight + blockCount - let fsBlockRepository = FSCompactBlockRepository( - fsBlockDbRoot: testTempDirectory, - metadataStore: FSMetadataStore.live( - fsBlockDbRoot: testTempDirectory, - rustBackend: rustBackend, - logger: logger - ), - blockDescriptor: ZcashCompactBlockDescriptor.live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ) - - try await fsBlockRepository.create() - let processorConfig = CompactBlockProcessor.Configuration( alias: .default, fsBlockCacheRoot: testTempDirectory, @@ -102,15 +104,12 @@ class BlockScanTests: XCTestCase { network: network ) - let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: fsBlockRepository, - rustBackend: rustBackend, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() - ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in + LightWalletServiceFactory(endpoint: endpoint).make() + } + try await mockContainer.resolve(CompactBlockRepository.self).create() + + let compactBlockProcessor = CompactBlockProcessor(container: mockContainer, config: processorConfig) let repository = BlockSQLDAO(dbProvider: SimpleConnectionProvider.init(path: self.dataDbURL.absoluteString, readonly: true)) var latestScannedheight = BlockHeight.empty() @@ -162,22 +161,6 @@ class BlockScanTests: XCTestCase { saplingTree: walletBirthDay.saplingTree ) - let service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make() - - let fsBlockRepository = FSCompactBlockRepository( - fsBlockDbRoot: testTempDirectory, - metadataStore: FSMetadataStore.live( - fsBlockDbRoot: testTempDirectory, - rustBackend: rustBackend, - logger: logger - ), - blockDescriptor: ZcashCompactBlockDescriptor.live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ) - - try await fsBlockRepository.create() - let processorConfig = CompactBlockProcessor.Configuration( alias: .default, fsBlockCacheRoot: testTempDirectory, @@ -191,15 +174,12 @@ class BlockScanTests: XCTestCase { network: network ) - let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: fsBlockRepository, - rustBackend: rustBackend, - config: processorConfig, - metrics: metrics, - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() - ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in + LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make() + } + try await mockContainer.resolve(CompactBlockRepository.self).create() + + let compactBlockProcessor = CompactBlockProcessor(container: mockContainer, config: processorConfig) let eventClosure: CompactBlockProcessor.EventClosure = { [weak self] event in switch event { @@ -220,7 +200,7 @@ class BlockScanTests: XCTestCase { try await blockDownloader.setSyncRange(range) await blockDownloader.startDownload(maxBlockBufferSize: 10) try await blockDownloader.waitUntilRequestedBlocksAreDownloaded(in: range) - + XCTAssertFalse(Task.isCancelled) try await compactBlockProcessor.blockValidator.validate() diff --git a/Tests/NetworkTests/BlockStreamingTest.swift b/Tests/NetworkTests/BlockStreamingTest.swift index 732cb288..972a2584 100644 --- a/Tests/NetworkTests/BlockStreamingTest.swift +++ b/Tests/NetworkTests/BlockStreamingTest.swift @@ -9,7 +9,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class BlockStreamingTest: XCTestCase { +class BlockStreamingTest: ZcashTestCase { let testFileManager = FileManager() var rustBackend: ZcashRustBackendWelding! var testTempDirectory: URL! @@ -21,6 +21,24 @@ class BlockStreamingTest: XCTestCase { try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false) rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .testnet) + logger = OSLogger(logLevel: .debug) + + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: try! __dataDbURL(), + spendParamsURL: try! __spendParamsURL(), + outputParamsURL: try! __outputParamsURL() + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) + ) + + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in LatestBlocksDataProviderMock() } + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in self.rustBackend } } override func tearDownWithError() throws { @@ -70,20 +88,6 @@ class BlockStreamingTest: XCTestCase { ) let service = LightWalletServiceFactory(endpoint: endpoint).make() - let storage = FSCompactBlockRepository( - fsBlockDbRoot: testTempDirectory, - metadataStore: FSMetadataStore.live( - fsBlockDbRoot: testTempDirectory, - rustBackend: rustBackend, - logger: logger - ), - blockDescriptor: .live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ) - - try await storage.create() - let latestBlockHeight = try await service.latestBlockHeight() let startHeight = latestBlockHeight - 100_000 let processorConfig = CompactBlockProcessor.Configuration.standard( @@ -91,15 +95,12 @@ class BlockStreamingTest: XCTestCase { walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight ) - let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: rustBackend, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() - ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in + LightWalletServiceFactory(endpoint: endpoint).make() + } + try await mockContainer.resolve(CompactBlockRepository.self).create() + + let compactBlockProcessor = CompactBlockProcessor(container: mockContainer, config: processorConfig) let cancelableTask = Task { do { @@ -127,20 +128,6 @@ class BlockStreamingTest: XCTestCase { ) let service = LightWalletServiceFactory(endpoint: endpoint).make() - let storage = FSCompactBlockRepository( - fsBlockDbRoot: testTempDirectory, - metadataStore: FSMetadataStore.live( - fsBlockDbRoot: testTempDirectory, - rustBackend: rustBackend, - logger: logger - ), - blockDescriptor: .live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ) - - try await storage.create() - let latestBlockHeight = try await service.latestBlockHeight() let startHeight = latestBlockHeight - 100_000 @@ -150,15 +137,12 @@ class BlockStreamingTest: XCTestCase { walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight ) - let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: rustBackend, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() - ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in + LightWalletServiceFactory(endpoint: endpoint).make() + } + try await mockContainer.resolve(CompactBlockRepository.self).create() + + let compactBlockProcessor = CompactBlockProcessor(container: mockContainer, config: processorConfig) let date = Date() diff --git a/Tests/NetworkTests/CompactBlockProcessorTests.swift b/Tests/NetworkTests/CompactBlockProcessorTests.swift index 002db406..46af6c9d 100644 --- a/Tests/NetworkTests/CompactBlockProcessorTests.swift +++ b/Tests/NetworkTests/CompactBlockProcessorTests.swift @@ -11,7 +11,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class CompactBlockProcessorTests: XCTestCase { +class CompactBlockProcessorTests: ZcashTestCase { var processorConfig: CompactBlockProcessor.Configuration! var cancellables: [AnyCancellable] = [] var processorEventHandler: CompactBlockProcessorEventHandler! = CompactBlockProcessorEventHandler() @@ -73,20 +73,6 @@ class CompactBlockProcessorTests: XCTestCase { info.estimatedHeight = UInt64(mockLatestHeight) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight) }) - - let storage = FSCompactBlockRepository( - fsBlockDbRoot: processorConfig.fsBlockCacheRoot, - metadataStore: FSMetadataStore.live( - fsBlockDbRoot: processorConfig.fsBlockCacheRoot, - rustBackend: rustBackend, - logger: logger - ), - blockDescriptor: .live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ) - - try await storage.create() let transactionRepository = MockTransactionRepository( unminedCount: 0, @@ -96,15 +82,28 @@ class CompactBlockProcessorTests: XCTestCase { network: network ) - processor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: rustBackend, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository) + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: processorConfig.dataDb, + spendParamsURL: processorConfig.spendParamsURL, + outputParamsURL: processorConfig.outputParamsURL + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) ) + + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in + LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository) + } + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in self.rustBackend } + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in service } + try await mockContainer.resolve(CompactBlockRepository.self).create() + + processor = CompactBlockProcessor(container: mockContainer, config: processorConfig) let dbInit = try await rustBackend.initDataDb(seed: nil) diff --git a/Tests/NetworkTests/CompactBlockReorgTests.swift b/Tests/NetworkTests/CompactBlockReorgTests.swift index 04c6c514..c6bb68ea 100644 --- a/Tests/NetworkTests/CompactBlockReorgTests.swift +++ b/Tests/NetworkTests/CompactBlockReorgTests.swift @@ -11,7 +11,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class CompactBlockReorgTests: XCTestCase { +class CompactBlockReorgTests: ZcashTestCase { var processorConfig: CompactBlockProcessor.Configuration! let testFileManager = FileManager() var cancellables: [AnyCancellable] = [] @@ -110,16 +110,29 @@ class CompactBlockReorgTests: XCTestCase { network: network ) - processor = CompactBlockProcessor( - service: service, - storage: realCache, - rustBackend: rustBackendMockHelper.rustBackendMock, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository) + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: processorConfig.dataDb, + spendParamsURL: processorConfig.spendParamsURL, + outputParamsURL: processorConfig.outputParamsURL + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) ) + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in + LatestBlocksDataProviderImpl(service: service, transactionRepository: transactionRepository) + } + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in self.rustBackendMockHelper.rustBackendMock } + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in service } + mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in realCache } + + processor = CompactBlockProcessor(container: mockContainer, config: processorConfig) + syncStartedExpect = XCTestExpectation(description: "\(self.description) syncStartedExpect") stopNotificationExpectation = XCTestExpectation(description: "\(self.description) stopNotificationExpectation") updatedNotificationExpectation = XCTestExpectation(description: "\(self.description) updatedNotificationExpectation") diff --git a/Tests/NetworkTests/DownloadTests.swift b/Tests/NetworkTests/DownloadTests.swift index 13d22893..b8f3f21d 100644 --- a/Tests/NetworkTests/DownloadTests.swift +++ b/Tests/NetworkTests/DownloadTests.swift @@ -11,7 +11,7 @@ import SQLite @testable import TestUtils @testable import ZcashLightClientKit -class DownloadTests: XCTestCase { +class DownloadTests: ZcashTestCase { let testFileManager = FileManager() var network = ZcashNetworkBuilder.network(for: .testnet) var testTempDirectory: URL! @@ -23,6 +23,22 @@ class DownloadTests: XCTestCase { await InternalSyncProgress(alias: .default, storage: UserDefaults.standard, logger: logger).rewind(to: 0) try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false) + + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: try! __dataDbURL(), + spendParamsURL: try! __spendParamsURL(), + outputParamsURL: try! __outputParamsURL() + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) + ) + + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in LatestBlocksDataProviderMock() } } override func tearDownWithError() throws { @@ -32,21 +48,13 @@ class DownloadTests: XCTestCase { } func testSingleDownload() async throws { - let service = LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make() - let rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: network.networkType) - - let storage = FSCompactBlockRepository( - fsBlockDbRoot: testTempDirectory, - metadataStore: FSMetadataStore.live( - fsBlockDbRoot: testTempDirectory, - rustBackend: rustBackend, - logger: logger - ), - blockDescriptor: .live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ) - + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in + ZcashRustBackend.makeForTests(fsBlockDbRoot: self.testTempDirectory, networkType: self.network.networkType) + } + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in + LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make() + } + let storage = mockContainer.resolve(CompactBlockRepository.self) try await storage.create() let blockCount = 100 @@ -58,15 +66,7 @@ class DownloadTests: XCTestCase { walletBirthday: network.constants.saplingActivationHeight ) - let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: rustBackend, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() - ) + let compactBlockProcessor = CompactBlockProcessor(container: mockContainer, config: processorConfig) do { try await compactBlockProcessor.blockDownloaderService.downloadBlockRange(range) diff --git a/Tests/OfflineTests/BlockBatchValidationTests.swift b/Tests/OfflineTests/BlockBatchValidationTests.swift index 01d2546b..4baf9680 100644 --- a/Tests/OfflineTests/BlockBatchValidationTests.swift +++ b/Tests/OfflineTests/BlockBatchValidationTests.swift @@ -9,7 +9,7 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class BlockBatchValidationTests: XCTestCase { +class BlockBatchValidationTests: ZcashTestCase { let testFileManager = FileManager() var rustBackend: ZcashRustBackendWelding! var testTempDirectory: URL! @@ -17,6 +17,23 @@ class BlockBatchValidationTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() testTempDirectory = Environment.uniqueTestTempDirectory + + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: try! __dataDbURL(), + spendParamsURL: try! __spendParamsURL(), + outputParamsURL: try! __outputParamsURL() + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) + ) + + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in LatestBlocksDataProviderMock() } + try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false) rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .testnet) } @@ -34,6 +51,7 @@ class BlockBatchValidationTests: XCTestCase { latestBlockHeight: 1210000, service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make() ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in service } let storage = FSCompactBlockRepository( fsBlockDbRoot: testTempDirectory, @@ -46,11 +64,15 @@ class BlockBatchValidationTests: XCTestCase { contentProvider: DirectoryListingProviders.defaultSorted, logger: logger ) + mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in storage } try await storage.create() - let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) - let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository) + mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in + let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) + return BlockDownloaderServiceImpl(service: service, storage: repository) + } + let config = CompactBlockProcessor.Configuration( alias: .default, fsBlockCacheRoot: testTempDirectory, @@ -77,19 +99,15 @@ class BlockBatchValidationTests: XCTestCase { service.mockLightDInfo = info let mockBackend = await RustBackendMockHelper(rustBackend: rustBackend, consensusBranchID: Int32(0xd34d)) + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in mockBackend.rustBackendMock } let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: mockBackend.rustBackendMock, - config: config, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() + container: mockContainer, + config: config ) do { - try await compactBlockProcessor.figureNextBatch(downloaderService: downloaderService) + try await compactBlockProcessor.figureNextBatch(downloaderService: mockContainer.resolve(BlockDownloaderService.self)) XCTAssertFalse(Task.isCancelled) } catch { switch error { @@ -107,6 +125,7 @@ class BlockBatchValidationTests: XCTestCase { latestBlockHeight: 1210000, service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make() ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in service } let storage = FSCompactBlockRepository( fsBlockDbRoot: testTempDirectory, @@ -119,11 +138,15 @@ class BlockBatchValidationTests: XCTestCase { contentProvider: DirectoryListingProviders.defaultSorted, logger: logger ) + mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in storage } try await storage.create() - let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) - let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository) + mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in + let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) + return BlockDownloaderServiceImpl(service: service, storage: repository) + } + let config = CompactBlockProcessor.Configuration( alias: .default, fsBlockCacheRoot: testTempDirectory, @@ -150,19 +173,15 @@ class BlockBatchValidationTests: XCTestCase { service.mockLightDInfo = info let mockBackend = await RustBackendMockHelper(rustBackend: rustBackend, consensusBranchID: 0xd34db4d) + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in mockBackend.rustBackendMock } let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: mockBackend.rustBackendMock, - config: config, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() + container: mockContainer, + config: config ) do { - try await compactBlockProcessor.figureNextBatch(downloaderService: downloaderService) + try await compactBlockProcessor.figureNextBatch(downloaderService: mockContainer.resolve(BlockDownloaderService.self)) XCTAssertFalse(Task.isCancelled) } catch { switch error { @@ -180,6 +199,7 @@ class BlockBatchValidationTests: XCTestCase { latestBlockHeight: 1210000, service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make() ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in service } let storage = FSCompactBlockRepository( fsBlockDbRoot: testTempDirectory, @@ -192,11 +212,15 @@ class BlockBatchValidationTests: XCTestCase { contentProvider: DirectoryListingProviders.defaultSorted, logger: logger ) + mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in storage } try await storage.create() - let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) - let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository) + mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in + let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) + return BlockDownloaderServiceImpl(service: service, storage: repository) + } + let config = CompactBlockProcessor.Configuration( alias: .default, fsBlockCacheRoot: testTempDirectory, @@ -223,19 +247,15 @@ class BlockBatchValidationTests: XCTestCase { service.mockLightDInfo = info let mockBackend = await RustBackendMockHelper(rustBackend: rustBackend, consensusBranchID: 0xd34db4d) + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in mockBackend.rustBackendMock } let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: mockBackend.rustBackendMock, - config: config, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() + container: mockContainer, + config: config ) do { - try await compactBlockProcessor.figureNextBatch(downloaderService: downloaderService) + try await compactBlockProcessor.figureNextBatch(downloaderService: mockContainer.resolve(BlockDownloaderService.self)) XCTAssertFalse(Task.isCancelled) } catch { switch error { @@ -253,6 +273,7 @@ class BlockBatchValidationTests: XCTestCase { latestBlockHeight: 1210000, service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.default).make() ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in service } let storage = FSCompactBlockRepository( fsBlockDbRoot: testTempDirectory, @@ -265,11 +286,15 @@ class BlockBatchValidationTests: XCTestCase { contentProvider: DirectoryListingProviders.defaultSorted, logger: logger ) + mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in storage } try await storage.create() - let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) - let downloaderService = BlockDownloaderServiceImpl(service: service, storage: repository) + mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in + let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) + return BlockDownloaderServiceImpl(service: service, storage: repository) + } + let config = CompactBlockProcessor.Configuration( alias: .default, fsBlockCacheRoot: testTempDirectory, @@ -297,19 +322,15 @@ class BlockBatchValidationTests: XCTestCase { service.mockLightDInfo = info let mockBackend = await RustBackendMockHelper(rustBackend: rustBackend, consensusBranchID: 0xd34db4d) + mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in mockBackend.rustBackendMock } let compactBlockProcessor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: mockBackend.rustBackendMock, - config: config, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() + container: mockContainer, + config: config ) do { - try await compactBlockProcessor.figureNextBatch(downloaderService: downloaderService) + try await compactBlockProcessor.figureNextBatch(downloaderService: mockContainer.resolve(BlockDownloaderService.self)) XCTAssertFalse(Task.isCancelled) } catch { switch error { diff --git a/Tests/OfflineTests/CompactBlockProcessorOfflineTests.swift b/Tests/OfflineTests/CompactBlockProcessorOfflineTests.swift index 11d66a9f..1e3b9211 100644 --- a/Tests/OfflineTests/CompactBlockProcessorOfflineTests.swift +++ b/Tests/OfflineTests/CompactBlockProcessorOfflineTests.swift @@ -9,13 +9,28 @@ import XCTest @testable import TestUtils @testable import ZcashLightClientKit -class CompactBlockProcessorOfflineTests: XCTestCase { +class CompactBlockProcessorOfflineTests: ZcashTestCase { let testFileManager = FileManager() var testTempDirectory: URL! override func setUpWithError() throws { try super.setUpWithError() testTempDirectory = Environment.uniqueTestTempDirectory + + Dependencies.setup( + in: mockContainer, + urls: Initializer.URLs( + fsBlockDbRoot: testTempDirectory, + dataDbURL: try! __dataDbURL(), + spendParamsURL: try! __spendParamsURL(), + outputParamsURL: try! __outputParamsURL() + ), + alias: .default, + networkType: .testnet, + endpoint: LightWalletEndpointBuilder.default, + loggingPolicy: .default(.debug) + ) + try self.testFileManager.createDirectory(at: testTempDirectory, withIntermediateDirectories: false) } @@ -37,6 +52,7 @@ class CompactBlockProcessorOfflineTests: XCTestCase { latestBlockHeight: 690000, service: LightWalletServiceFactory(endpoint: LightWalletEndpointBuilder.eccTestnet).make() ) + mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in service } let storage = FSCompactBlockRepository( fsBlockDbRoot: testTempDirectory, @@ -49,15 +65,12 @@ class CompactBlockProcessorOfflineTests: XCTestCase { contentProvider: DirectoryListingProviders.defaultSorted, logger: logger ) + mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in storage } + mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in LatestBlocksDataProviderMock() } let processor = CompactBlockProcessor( - service: service, - storage: storage, - rustBackend: rustBackend, - config: processorConfig, - metrics: SDKMetrics(), - logger: logger, - latestBlocksDataProvider: LatestBlocksDataProviderMock() + container: mockContainer, + config: processorConfig ) let fullRange = 0...1000 diff --git a/Tests/OfflineTests/SynchronizerOfflineTests.swift b/Tests/OfflineTests/SynchronizerOfflineTests.swift index d2909c32..ec548540 100644 --- a/Tests/OfflineTests/SynchronizerOfflineTests.swift +++ b/Tests/OfflineTests/SynchronizerOfflineTests.swift @@ -11,7 +11,7 @@ import Foundation import XCTest @testable import ZcashLightClientKit -class SynchronizerOfflineTests: XCTestCase { +class SynchronizerOfflineTests: ZcashTestCase { let data = TestsData(networkType: .testnet) var network: ZcashNetwork! var cancellables: [AnyCancellable] = [] @@ -31,6 +31,7 @@ class SynchronizerOfflineTests: XCTestCase { func testCallPrepareWithAlreadyUsedAliasThrowsError() async throws { let firstTestCoordinator = try await TestCoordinator( alias: .custom("alias"), + container: mockContainer, walletBirthday: 10, network: network, callPrepareInConstructor: false @@ -38,6 +39,7 @@ class SynchronizerOfflineTests: XCTestCase { let secondTestCoordinator = try await TestCoordinator( alias: .custom("alias"), + container: mockContainer, walletBirthday: 10, network: network, callPrepareInConstructor: false @@ -58,6 +60,7 @@ class SynchronizerOfflineTests: XCTestCase { func testWhenSynchronizerIsDeallocatedAliasIsntUsedAnymore() async throws { var testCoordinator: TestCoordinator! = try await TestCoordinator( alias: .default, + container: mockContainer, walletBirthday: 10, network: network, callPrepareInConstructor: false @@ -71,6 +74,7 @@ class SynchronizerOfflineTests: XCTestCase { testCoordinator = try await TestCoordinator( alias: .default, + container: mockContainer, walletBirthday: 10, network: network, callPrepareInConstructor: false @@ -84,8 +88,21 @@ class SynchronizerOfflineTests: XCTestCase { } func testCallWipeWithAlreadyUsedAliasThrowsError() async throws { - let firstTestCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false) - let secondTestCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false) + let firstTestCoordinator = try await TestCoordinator( + alias: .default, + container: mockContainer, + walletBirthday: 10, + network: network, + callPrepareInConstructor: false + ) + + let secondTestCoordinator = try await TestCoordinator( + alias: .default, + container: mockContainer, + walletBirthday: 10, + network: network, + callPrepareInConstructor: false + ) let firstWipeExpectation = XCTestExpectation(description: "First wipe expectation") @@ -129,7 +146,13 @@ class SynchronizerOfflineTests: XCTestCase { } func testPrepareCanBeCalledAfterWipeWithSameInstanceOfSDKSynchronizer() async throws { - let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false) + let testCoordinator = try await TestCoordinator( + alias: .default, + container: mockContainer, + walletBirthday: 10, + network: network, + callPrepareInConstructor: false + ) let expectation = XCTestExpectation(description: "Wipe expectation") @@ -157,7 +180,13 @@ class SynchronizerOfflineTests: XCTestCase { } func testSendToAddressCalledWithoutPrepareThrowsError() async throws { - let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false) + let testCoordinator = try await TestCoordinator( + alias: .default, + container: mockContainer, + walletBirthday: 10, + network: network, + callPrepareInConstructor: false + ) do { _ = try await testCoordinator.synchronizer.sendToAddress( @@ -175,27 +204,14 @@ class SynchronizerOfflineTests: XCTestCase { } } - func testSendToWithTransparentAddressThrowsError() async throws { - let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: true) - - do { - _ = try await testCoordinator.synchronizer.sendToAddress( - spendingKey: testCoordinator.spendingKey, - zatoshi: Zatoshi(1), - toAddress: .transparent(data.transparentAddress), - memo: try .init(string: "hello") - ) - XCTFail("Send to address should fail.") - } catch { - if let error = error as? ZcashError, case .synchronizerSendMemoToTransparentAddress = error { - } else { - XCTFail("Send to address failed with unexpected error: \(error)") - } - } - } - func testShieldFundsCalledWithoutPrepareThrowsError() async throws { - let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false) + let testCoordinator = try await TestCoordinator( + alias: .default, + container: mockContainer, + walletBirthday: 10, + network: network, + callPrepareInConstructor: false + ) do { _ = try await testCoordinator.synchronizer.shieldFunds( @@ -213,7 +229,13 @@ class SynchronizerOfflineTests: XCTestCase { } func testRefreshUTXOCalledWithoutPrepareThrowsError() async throws { - let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false) + let testCoordinator = try await TestCoordinator( + alias: .default, + container: mockContainer, + walletBirthday: 10, + network: network, + callPrepareInConstructor: false + ) do { _ = try await testCoordinator.synchronizer.refreshUTXOs(address: data.transparentAddress, from: 1) @@ -227,7 +249,13 @@ class SynchronizerOfflineTests: XCTestCase { } func testRewindCalledWithoutPrepareThrowsError() async throws { - let testCoordinator = try await TestCoordinator(alias: .default, walletBirthday: 10, network: network, callPrepareInConstructor: false) + let testCoordinator = try await TestCoordinator( + alias: .default, + container: mockContainer, + walletBirthday: 10, + network: network, + callPrepareInConstructor: false + ) let expectation = XCTestExpectation() diff --git a/Tests/TestUtils/SDKSynchronizer+Utils.swift b/Tests/TestUtils/SDKSynchronizer+Utils.swift deleted file mode 100644 index e4af245c..00000000 --- a/Tests/TestUtils/SDKSynchronizer+Utils.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// SDKSynchronizer+Utils.swift -// -// -// Created by Francisco Gindre on 3/31/23. -// - -import Foundation -@testable import ZcashLightClientKit - -extension SDKSynchronizer { - convenience init(initializer: Initializer, sessionGenerator: SyncSessionIDGenerator, sessionTicker: SessionTicker) { - let metrics = SDKMetrics() - let latestBlocksDataProvider = LatestBlocksDataProviderImpl( - service: initializer.lightWalletService, - transactionRepository: initializer.transactionRepository - ) - self.init( - status: .unprepared, - initializer: initializer, - transactionEncoder: WalletTransactionEncoder(initializer: initializer), - transactionRepository: initializer.transactionRepository, - utxoRepository: UTXORepositoryBuilder.build(initializer: initializer), - blockProcessor: CompactBlockProcessor( - initializer: initializer, - metrics: metrics, - logger: initializer.logger, - latestBlocksDataProvider: latestBlocksDataProvider, - walletBirthdayProvider: { initializer.walletBirthday } - ), - metrics: metrics, - syncSessionIDGenerator: sessionGenerator, - syncSessionTicker: sessionTicker, - latestBlocksDataProvider: latestBlocksDataProvider - ) - } -} diff --git a/Tests/TestUtils/TestCoordinator.swift b/Tests/TestUtils/TestCoordinator.swift index 66fc21eb..291c7b12 100644 --- a/Tests/TestUtils/TestCoordinator.swift +++ b/Tests/TestUtils/TestCoordinator.swift @@ -55,6 +55,7 @@ class TestCoordinator { init( alias: ZcashSynchronizerAlias = .default, + container: DIContainer, walletBirthday: BlockHeight, network: ZcashNetwork, callPrepareInConstructor: Bool = true, @@ -63,86 +64,40 @@ class TestCoordinator { dbTracingClosure: ((String) -> Void)? = nil ) async throws { await InternalSyncProgress(alias: alias, storage: UserDefaults.standard, logger: logger).rewind(to: 0) - + let databases = TemporaryDbBuilder.build() self.databases = databases - - let urls = Initializer.URLs( + + let initializer = Initializer( + container: container, + cacheDbURL: nil, fsBlockDbRoot: databases.fsCacheDbRoot, dataDbURL: databases.dataDB, - spendParamsURL: try __spendParamsURL(), - outputParamsURL: try __outputParamsURL() - ) - - let (updatedURLs, parsingError) = Initializer.tryToUpdateURLs(with: alias, urls: urls) - - let backend = ZcashRustBackend( - dbData: updatedURLs.dataDbURL, - fsBlockDbRoot: updatedURLs.fsBlockDbRoot, - spendParamsPath: updatedURLs.spendParamsURL, - outputParamsPath: updatedURLs.outputParamsURL, - networkType: network.networkType - ) - - let transactionRepository = TransactionSQLDAO( - dbProvider: SimpleConnectionProvider(path: updatedURLs.dataDbURL.absoluteString), - traceClosure: dbTracingClosure - ) - - let accountRepository = AccountRepositoryBuilder.build( - dataDbURL: updatedURLs.dataDbURL, - readOnly: true, - caching: true, - logger: logger - ) - - let fsBlockRepository = FSCompactBlockRepository( - fsBlockDbRoot: updatedURLs.fsBlockDbRoot, - metadataStore: .live( - fsBlockDbRoot: updatedURLs.fsBlockDbRoot, - rustBackend: backend, - logger: logger - ), - blockDescriptor: .live, - contentProvider: DirectoryListingProviders.defaultSorted, - logger: logger - ) - - let service = Initializer.makeLightWalletServiceFactory(endpoint: endpoint).make() - - let initializer = Initializer( - rustBackend: backend, - network: network, - cacheDbURL: nil, - urls: updatedURLs, endpoint: endpoint, - service: service, - repository: transactionRepository, - accountRepository: accountRepository, - storage: fsBlockRepository, + network: network, + spendParamsURL: try __spendParamsURL(), + outputParamsURL: try __outputParamsURL(), saplingParamsSourceURL: SaplingParamsSourceURL.tests, alias: alias, - urlsParsingError: parsingError, - logger: OSLogger(logLevel: .debug) + loggingPolicy: .default(.debug) ) - + let derivationTool = DerivationTool(networkType: network.networkType) - + self.spendingKey = try derivationTool.deriveUnifiedSpendingKey( seed: Environment.seedBytes, accountIndex: 0 ) - + self.viewingKey = try derivationTool.deriveUnifiedFullViewingKey(from: spendingKey) self.birthday = walletBirthday self.network = network - + let liveService = LightWalletServiceFactory(endpoint: endpoint).make() self.service = DarksideWalletService(endpoint: endpoint, service: liveService) - - self.synchronizer = SDKSynchronizer(initializer: initializer, sessionGenerator: syncSessionIDGenerator, sessionTicker: .live) + self.synchronizer = SDKSynchronizer(initializer: initializer) subscribeToState(synchronizer: self.synchronizer) - + if callPrepareInConstructor { if case .seedRequired = try await prepare(seed: Environment.seedBytes) { throw TestCoordinator.CoordinatorError.seedRequiredForMigration diff --git a/Tests/TestUtils/ZcashTestCase.swift b/Tests/TestUtils/ZcashTestCase.swift new file mode 100644 index 00000000..3a82d3a6 --- /dev/null +++ b/Tests/TestUtils/ZcashTestCase.swift @@ -0,0 +1,54 @@ +// +// ZcashTestCase.swift +// +// +// Created by Michal Fousek on 01.05.2023. +// + +import Foundation +@testable import ZcashLightClientKit +import XCTest + +class ZcashTestCase: XCTestCase { + var mockContainer: DIContainer! + + private func createMockContainer() { + guard mockContainer == nil else { return } + mockContainer = DIContainer() + mockContainer.isTestEnvironment = true + } + + private func destroyMockContainer() { + mockContainer = nil + } + + override func setUp() async throws { + try await super.setUp() + createMockContainer() + } + + override func setUp() { + super.setUp() + createMockContainer() + } + + override func setUpWithError() throws { + try super.setUpWithError() + createMockContainer() + } + + override func tearDown() { + super.tearDown() + destroyMockContainer() + } + + override func tearDown() async throws { + try await super.tearDown() + destroyMockContainer() + } + + override func tearDownWithError() throws { + try super.tearDownWithError() + destroyMockContainer() + } +}