[#1012] Introduce dependency injection in the SDK

Closes #1012

- This is the first change for the dependency injection. This change
  adds `DIContainer` which is used to register and resolve dependencies.
- Then this change shows how `DIContainer` can be used to resolve
  dependencies. And it also shows how to mock dependencies in tests.
- Constructors of `Initializer` are changed to support mocking of
  depedendencies. But it doesn't affect public API in any way.
- Depedencies are registered in `Dependencies.setup()`. Then in the code
  `container.resolve(SomeType.self)` is called to get instance of the
  dependency.
- `ZcashTestCase` class is added. It inherits from `XCTestCase` and
  should be used for base class in tests. `ZcashTestCase` takes care of
  creation of `DIContainer` for dependencies mocking. In future we can
  maybe move there more things that are used in each test.
- Lot is missing here. Not all the code is using dependency injection.
  Tests for `DIContainer` are missing. Only `OfflineTests` work now
  (other tests can't be even compiled). It will be added in future
  changes.
This commit is contained in:
Michal Fousek 2023-05-01 16:28:59 +02:00
parent b582e1e4e1
commit 5d2b2298b5
30 changed files with 894 additions and 576 deletions

View File

@ -149,6 +149,7 @@ identifier_name:
- nf
- tx
- as
- di
indentation_width:
indentation_width: 4

View File

@ -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
}

View File

@ -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`.
///

View File

@ -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
)
}
}
}

View File

@ -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)

View File

@ -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<T>(for type: T.Type) -> String {
return String(describing: T.self)
}
func register<T>(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<T>(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<T>(_ 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
}
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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)

View File

@ -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")
}

View File

@ -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()
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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()

View File

@ -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
)
}
}

View File

@ -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

View File

@ -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()
}
}