[#1176] Cover Spend before Sync with tests
- WIP [#1176] Cover Spend before Sync with tests - next batch of updates [#1176] Cover Spend before Sync with tests - last batch of fixes and new tests [#1176] Cover Spend before Sync with tests - package.resolved updated [#1176] Cover Spend before Sync with tests (#1212) - added tests for brand new actions related Spend before Sync - RewindActionTests - UpdateChainTipActionTests - UpdateSubtreeRootsActionTests - ProcessSuggestedScanRangesActionTests
This commit is contained in:
parent
d3dc64aaa3
commit
8152dcaf85
|
@ -158,7 +158,7 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "c3b5f4ebee758b619aa81e844d791aa1fd35b918"
|
"revision" : "6a53c9e32520b46f8c70597e27b335105fabfc21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -113,7 +113,7 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "c3b5f4ebee758b619aa81e844d791aa1fd35b918"
|
"revision" : "6a53c9e32520b46f8c70597e27b335105fabfc21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -16,7 +16,7 @@ let package = Package(
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.14.0"),
|
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.14.0"),
|
||||||
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
|
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
|
||||||
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "c3b5f4ebee758b619aa81e844d791aa1fd35b918")
|
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "6a53c9e32520b46f8c70597e27b335105fabfc21")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
|
|
|
@ -7,7 +7,32 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
actor ActionContext {
|
protocol ActionContext {
|
||||||
|
var state: CBPState { get async }
|
||||||
|
var prevState: CBPState? { get async }
|
||||||
|
var syncControlData: SyncControlData { get async }
|
||||||
|
var preferredSyncAlgorithm: SyncAlgorithm { get }
|
||||||
|
var supportedSyncAlgorithm: SyncAlgorithm? { get async }
|
||||||
|
var requestedRewindHeight: BlockHeight? { get async }
|
||||||
|
var totalProgressRange: CompactBlockRange { get async }
|
||||||
|
var processedHeight: BlockHeight { get async }
|
||||||
|
var lastChainTipUpdateTime: TimeInterval { get async }
|
||||||
|
var lastScannedHeight: BlockHeight? { get async }
|
||||||
|
var lastEnhancedHeight: BlockHeight? { get async }
|
||||||
|
|
||||||
|
func update(state: CBPState) async
|
||||||
|
func update(syncControlData: SyncControlData) async
|
||||||
|
func update(totalProgressRange: CompactBlockRange) async
|
||||||
|
func update(processedHeight: BlockHeight) async
|
||||||
|
func update(lastChainTipUpdateTime: TimeInterval) async
|
||||||
|
func update(lastScannedHeight: BlockHeight) async
|
||||||
|
func update(lastDownloadedHeight: BlockHeight) async
|
||||||
|
func update(lastEnhancedHeight: BlockHeight?) async
|
||||||
|
func update(supportedSyncAlgorithm: SyncAlgorithm) async
|
||||||
|
func update(requestedRewindHeight: BlockHeight) async
|
||||||
|
}
|
||||||
|
|
||||||
|
actor ActionContextImpl: ActionContext {
|
||||||
var state: CBPState
|
var state: CBPState
|
||||||
var prevState: CBPState?
|
var prevState: CBPState?
|
||||||
var syncControlData: SyncControlData
|
var syncControlData: SyncControlData
|
||||||
|
|
|
@ -20,6 +20,7 @@ extension ClearCacheAction: Action {
|
||||||
|
|
||||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||||
try await storage.clear()
|
try await storage.clear()
|
||||||
|
|
||||||
if await context.prevState == .idle {
|
if await context.prevState == .idle {
|
||||||
await context.update(state: .migrateLegacyCacheDB)
|
await context.update(state: .migrateLegacyCacheDB)
|
||||||
} else {
|
} else {
|
||||||
|
@ -37,6 +38,7 @@ extension ClearCacheAction: Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ extension DownloadAction: Action {
|
||||||
let downloadLimit = await context.syncControlData.latestBlockHeight >= potentialDownloadLimit ? potentialDownloadLimit : batchRangeEnd
|
let downloadLimit = await context.syncControlData.latestBlockHeight >= potentialDownloadLimit ? potentialDownloadLimit : batchRangeEnd
|
||||||
|
|
||||||
logger.debug("Starting download with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
logger.debug("Starting download with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
||||||
|
|
||||||
await downloader.update(latestDownloadedBlockHeight: batchRange.lowerBound, force: true) // SbS
|
await downloader.update(latestDownloadedBlockHeight: batchRange.lowerBound, force: true) // SbS
|
||||||
try await downloader.setSyncRange(lastScannedHeight...latestBlockHeight, batchSize: config.batchSize)
|
try await downloader.setSyncRange(lastScannedHeight...latestBlockHeight, batchSize: config.batchSize)
|
||||||
await downloader.setDownloadLimit(downloadLimit)
|
await downloader.setDownloadLimit(downloadLimit)
|
||||||
|
|
|
@ -11,13 +11,11 @@ final class EnhanceAction {
|
||||||
let blockEnhancer: BlockEnhancer
|
let blockEnhancer: BlockEnhancer
|
||||||
let configProvider: CompactBlockProcessor.ConfigProvider
|
let configProvider: CompactBlockProcessor.ConfigProvider
|
||||||
let logger: Logger
|
let logger: Logger
|
||||||
let transactionRepository: TransactionRepository
|
|
||||||
|
|
||||||
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
||||||
blockEnhancer = container.resolve(BlockEnhancer.self)
|
blockEnhancer = container.resolve(BlockEnhancer.self)
|
||||||
self.configProvider = configProvider
|
self.configProvider = configProvider
|
||||||
logger = container.resolve(Logger.self)
|
logger = container.resolve(Logger.self)
|
||||||
transactionRepository = container.resolve(TransactionRepository.self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
|
func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
|
||||||
|
|
|
@ -193,7 +193,7 @@ actor CompactBlockProcessor {
|
||||||
)
|
)
|
||||||
|
|
||||||
let configProvider = ConfigProvider(config: config)
|
let configProvider = ConfigProvider(config: config)
|
||||||
context = ActionContext(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
|
context = ActionContextImpl(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
|
||||||
actions = Self.makeActions(container: container, configProvider: configProvider)
|
actions = Self.makeActions(container: container, configProvider: configProvider)
|
||||||
|
|
||||||
self.metrics = container.resolve(SDKMetrics.self)
|
self.metrics = container.resolve(SDKMetrics.self)
|
||||||
|
@ -638,7 +638,7 @@ extension CompactBlockProcessor {
|
||||||
|
|
||||||
private func resetContext() async {
|
private func resetContext() async {
|
||||||
let lastEnhancedheight = await context.lastEnhancedHeight
|
let lastEnhancedheight = await context.lastEnhancedHeight
|
||||||
context = ActionContext(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
|
context = ActionContextImpl(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
|
||||||
await context.update(lastEnhancedHeight: lastEnhancedheight)
|
await context.update(lastEnhancedHeight: lastEnhancedheight)
|
||||||
await compactBlockProgress.reset()
|
await compactBlockProgress.reset()
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ class BlockStreamingTest: ZcashTestCase {
|
||||||
latestScannedHeight: startHeight,
|
latestScannedHeight: startHeight,
|
||||||
firstUnenhancedHeight: nil
|
firstUnenhancedHeight: nil
|
||||||
)
|
)
|
||||||
let context = ActionContext(state: .download)
|
let context = ActionContextMock()
|
||||||
await context.update(syncControlData: syncControlData)
|
await context.update(syncControlData: syncControlData)
|
||||||
|
|
||||||
let expectation = XCTestExpectation()
|
let expectation = XCTestExpectation()
|
||||||
|
@ -175,7 +175,7 @@ class BlockStreamingTest: ZcashTestCase {
|
||||||
latestScannedHeight: startHeight,
|
latestScannedHeight: startHeight,
|
||||||
firstUnenhancedHeight: nil
|
firstUnenhancedHeight: nil
|
||||||
)
|
)
|
||||||
let context = ActionContext(state: .download)
|
let context = ActionContextMock()
|
||||||
await context.update(syncControlData: syncControlData)
|
await context.update(syncControlData: syncControlData)
|
||||||
|
|
||||||
let date = Date()
|
let date = Date()
|
||||||
|
|
|
@ -11,7 +11,7 @@ import XCTest
|
||||||
|
|
||||||
final class ActionContextStateTests: XCTestCase {
|
final class ActionContextStateTests: XCTestCase {
|
||||||
func testPreviousState() async throws {
|
func testPreviousState() async throws {
|
||||||
let syncContext: ActionContext = .init(state: .idle)
|
let syncContext = ActionContextImpl(state: .idle)
|
||||||
|
|
||||||
await syncContext.update(state: .clearCache)
|
await syncContext.update(state: .clearCache)
|
||||||
|
|
||||||
|
@ -32,4 +32,15 @@ final class ActionContextStateTests: XCTestCase {
|
||||||
XCTFail("syncContext.prevState is not expected to be nil.")
|
XCTFail("syncContext.prevState is not expected to be nil.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDefaultSyncAlgorith() async throws {
|
||||||
|
let syncContext = ActionContextImpl(state: .idle)
|
||||||
|
|
||||||
|
let preferredSyncAlgorithm = await syncContext.preferredSyncAlgorithm
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
preferredSyncAlgorithm == .linear,
|
||||||
|
"ActionContext default preferredSyncAlgorithm is expected to be .linear but received \(preferredSyncAlgorithm)"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,48 @@ import XCTest
|
||||||
final class ClearAlreadyScannedBlocksActionTests: ZcashTestCase {
|
final class ClearAlreadyScannedBlocksActionTests: ZcashTestCase {
|
||||||
func testClearAlreadyScannedBlocksAction_NextAction() async throws {
|
func testClearAlreadyScannedBlocksAction_NextAction() async throws {
|
||||||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
|
||||||
|
let clearAlreadyScannedBlocksAction = setupAction(compactBlockRepositoryMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.lastScannedHeight = -1
|
||||||
|
|
||||||
|
let nextContext = try await clearAlreadyScannedBlocksAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(compactBlockRepositoryMock.clearUpToCalled, "storage.clear(upTo:) is expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.enhance)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testClearAlreadyScannedBlocksAction_NextAction is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClearAlreadyScannedBlocksAction_LastScanHeightZcashError() async throws {
|
||||||
|
let clearAlreadyScannedBlocksAction = setupAction()
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock()
|
||||||
|
|
||||||
|
_ = try await clearAlreadyScannedBlocksAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTFail("testClearAlreadyScannedBlocksAction_LastScanHeightZcashError should throw an error so fail here is unexpected.")
|
||||||
|
} catch ZcashError.compactBlockProcessorLastScannedHeight {
|
||||||
|
// it's expected to end up here because we test that error is a specific one and Swift automatically catched it up for us
|
||||||
|
} catch {
|
||||||
|
XCTFail(
|
||||||
|
"""
|
||||||
|
testClearAlreadyScannedBlocksAction_NextAction is expected to fail
|
||||||
|
with ZcashError.compactBlockProcessorLastScannedHeight but received \(error)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupAction(
|
||||||
|
_ compactBlockRepositoryMock: CompactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
) -> ClearAlreadyScannedBlocksAction {
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||||
|
|
||||||
compactBlockRepositoryMock.clearUpToClosure = { _ in }
|
compactBlockRepositoryMock.clearUpToClosure = { _ in }
|
||||||
|
@ -20,23 +62,8 @@ final class ClearAlreadyScannedBlocksActionTests: ZcashTestCase {
|
||||||
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
||||||
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
|
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
|
||||||
|
|
||||||
let clearAlreadyScannedBlocksAction = ClearAlreadyScannedBlocksAction(
|
return ClearAlreadyScannedBlocksAction(
|
||||||
container: mockContainer
|
container: mockContainer
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
|
||||||
let context = ActionContext(state: .clearAlreadyScannedBlocks)
|
|
||||||
await context.update(lastScannedHeight: -1)
|
|
||||||
|
|
||||||
let nextContext = try await clearAlreadyScannedBlocksAction.run(with: context) { _ in }
|
|
||||||
XCTAssertTrue(compactBlockRepositoryMock.clearUpToCalled, "storage.clear(upTo:) is expected to be called.")
|
|
||||||
let nextState = await nextContext.state
|
|
||||||
XCTAssertTrue(
|
|
||||||
nextState == .enhance,
|
|
||||||
"nextContext after .clearAlreadyScannedBlocks is expected to be .enhance but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
XCTFail("testClearAlreadyScannedBlocksAction_NextAction is not expected to fail. \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,27 +10,119 @@ import XCTest
|
||||||
@testable import ZcashLightClientKit
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
final class ClearCacheActionTests: ZcashTestCase {
|
final class ClearCacheActionTests: ZcashTestCase {
|
||||||
func testClearCacheAction_NextAction() async throws {
|
func testClearCacheAction_MigrationLegacyCacheDB() async throws {
|
||||||
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
|
||||||
|
let clearCacheAction = setupAction(compactBlockRepositoryMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.prevState = .idle
|
||||||
|
|
||||||
|
let nextContext = try await clearCacheAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.migrateLegacyCacheDB)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testClearCacheAction_MigrationLegacyCacheDB is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClearCacheAction_FinishedLinear() async throws {
|
||||||
|
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
|
||||||
|
let clearCacheAction = setupAction(compactBlockRepositoryMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.underlyingPreferredSyncAlgorithm = .linear
|
||||||
|
|
||||||
|
let nextContext = try await clearCacheAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.finished)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClearCacheAction_PreferredSbSNoSupportedSyncAlgorithm() async throws {
|
||||||
|
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
|
||||||
|
let clearCacheAction = setupAction(compactBlockRepositoryMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
|
||||||
|
|
||||||
|
_ = try await clearCacheAction.run(with: context) { _ in }
|
||||||
|
} catch ZcashError.compactBlockProcessorSupportedSyncAlgorithm {
|
||||||
|
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
|
||||||
|
} catch {
|
||||||
|
XCTFail(
|
||||||
|
"""
|
||||||
|
testClearCacheAction_PredferedSbSNoSupportedSyncAlgorithm is expected to fail
|
||||||
|
with ZcashError.compactBlockProcessorSupportedSyncAlgorithm but received \(error)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClearCacheAction_PreferredSbSSupportedSyncAlgorithmLinear() async throws {
|
||||||
|
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
|
||||||
|
let clearCacheAction = setupAction(compactBlockRepositoryMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
|
||||||
|
context.supportedSyncAlgorithm = .linear
|
||||||
|
|
||||||
|
let nextContext = try await clearCacheAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.finished)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClearCacheAction_PreferedSbSSupportedSyncAlgorithmSbS() async throws {
|
||||||
|
let compactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
|
||||||
|
let clearCacheAction = setupAction(compactBlockRepositoryMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
|
||||||
|
context.supportedSyncAlgorithm = .spendBeforeSync
|
||||||
|
|
||||||
|
let nextContext = try await clearCacheAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.processSuggestedScanRanges)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupAction(
|
||||||
|
_ compactBlockRepositoryMock: CompactBlockRepositoryMock = CompactBlockRepositoryMock()
|
||||||
|
) -> ClearCacheAction {
|
||||||
compactBlockRepositoryMock.clearClosure = { }
|
compactBlockRepositoryMock.clearClosure = { }
|
||||||
|
|
||||||
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
||||||
|
|
||||||
let clearCacheAction = ClearCacheAction(
|
return ClearCacheAction(
|
||||||
container: mockContainer
|
container: mockContainer
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
|
||||||
let nextContext = try await clearCacheAction.run(with: .init(state: .clearCache)) { _ in }
|
|
||||||
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
|
|
||||||
let nextState = await nextContext.state
|
|
||||||
XCTAssertTrue(
|
|
||||||
nextState == .finished,
|
|
||||||
"nextContext after .clearCache is expected to be .finished but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
XCTFail("testClearCacheAction_NextAction is not expected to fail. \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,32 +30,24 @@ final class ComputeSyncControlDataActionTests: ZcashTestCase {
|
||||||
latestBlocksDataProviderMock,
|
latestBlocksDataProviderMock,
|
||||||
loggerMock
|
loggerMock
|
||||||
)
|
)
|
||||||
|
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 123
|
||||||
|
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 123
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = await setupActionContext()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
||||||
|
|
||||||
XCTAssertTrue(
|
checkLatestBlocksDataProvider(latestBlocksDataProviderMock)
|
||||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
checkActionContext(nextContext, expectedNextState: .finished)
|
||||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
|
||||||
)
|
|
||||||
XCTAssertTrue(
|
|
||||||
latestBlocksDataProviderMock.updateBlockDataCalled,
|
|
||||||
"latestBlocksDataProvider.updateBlockData() is expected to be called."
|
|
||||||
)
|
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug() is expected to be called.")
|
||||||
XCTAssertTrue(
|
|
||||||
nextState == .finished,
|
|
||||||
"nextContext after .computeSyncControlData is expected to be .finished but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testComputeSyncControlDataAction_finishProcessingCase is not expected to fail. \(error)")
|
XCTFail("testComputeSyncControlDataAction_finishProcessingCase is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testComputeSyncControlDataAction_fetchUTXOsCase() async throws {
|
func testComputeSyncControlDataAction_DownloadCase() async throws {
|
||||||
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||||
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
||||||
let loggerMock = LoggerMock()
|
let loggerMock = LoggerMock()
|
||||||
|
@ -65,47 +57,53 @@ final class ComputeSyncControlDataActionTests: ZcashTestCase {
|
||||||
latestBlocksDataProviderMock,
|
latestBlocksDataProviderMock,
|
||||||
loggerMock
|
loggerMock
|
||||||
)
|
)
|
||||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 10
|
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 1234
|
||||||
|
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 123
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = await setupActionContext()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
||||||
|
|
||||||
XCTAssertTrue(
|
checkLatestBlocksDataProvider(latestBlocksDataProviderMock)
|
||||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
checkActionContext(nextContext, expectedNextState: .download)
|
||||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
|
||||||
)
|
|
||||||
XCTAssertTrue(latestBlocksDataProviderMock.updateBlockDataCalled, "latestBlocksDataProvider.updateBlockData() is expected to be called.")
|
|
||||||
XCTAssertFalse(loggerMock.infoFileFunctionLineCalled, "logger.info() is not expected to be called.")
|
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug() is expected to be called.")
|
||||||
XCTAssertTrue(
|
|
||||||
nextState == .download,
|
|
||||||
"nextContext after .computeSyncControlData is expected to be .download but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testComputeSyncControlDataAction_checksBeforeSyncCase is not expected to fail. \(error)")
|
XCTFail("testComputeSyncControlDataAction_checksBeforeSyncCase is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupSyncControlData() -> SyncControlData {
|
private func setupActionContext() async -> ActionContextMock {
|
||||||
SyncControlData(
|
let syncContext = ActionContextMock()
|
||||||
latestBlockHeight: 0,
|
|
||||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
|
||||||
firstUnenhancedHeight: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupActionContext() async -> ActionContext {
|
syncContext.updateLastScannedHeightClosure = { _ in }
|
||||||
let syncContext: ActionContext = .init(state: .computeSyncControlData)
|
syncContext.updateLastDownloadedHeightClosure = { _ in }
|
||||||
|
syncContext.updateSyncControlDataClosure = { _ in }
|
||||||
await syncContext.update(syncControlData: setupSyncControlData())
|
syncContext.updateTotalProgressRangeClosure = { _ in }
|
||||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
syncContext.updateStateClosure = { _ in }
|
||||||
|
syncContext.underlyingState = .idle
|
||||||
|
|
||||||
return syncContext
|
return syncContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func setupDefaultMocksAndReturnAction(
|
||||||
|
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||||
|
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||||
|
_ loggerMock: LoggerMock = LoggerMock()
|
||||||
|
) -> ComputeSyncControlDataAction {
|
||||||
|
latestBlocksDataProviderMock.updateScannedDataClosure = { }
|
||||||
|
latestBlocksDataProviderMock.updateBlockDataClosure = { }
|
||||||
|
latestBlocksDataProviderMock.updateUnenhancedDataClosure = { }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
return setupAction(
|
||||||
|
blockDownloaderServiceMock,
|
||||||
|
latestBlocksDataProviderMock,
|
||||||
|
loggerMock
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func setupAction(
|
private func setupAction(
|
||||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||||
|
@ -125,23 +123,44 @@ final class ComputeSyncControlDataActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupDefaultMocksAndReturnAction(
|
private func checkLatestBlocksDataProvider(_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock) {
|
||||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
XCTAssertTrue(
|
||||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||||
_ loggerMock: LoggerMock = LoggerMock()
|
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||||
) -> ComputeSyncControlDataAction {
|
)
|
||||||
blockDownloaderServiceMock.lastDownloadedBlockHeightReturnValue = 1
|
XCTAssertTrue(
|
||||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 1
|
latestBlocksDataProviderMock.updateBlockDataCalled,
|
||||||
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 1
|
"latestBlocksDataProvider.updateBlockData() is expected to be called."
|
||||||
latestBlocksDataProviderMock.updateScannedDataClosure = { }
|
)
|
||||||
latestBlocksDataProviderMock.updateBlockDataClosure = { }
|
XCTAssertTrue(
|
||||||
latestBlocksDataProviderMock.updateUnenhancedDataClosure = { }
|
latestBlocksDataProviderMock.updateUnenhancedDataCalled,
|
||||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
"latestBlocksDataProvider.updateUnenhancedData() is expected to be called."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return setupAction(
|
private func checkActionContext(_ actionContext: ActionContext, expectedNextState: CBPState) {
|
||||||
blockDownloaderServiceMock,
|
guard let nextContextMock = actionContext as? ActionContextMock else {
|
||||||
latestBlocksDataProviderMock,
|
return XCTFail("Result of run(with:) is expected to be an ActionContextMock")
|
||||||
loggerMock
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(nextContextMock.updateStateCallsCount == 1)
|
||||||
|
XCTAssertTrue(nextContextMock.updateStateReceivedState == expectedNextState)
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
nextContextMock.updateLastScannedHeightCallsCount == 1,
|
||||||
|
"actionContext.update(lastScannedHeight:) is expected to be called."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
nextContextMock.updateLastDownloadedHeightCallsCount == 1,
|
||||||
|
"actionContext.update(lastDownloadedHeight:) is expected to be called."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
nextContextMock.updateSyncControlDataCallsCount == 1,
|
||||||
|
"actionContext.update(syncControlData:) is expected to be called."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
nextContextMock.updateTotalProgressRangeCallsCount == 1,
|
||||||
|
"actionContext.update(totalProgressRange:) is expected to be called."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
var underlyingDownloadRange: CompactBlockRange?
|
var underlyingDownloadRange: CompactBlockRange?
|
||||||
var underlyingScanRange: CompactBlockRange?
|
var underlyingScanRange: CompactBlockRange?
|
||||||
|
|
||||||
func testDownloadAction_NextAction() async throws {
|
func testDownloadAction_FullPass() async throws {
|
||||||
let blockDownloaderMock = BlockDownloaderMock()
|
let blockDownloaderMock = BlockDownloaderMock()
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||||
|
|
||||||
|
@ -32,25 +32,69 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = ActionContextMock.default()
|
||||||
await syncContext.update(lastScannedHeight: 1000)
|
syncContext.lastScannedHeight = 1000
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 2000,
|
||||||
|
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||||
|
firstUnenhancedHeight: nil
|
||||||
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
||||||
|
|
||||||
XCTAssertTrue(blockDownloaderMock.setSyncRangeBatchSizeCalled, "downloader.setSyncRange() is expected to be called.")
|
|
||||||
XCTAssertTrue(blockDownloaderMock.setDownloadLimitCalled, "downloader.setDownloadLimit() is expected to be called.")
|
|
||||||
XCTAssertTrue(blockDownloaderMock.startDownloadMaxBlockBufferSizeCalled, "downloader.startDownload() is expected to be called.")
|
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(
|
||||||
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInCalled,
|
blockDownloaderMock.setSyncRangeBatchSizeCallsCount == 1,
|
||||||
"downloader.waitUntilRequestedBlocksAreDownloaded() is expected to be called."
|
"downloader.setSyncRange() is expected to be called exatcly once."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(blockDownloaderMock.setDownloadLimitCallsCount == 1, "downloader.setDownloadLimit() is expected to be called exatcly once.")
|
||||||
|
XCTAssertTrue(
|
||||||
|
blockDownloaderMock.startDownloadMaxBlockBufferSizeCallsCount == 1,
|
||||||
|
"downloader.startDownload() is expected to be called exatcly once."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
blockDownloaderMock.updateLatestDownloadedBlockHeightForceCallsCount == 1,
|
||||||
|
"downloader.update(latestDownloadedBlockHeight:) expected to be called exactly once."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInCallsCount == 1,
|
||||||
|
"downloader.waitUntilRequestedBlocksAreDownloaded() is expected to be called exatcly once."
|
||||||
)
|
)
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
let acResult = nextContext.checkStateIs(.scan)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testDownloadAction_NextAction is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDownloadAction_LastScanHeightNil() async throws {
|
||||||
|
let blockDownloaderMock = BlockDownloaderMock()
|
||||||
|
|
||||||
|
let downloadAction = setupAction(blockDownloaderMock)
|
||||||
|
|
||||||
|
let syncContext = ActionContextMock.default()
|
||||||
|
|
||||||
|
do {
|
||||||
|
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(blockDownloaderMock.setSyncRangeBatchSizeCallsCount == 0, "downloader.setSyncRange() is not expected to be called.")
|
||||||
|
XCTAssertTrue(blockDownloaderMock.setDownloadLimitCallsCount == 0, "downloader.setDownloadLimit() is not expected to be called.")
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(
|
||||||
nextState == .scan,
|
blockDownloaderMock.startDownloadMaxBlockBufferSizeCallsCount == 0,
|
||||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
"downloader.startDownload() is not expected to be called."
|
||||||
)
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
blockDownloaderMock.updateLatestDownloadedBlockHeightForceCallsCount == 0,
|
||||||
|
"downloader.update(latestDownloadedBlockHeight:) is not expected to be called."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInCallsCount == 0,
|
||||||
|
"downloader.waitUntilRequestedBlocksAreDownloaded() is not expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.scan)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testDownloadAction_NextAction is not expected to fail. \(error)")
|
XCTFail("testDownloadAction_NextAction is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -65,7 +109,13 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
transactionRepositoryMock
|
transactionRepositoryMock
|
||||||
)
|
)
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = ActionContextMock.default()
|
||||||
|
syncContext.lastScannedHeight = 1000
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 999,
|
||||||
|
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||||
|
firstUnenhancedHeight: nil
|
||||||
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
||||||
|
@ -82,53 +132,13 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
"downloader.waitUntilRequestedBlocksAreDownloaded() is not expected to be called."
|
"downloader.waitUntilRequestedBlocksAreDownloaded() is not expected to be called."
|
||||||
)
|
)
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
let acResult = nextContext.checkStateIs(.scan)
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
nextState == .scan,
|
|
||||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testDownloadAction_NoDownloadAndScanRange is not expected to fail. \(error)")
|
XCTFail("testDownloadAction_NoDownloadAndScanRange is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDownloadAction_NothingMoreToDownload() async throws {
|
|
||||||
let blockDownloaderMock = BlockDownloaderMock()
|
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
|
||||||
|
|
||||||
transactionRepositoryMock.lastScannedHeightReturnValue = 2001
|
|
||||||
|
|
||||||
let downloadAction = setupAction(
|
|
||||||
blockDownloaderMock,
|
|
||||||
transactionRepositoryMock
|
|
||||||
)
|
|
||||||
|
|
||||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
|
||||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
|
||||||
|
|
||||||
do {
|
|
||||||
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
|
|
||||||
|
|
||||||
XCTAssertFalse(blockDownloaderMock.setSyncRangeBatchSizeCalled, "downloader.setSyncRange() is not expected to be called.")
|
|
||||||
XCTAssertFalse(blockDownloaderMock.setDownloadLimitCalled, "downloader.setDownloadLimit() is not expected to be called.")
|
|
||||||
XCTAssertFalse(blockDownloaderMock.startDownloadMaxBlockBufferSizeCalled, "downloader.startDownload() is not expected to be called.")
|
|
||||||
XCTAssertFalse(
|
|
||||||
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInCalled,
|
|
||||||
"downloader.waitUntilRequestedBlocksAreDownloaded() is not expected to be called."
|
|
||||||
)
|
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
|
||||||
XCTAssertTrue(
|
|
||||||
nextState == .scan,
|
|
||||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
XCTFail("testDownloadAction_NothingMoreToDownload is not expected to fail. \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDownloadAction_DownloadStops() async throws {
|
func testDownloadAction_DownloadStops() async throws {
|
||||||
let blockDownloaderMock = BlockDownloaderMock()
|
let blockDownloaderMock = BlockDownloaderMock()
|
||||||
|
|
||||||
|
@ -143,20 +153,6 @@ final class DownloadActionTests: ZcashTestCase {
|
||||||
XCTAssertTrue(blockDownloaderMock.stopDownloadCalled, "downloader.stopDownload() is expected to be called.")
|
XCTAssertTrue(blockDownloaderMock.stopDownloadCalled, "downloader.stopDownload() is expected to be called.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupActionContext() async -> ActionContext {
|
|
||||||
let syncContext: ActionContext = .init(state: .download)
|
|
||||||
|
|
||||||
let syncControlData = SyncControlData(
|
|
||||||
latestBlockHeight: 2000,
|
|
||||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
|
||||||
firstUnenhancedHeight: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
await syncContext.update(syncControlData: syncControlData)
|
|
||||||
|
|
||||||
return syncContext
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupAction(
|
private func setupAction(
|
||||||
_ blockDownloaderMock: BlockDownloaderMock = BlockDownloaderMock(),
|
_ blockDownloaderMock: BlockDownloaderMock = BlockDownloaderMock(),
|
||||||
_ transactionRepositoryMock: TransactionRepositoryMock = TransactionRepositoryMock(),
|
_ transactionRepositoryMock: TransactionRepositoryMock = TransactionRepositoryMock(),
|
||||||
|
|
|
@ -25,14 +25,12 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
func testEnhanceAction_decideWhatToDoNext_NoDownloadAndScanRange() async throws {
|
func testEnhanceAction_decideWhatToDoNext_NoDownloadAndScanRange() async throws {
|
||||||
let enhanceAction = setupAction()
|
let enhanceAction = setupAction()
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = setupActionContext()
|
||||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1)
|
|
||||||
let nextState = await nextContext.state
|
|
||||||
|
|
||||||
XCTAssertTrue(
|
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1)
|
||||||
nextState == .clearCache,
|
|
||||||
"testEnhanceAction_decideWhatToDoNext_NoDownloadAndScanRange is expected to be .clearCache but received \(nextState)"
|
let acResult = nextContext.checkStateIs(.clearCache)
|
||||||
)
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEnhanceAction_decideWhatToDoNext_NothingToDownloadAndScanLeft() async throws {
|
func testEnhanceAction_decideWhatToDoNext_NothingToDownloadAndScanLeft() async throws {
|
||||||
|
@ -40,14 +38,12 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = setupActionContext()
|
||||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 2000)
|
|
||||||
let nextState = await nextContext.state
|
|
||||||
|
|
||||||
XCTAssertTrue(
|
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 2000)
|
||||||
nextState == .clearCache,
|
|
||||||
"testEnhanceAction_decideWhatToDoNext_NothingToDownloadAndScanLeft is expected to be .clearCache but received \(nextState)"
|
let acResult = nextContext.checkStateIs(.clearCache)
|
||||||
)
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEnhanceAction_decideWhatToDoNext_UpdateChainTipExpected() async throws {
|
func testEnhanceAction_decideWhatToDoNext_UpdateChainTipExpected() async throws {
|
||||||
|
@ -55,29 +51,63 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = setupActionContext()
|
||||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1500)
|
|
||||||
let nextState = await nextContext.state
|
|
||||||
|
|
||||||
XCTAssertTrue(
|
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1500)
|
||||||
nextState == .updateChainTip,
|
|
||||||
"testEnhanceAction_decideWhatToDoNext_DownloadExpected is expected to be .updateChainTip but received \(nextState)"
|
let acResult = nextContext.checkStateIs(.updateChainTip)
|
||||||
)
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEnhanceAction_LastScanHeightNil() async throws {
|
||||||
|
let blockEnhancerMock = BlockEnhancerMock()
|
||||||
|
|
||||||
|
let enhanceAction = setupAction(blockEnhancerMock)
|
||||||
|
|
||||||
|
let syncContext = setupActionContext()
|
||||||
|
|
||||||
|
do {
|
||||||
|
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
||||||
|
XCTFail("testEnhanceAction_LastScanHeightNil is expected to fail.")
|
||||||
|
} catch ZcashError.compactBlockProcessorLastScannedHeight {
|
||||||
|
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testEnhanceAction_LastScanHeightNil is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEnhanceAction_firstUnenhancedHeightNil() async throws {
|
||||||
|
let blockEnhancerMock = BlockEnhancerMock()
|
||||||
|
|
||||||
|
let enhanceAction = setupAction(blockEnhancerMock)
|
||||||
|
|
||||||
|
let syncContext = setupActionContext()
|
||||||
|
syncContext.lastScannedHeight = 1
|
||||||
|
|
||||||
|
do {
|
||||||
|
let nextContext = try await enhanceAction.run(with: syncContext) { _ in }
|
||||||
|
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.clearCache)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testEnhanceAction_NoEnhanceRange is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEnhanceAction_NoEnhanceRange() async throws {
|
func testEnhanceAction_NoEnhanceRange() async throws {
|
||||||
let blockEnhancerMock = BlockEnhancerMock()
|
let blockEnhancerMock = BlockEnhancerMock()
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
|
||||||
|
|
||||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
let enhanceAction = setupAction(blockEnhancerMock)
|
||||||
|
|
||||||
let enhanceAction = setupAction(
|
let syncContext = setupActionContext()
|
||||||
blockEnhancerMock,
|
syncContext.lastScannedHeight = 1
|
||||||
transactionRepositoryMock
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 2000,
|
||||||
|
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||||
|
firstUnenhancedHeight: 2000
|
||||||
)
|
)
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
||||||
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||||
|
@ -88,19 +118,18 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
func testEnhanceAction_1000BlocksConditionNotFulfilled() async throws {
|
func testEnhanceAction_1000BlocksConditionNotFulfilled() async throws {
|
||||||
let blockEnhancerMock = BlockEnhancerMock()
|
let blockEnhancerMock = BlockEnhancerMock()
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
|
||||||
|
|
||||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
let enhanceAction = setupAction(blockEnhancerMock)
|
||||||
|
|
||||||
let enhanceAction = setupAction(
|
let syncContext = setupActionContext()
|
||||||
blockEnhancerMock,
|
syncContext.lastScannedHeight = 1000
|
||||||
transactionRepositoryMock
|
syncContext.lastEnhancedHeight = 1000
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 2000,
|
||||||
|
latestScannedHeight: 1000,
|
||||||
|
firstUnenhancedHeight: 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
||||||
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
XCTAssertFalse(blockEnhancerMock.enhanceAtDidEnhanceCalled, "blockEnhancer.enhance() is not expected to be called.")
|
||||||
|
@ -111,9 +140,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
func testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions() async throws {
|
func testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions() async throws {
|
||||||
let blockEnhancerMock = BlockEnhancerMock()
|
let blockEnhancerMock = BlockEnhancerMock()
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
|
||||||
|
|
||||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1500
|
|
||||||
|
|
||||||
let transaction = ZcashTransaction.Overview(
|
let transaction = ZcashTransaction.Overview(
|
||||||
accountId: 0,
|
accountId: 0,
|
||||||
|
@ -138,14 +164,17 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
return [transaction]
|
return [transaction]
|
||||||
}
|
}
|
||||||
|
|
||||||
let enhanceAction = setupAction(
|
let enhanceAction = setupAction(blockEnhancerMock)
|
||||||
blockEnhancerMock,
|
|
||||||
transactionRepositoryMock
|
let syncContext = setupActionContext()
|
||||||
|
syncContext.lastScannedHeight = 2000
|
||||||
|
syncContext.lastEnhancedHeight = 1500
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 2000,
|
||||||
|
latestScannedHeight: 1,
|
||||||
|
firstUnenhancedHeight: 1000
|
||||||
)
|
)
|
||||||
|
syncContext.updateLastEnhancedHeightClosure = { _ in }
|
||||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await enhanceAction.run(with: syncContext) { event in
|
_ = try await enhanceAction.run(with: syncContext) { event in
|
||||||
|
@ -168,9 +197,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
func testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction() async throws {
|
func testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction() async throws {
|
||||||
let blockEnhancerMock = BlockEnhancerMock()
|
let blockEnhancerMock = BlockEnhancerMock()
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
|
||||||
|
|
||||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1500
|
|
||||||
|
|
||||||
let transaction = ZcashTransaction.Overview(
|
let transaction = ZcashTransaction.Overview(
|
||||||
accountId: 0,
|
accountId: 0,
|
||||||
|
@ -203,14 +229,17 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let enhanceAction = setupAction(
|
let enhanceAction = setupAction(blockEnhancerMock)
|
||||||
blockEnhancerMock,
|
|
||||||
transactionRepositoryMock
|
let syncContext = setupActionContext()
|
||||||
|
syncContext.lastScannedHeight = 2000
|
||||||
|
syncContext.lastEnhancedHeight = 1500
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 2000,
|
||||||
|
latestScannedHeight: 1,
|
||||||
|
firstUnenhancedHeight: 1000
|
||||||
)
|
)
|
||||||
|
syncContext.updateLastEnhancedHeightClosure = { _ in }
|
||||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await enhanceAction.run(with: syncContext) { event in
|
_ = try await enhanceAction.run(with: syncContext) { event in
|
||||||
|
@ -229,9 +258,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
|
|
||||||
func testEnhanceAction_EnhancementOfBlocksCalled_usingSmallRange_minedTransaction() async throws {
|
func testEnhanceAction_EnhancementOfBlocksCalled_usingSmallRange_minedTransaction() async throws {
|
||||||
let blockEnhancerMock = BlockEnhancerMock()
|
let blockEnhancerMock = BlockEnhancerMock()
|
||||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
|
||||||
|
|
||||||
transactionRepositoryMock.lastScannedHeightReturnValue = 2000
|
|
||||||
|
|
||||||
let transaction = ZcashTransaction.Overview(
|
let transaction = ZcashTransaction.Overview(
|
||||||
accountId: 0,
|
accountId: 0,
|
||||||
|
@ -264,14 +290,17 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let enhanceAction = setupAction(
|
let enhanceAction = setupAction(blockEnhancerMock)
|
||||||
blockEnhancerMock,
|
|
||||||
transactionRepositoryMock
|
let syncContext = setupActionContext()
|
||||||
|
syncContext.lastScannedHeight = 2000
|
||||||
|
syncContext.lastEnhancedHeight = 1500
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 2000,
|
||||||
|
latestScannedHeight: 1,
|
||||||
|
firstUnenhancedHeight: 1000
|
||||||
)
|
)
|
||||||
|
syncContext.updateLastEnhancedHeightClosure = { _ in }
|
||||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1900, 2000))
|
|
||||||
|
|
||||||
let syncContext = await setupActionContext()
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await enhanceAction.run(with: syncContext) { event in
|
_ = try await enhanceAction.run(with: syncContext) { event in
|
||||||
|
@ -288,19 +317,15 @@ final class EnhanceActionTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupActionContext() async -> ActionContext {
|
private func setupActionContext() -> ActionContextMock {
|
||||||
let syncContext: ActionContext = .init(state: .enhance)
|
let syncContext = ActionContextMock.default()
|
||||||
|
|
||||||
let syncControlData = SyncControlData(
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
latestBlockHeight: 2000,
|
latestBlockHeight: 2000,
|
||||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||||
firstUnenhancedHeight: underlyingEnhanceRange?.lowerBound
|
firstUnenhancedHeight: underlyingEnhanceRange?.lowerBound
|
||||||
)
|
)
|
||||||
|
|
||||||
await syncContext.update(syncControlData: syncControlData)
|
|
||||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
|
||||||
await syncContext.update(lastScannedHeight: underlyingScanRange?.lowerBound ?? -1)
|
|
||||||
|
|
||||||
return syncContext
|
return syncContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,16 +24,13 @@ final class FetchUTXOsActionTests: ZcashTestCase {
|
||||||
|
|
||||||
let fetchUTXOsAction = FetchUTXOsAction(container: mockContainer)
|
let fetchUTXOsAction = FetchUTXOsAction(container: mockContainer)
|
||||||
|
|
||||||
let syncContext: ActionContext = .init(state: .fetchUTXO)
|
let syncContext = ActionContextMock.default()
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
let syncControlData = SyncControlData(
|
|
||||||
latestBlockHeight: 0,
|
latestBlockHeight: 0,
|
||||||
latestScannedHeight: 0,
|
latestScannedHeight: 0,
|
||||||
firstUnenhancedHeight: nil
|
firstUnenhancedHeight: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
await syncContext.update(syncControlData: syncControlData)
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await fetchUTXOsAction.run(with: syncContext) { event in
|
let nextContext = try await fetchUTXOsAction.run(with: syncContext) { event in
|
||||||
guard case .storedUTXOs(let result) = event else {
|
guard case .storedUTXOs(let result) = event else {
|
||||||
|
@ -45,11 +42,9 @@ final class FetchUTXOsActionTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
||||||
XCTAssertTrue(uTXOFetcherMock.fetchDidFetchCalled, "utxoFetcher.fetch() is expected to be called.")
|
XCTAssertTrue(uTXOFetcherMock.fetchDidFetchCalled, "utxoFetcher.fetch() is expected to be called.")
|
||||||
let nextState = await nextContext.state
|
|
||||||
XCTAssertTrue(
|
let acResult = nextContext.checkStateIs(.handleSaplingParams)
|
||||||
nextState == .handleSaplingParams,
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
"nextContext after .fetchUTXO is expected to be .handleSaplingParams but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testFetchUTXOsAction_NextAction is not expected to fail. \(error)")
|
XCTFail("testFetchUTXOsAction_NextAction is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
let context = ActionContextMock.default()
|
||||||
|
let nextContext = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
|
||||||
|
|
||||||
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
||||||
XCTAssertFalse(
|
XCTAssertFalse(
|
||||||
|
@ -44,11 +45,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
||||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
let acResult = nextContext.checkStateIs(.validateServer)
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
nextState == .validateServer,
|
|
||||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testMigrateLegacyCacheDBAction_noCacheDbURL is not expected to fail. \(error)")
|
XCTFail("testMigrateLegacyCacheDBAction_noCacheDbURL is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -68,7 +66,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
let context = ActionContextMock.default()
|
||||||
|
_ = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
|
||||||
XCTFail("testMigrateLegacyCacheDBAction_noFsBlockCacheRoot is expected to fail.")
|
XCTFail("testMigrateLegacyCacheDBAction_noFsBlockCacheRoot is expected to fail.")
|
||||||
} catch ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL {
|
} catch ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL {
|
||||||
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
||||||
|
@ -104,7 +103,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
let context = ActionContextMock.default()
|
||||||
|
let nextContext = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
|
||||||
|
|
||||||
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
||||||
XCTAssertFalse(
|
XCTAssertFalse(
|
||||||
|
@ -114,11 +114,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
||||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
let acResult = nextContext.checkStateIs(.validateServer)
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
nextState == .validateServer,
|
|
||||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testMigrateLegacyCacheDBAction_aliasDoesntMatchDefault is not expected to fail. \(error)")
|
XCTFail("testMigrateLegacyCacheDBAction_aliasDoesntMatchDefault is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -142,7 +139,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
let context = ActionContextMock.default()
|
||||||
|
let nextContext = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
|
||||||
|
|
||||||
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
||||||
XCTAssertFalse(
|
XCTAssertFalse(
|
||||||
|
@ -152,11 +150,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
||||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
let acResult = nextContext.checkStateIs(.validateServer)
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
nextState == .validateServer,
|
|
||||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testMigrateLegacyCacheDBAction_isNotReadableFile is not expected to fail. \(error)")
|
XCTFail("testMigrateLegacyCacheDBAction_isNotReadableFile is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -181,7 +176,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
let context = ActionContextMock.default()
|
||||||
|
_ = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
|
||||||
} catch ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb {
|
} catch ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb {
|
||||||
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
||||||
XCTAssertFalse(
|
XCTAssertFalse(
|
||||||
|
@ -218,17 +214,15 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
let context = ActionContextMock.default()
|
||||||
|
let nextContext = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
|
||||||
|
|
||||||
XCTAssertTrue(compactBlockRepositoryMock.createCalled, "storage.create() is expected to be called.")
|
XCTAssertTrue(compactBlockRepositoryMock.createCalled, "storage.create() is expected to be called.")
|
||||||
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
||||||
XCTAssertTrue(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is expected to be called.")
|
XCTAssertTrue(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is expected to be called.")
|
||||||
|
|
||||||
let nextState = await nextContext.state
|
let acResult = nextContext.checkStateIs(.validateServer)
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
nextState == .validateServer,
|
|
||||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testMigrateLegacyCacheDBAction_nextAction is not expected to fail. \(error)")
|
XCTFail("testMigrateLegacyCacheDBAction_nextAction is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,247 @@
|
||||||
|
//
|
||||||
|
// ProcessSuggestedScanRangesActionTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 25.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import TestUtils
|
||||||
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
|
final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
||||||
|
var underlyingChainName = ""
|
||||||
|
var underlyingNetworkType = NetworkType.testnet
|
||||||
|
var underlyingSaplingActivationHeight: BlockHeight?
|
||||||
|
var underlyingConsensusBranchID = ""
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
underlyingChainName = "test"
|
||||||
|
underlyingNetworkType = .testnet
|
||||||
|
underlyingSaplingActivationHeight = nil
|
||||||
|
underlyingConsensusBranchID = "c2d6d0b4"
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProcessSuggestedScanRangesAction_EmptyScanRanges() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
await tupple.rustBackendMock.setSuggestScanRangesClosure( { [] } )
|
||||||
|
|
||||||
|
let processSuggestedScanRangesActionAction = tupple.action
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
|
||||||
|
let nextContext = try await processSuggestedScanRangesActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertFalse(
|
||||||
|
loggerMock.debugFileFunctionLineCalled,
|
||||||
|
"logger.debug() is not expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.finished)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testProcessSuggestedScanRangesAction_EmptyScanRanges is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProcessSuggestedScanRangesAction_VerifyScanRangeSetTotalProgressRange() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
await tupple.rustBackendMock.setSuggestScanRangesClosure( { [
|
||||||
|
ScanRange(range: 0..<10, priority: .verify)
|
||||||
|
] } )
|
||||||
|
|
||||||
|
let processSuggestedScanRangesActionAction = tupple.action
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.updateLastScannedHeightClosure = { _ in }
|
||||||
|
context.updateLastDownloadedHeightClosure = { _ in }
|
||||||
|
context.updateSyncControlDataClosure = { _ in }
|
||||||
|
context.underlyingTotalProgressRange = 0...0
|
||||||
|
context.updateTotalProgressRangeClosure = { _ in }
|
||||||
|
context.updateRequestedRewindHeightClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await processSuggestedScanRangesActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
loggerMock.debugFileFunctionLineCalled,
|
||||||
|
"logger.debug() is not expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
if let nextContextMock = nextContext as? ActionContextMock {
|
||||||
|
XCTAssertTrue(
|
||||||
|
nextContextMock.updateRequestedRewindHeightCallsCount == 1,
|
||||||
|
"context.update(requestedRewindHeight:) is expected to be called exactly once."
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContext` is not the ActionContextMock")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
loggerMock.debugFileFunctionLineCalled,
|
||||||
|
"logger.debug() is not expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
if let infoArguments = loggerMock.infoFileFunctionLineReceivedArguments {
|
||||||
|
XCTAssertTrue(infoArguments.message.contains("Setting the total range for Spend before Sync to"))
|
||||||
|
} else {
|
||||||
|
XCTFail("`infoArguments` unavailable.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.rewind)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testProcessSuggestedScanRangesAction_VerifyScanRangeSetTotalProgressRange is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProcessSuggestedScanRangesAction_VerifyScanRangeTotalProgressRangeSkipped() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
await tupple.rustBackendMock.setSuggestScanRangesClosure( { [
|
||||||
|
ScanRange(range: 0..<10, priority: .verify)
|
||||||
|
] } )
|
||||||
|
|
||||||
|
let processSuggestedScanRangesActionAction = tupple.action
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.updateLastScannedHeightClosure = { _ in }
|
||||||
|
context.updateLastDownloadedHeightClosure = { _ in }
|
||||||
|
context.updateSyncControlDataClosure = { _ in }
|
||||||
|
context.underlyingTotalProgressRange = 1...1
|
||||||
|
context.updateRequestedRewindHeightClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await processSuggestedScanRangesActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
loggerMock.debugFileFunctionLineCalled,
|
||||||
|
"logger.debug() is not expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
if let nextContextMock = nextContext as? ActionContextMock {
|
||||||
|
XCTAssertTrue(
|
||||||
|
nextContextMock.updateRequestedRewindHeightCallsCount == 1,
|
||||||
|
"context.update(requestedRewindHeight:) is expected to be called exactly once."
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContext` is not the ActionContextMock")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let infoArguments = loggerMock.infoFileFunctionLineReceivedArguments {
|
||||||
|
XCTAssertFalse(infoArguments.message.contains("Setting the total range for Spend before Sync to"))
|
||||||
|
} else {
|
||||||
|
XCTFail("`infoArguments` unavailable.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.rewind)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testProcessSuggestedScanRangesAction_VerifyScanRangeTotalProgressRangeSkipped is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProcessSuggestedScanRangesAction_ChainTipScanRange() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
await tupple.rustBackendMock.setSuggestScanRangesClosure( { [
|
||||||
|
ScanRange(range: 0..<10, priority: .chainTip)
|
||||||
|
] } )
|
||||||
|
|
||||||
|
let processSuggestedScanRangesActionAction = tupple.action
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.updateLastScannedHeightClosure = { _ in }
|
||||||
|
context.updateLastDownloadedHeightClosure = { _ in }
|
||||||
|
context.updateSyncControlDataClosure = { _ in }
|
||||||
|
context.underlyingTotalProgressRange = 1...1
|
||||||
|
context.updateRequestedRewindHeightClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await processSuggestedScanRangesActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
if let nextContextMock = nextContext as? ActionContextMock {
|
||||||
|
XCTAssertFalse(
|
||||||
|
nextContextMock.updateRequestedRewindHeightCalled,
|
||||||
|
"context.update(requestedRewindHeight:) is not expected to be called"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContext` is not the ActionContextMock")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
loggerMock.debugFileFunctionLineCalled,
|
||||||
|
"logger.debug() is not expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
if let infoArguments = loggerMock.infoFileFunctionLineReceivedArguments {
|
||||||
|
XCTAssertFalse(infoArguments.message.contains("Setting the total range for Spend before Sync to"))
|
||||||
|
} else {
|
||||||
|
XCTFail("`infoArguments` unavailable.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.download)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testProcessSuggestedScanRangesAction_ChainTipScanRange is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupAction(
|
||||||
|
_ loggerMock: LoggerMock = LoggerMock()
|
||||||
|
) -> (
|
||||||
|
action: ProcessSuggestedScanRangesAction,
|
||||||
|
serviceMock: LightWalletServiceMock,
|
||||||
|
rustBackendMock: ZcashRustBackendWeldingMock
|
||||||
|
) {
|
||||||
|
let config: CompactBlockProcessor.Configuration = .standard(
|
||||||
|
for: ZcashNetworkBuilder.network(for: underlyingNetworkType), walletBirthday: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
let rustBackendMock = ZcashRustBackendWeldingMock(
|
||||||
|
consensusBranchIdForHeightClosure: { height in
|
||||||
|
XCTAssertEqual(height, 2, "")
|
||||||
|
return -1026109260
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let lightWalletdInfoMock = LightWalletdInfoMock()
|
||||||
|
lightWalletdInfoMock.underlyingConsensusBranchID = underlyingConsensusBranchID
|
||||||
|
lightWalletdInfoMock.underlyingSaplingActivationHeight = UInt64(underlyingSaplingActivationHeight ?? config.saplingActivation)
|
||||||
|
lightWalletdInfoMock.underlyingBlockHeight = 2
|
||||||
|
lightWalletdInfoMock.underlyingChainName = underlyingChainName
|
||||||
|
|
||||||
|
let serviceMock = LightWalletServiceMock()
|
||||||
|
serviceMock.getInfoReturnValue = lightWalletdInfoMock
|
||||||
|
|
||||||
|
mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in rustBackendMock }
|
||||||
|
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in serviceMock }
|
||||||
|
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||||
|
|
||||||
|
return (
|
||||||
|
action:ProcessSuggestedScanRangesAction(container: mockContainer),
|
||||||
|
serviceMock: serviceMock,
|
||||||
|
rustBackendMock: rustBackendMock
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
//
|
||||||
|
// RewindActionTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 25.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import TestUtils
|
||||||
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
|
final class RewindActionTests: ZcashTestCase {
|
||||||
|
var underlyingChainName = ""
|
||||||
|
var underlyingNetworkType = NetworkType.testnet
|
||||||
|
var underlyingSaplingActivationHeight: BlockHeight?
|
||||||
|
var underlyingConsensusBranchID = ""
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
underlyingChainName = "test"
|
||||||
|
underlyingNetworkType = .testnet
|
||||||
|
underlyingSaplingActivationHeight = nil
|
||||||
|
underlyingConsensusBranchID = "c2d6d0b4"
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRewindAction_requestedRewindHeightNil() async throws {
|
||||||
|
let blockDownloaderMock = BlockDownloaderMock()
|
||||||
|
|
||||||
|
let rewindActionAction = await setupAction(blockDownloaderMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
|
||||||
|
let nextContext = try await rewindActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertFalse(
|
||||||
|
blockDownloaderMock.rewindLatestDownloadedBlockHeightCalled,
|
||||||
|
"downloader.rewind(latestDownloadedBlockHeight:) is not expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.download)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testRewindAction_requestedRewindHeightNil is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRewindAction_FullPass() async throws {
|
||||||
|
let blockDownloaderMock = BlockDownloaderMock()
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||||
|
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
blockDownloaderMock.rewindLatestDownloadedBlockHeightClosure = { _ in }
|
||||||
|
blockDownloaderServiceMock.rewindToClosure = { _ in }
|
||||||
|
|
||||||
|
let rewindActionAction = await setupAction(
|
||||||
|
blockDownloaderMock,
|
||||||
|
loggerMock,
|
||||||
|
blockDownloaderServiceMock
|
||||||
|
)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.requestedRewindHeight = 1
|
||||||
|
|
||||||
|
let nextContext = try await rewindActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
blockDownloaderMock.rewindLatestDownloadedBlockHeightCallsCount == 1,
|
||||||
|
"downloader.rewind(latestDownloadedBlockHeight:) is expected to be called."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
loggerMock.debugFileFunctionLineCallsCount == 1,
|
||||||
|
"logger.debug() is expected to be called."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
blockDownloaderServiceMock.rewindToCallsCount == 1,
|
||||||
|
"downloaderService.rewind(to:) is expected to be called."
|
||||||
|
)
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.download)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testRewindAction_FullPass is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupAction(
|
||||||
|
_ blockDownloaderMock: BlockDownloaderMock = BlockDownloaderMock(),
|
||||||
|
_ loggerMock: LoggerMock = LoggerMock(),
|
||||||
|
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||||
|
) async -> RewindAction {
|
||||||
|
let rustBackendMock = ZcashRustBackendWeldingMock(
|
||||||
|
consensusBranchIdForHeightClosure: { height in
|
||||||
|
XCTAssertEqual(height, 2, "")
|
||||||
|
return -1026109260
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await rustBackendMock.setRewindToHeightHeightClosure( { _ in } )
|
||||||
|
|
||||||
|
mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in rustBackendMock }
|
||||||
|
mockContainer.mock(type: BlockDownloaderService.self, isSingleton: true) { _ in blockDownloaderServiceMock }
|
||||||
|
mockContainer.mock(type: BlockDownloader.self, isSingleton: true) { _ in blockDownloaderMock }
|
||||||
|
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||||
|
|
||||||
|
return RewindAction(container: mockContainer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,29 +10,62 @@ import XCTest
|
||||||
@testable import ZcashLightClientKit
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
final class SaplingParamsActionTests: ZcashTestCase {
|
final class SaplingParamsActionTests: ZcashTestCase {
|
||||||
func testSaplingParamsAction_NextAction() async throws {
|
func testSaplingParamsAction_NextAction_linearSync() async throws {
|
||||||
let loggerMock = LoggerMock()
|
let loggerMock = LoggerMock()
|
||||||
let saplingParametersHandlerMock = SaplingParametersHandlerMock()
|
let saplingParametersHandlerMock = SaplingParametersHandlerMock()
|
||||||
|
|
||||||
|
let saplingParamsActionAction = setupAction(saplingParametersHandlerMock, loggerMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.underlyingPreferredSyncAlgorithm = .linear
|
||||||
|
|
||||||
|
let nextContext = try await saplingParamsActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
||||||
|
XCTAssertTrue(saplingParametersHandlerMock.handleIfNeededCalled, "saplingParametersHandler.handleIfNeeded() is expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.computeSyncControlData)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testSaplingParamsAction_NextAction is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSaplingParamsAction_NextAction_SpendBeforeSync() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
let saplingParametersHandlerMock = SaplingParametersHandlerMock()
|
||||||
|
|
||||||
|
let saplingParamsActionAction = setupAction(saplingParametersHandlerMock, loggerMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
|
||||||
|
|
||||||
|
let nextContext = try await saplingParamsActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
||||||
|
XCTAssertTrue(saplingParametersHandlerMock.handleIfNeededCalled, "saplingParametersHandler.handleIfNeeded() is expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.updateSubtreeRoots)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testSaplingParamsAction_NextAction is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupAction(
|
||||||
|
_ saplingParametersHandlerMock: SaplingParametersHandlerMock = SaplingParametersHandlerMock(),
|
||||||
|
_ loggerMock: LoggerMock = LoggerMock()
|
||||||
|
) -> SaplingParamsAction {
|
||||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
saplingParametersHandlerMock.handleIfNeededClosure = { }
|
saplingParametersHandlerMock.handleIfNeededClosure = { }
|
||||||
|
|
||||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||||
mockContainer.mock(type: SaplingParametersHandler.self, isSingleton: true) { _ in saplingParametersHandlerMock }
|
mockContainer.mock(type: SaplingParametersHandler.self, isSingleton: true) { _ in saplingParametersHandlerMock }
|
||||||
|
|
||||||
let saplingParamsActionAction = SaplingParamsAction(container: mockContainer)
|
return SaplingParamsAction(
|
||||||
|
container: mockContainer
|
||||||
do {
|
|
||||||
let nextContext = try await saplingParamsActionAction.run(with: .init(state: .handleSaplingParams)) { _ in }
|
|
||||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
|
||||||
XCTAssertTrue(saplingParametersHandlerMock.handleIfNeededCalled, "saplingParametersHandler.handleIfNeeded() is expected to be called.")
|
|
||||||
let nextState = await nextContext.state
|
|
||||||
XCTAssertTrue(
|
|
||||||
nextState == .computeSyncControlData,
|
|
||||||
"nextContext after .handleSaplingParams is expected to be .computeSyncControlData but received \(nextState)"
|
|
||||||
)
|
)
|
||||||
} catch {
|
|
||||||
XCTFail("testSaplingParamsAction_NextAction is not expected to fail. \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,15 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
blockScannerMock.scanBlocksAtTotalProgressRangeDidScanClosure = { _, _, _ in 2 }
|
blockScannerMock.scanBlocksAtTotalProgressRangeDidScanClosure = { _, _, _ in 2 }
|
||||||
|
|
||||||
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
||||||
let syncContext = await setupActionContext()
|
|
||||||
|
|
||||||
await syncContext.update(lastScannedHeight: 1500)
|
let syncContext = ActionContextMock.default()
|
||||||
|
syncContext.lastScannedHeight = 1500
|
||||||
|
syncContext.underlyingTotalProgressRange = 1000...2000
|
||||||
|
syncContext.underlyingSyncControlData = SyncControlData(
|
||||||
|
latestBlockHeight: 2000,
|
||||||
|
latestScannedHeight: 1000,
|
||||||
|
firstUnenhancedHeight: nil
|
||||||
|
)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await scanAction.run(with: syncContext) { event in
|
let nextContext = try await scanAction.run(with: syncContext) { event in
|
||||||
|
@ -36,11 +42,9 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
||||||
XCTAssertTrue(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
|
XCTAssertTrue(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
|
||||||
let nextState = await nextContext.state
|
|
||||||
XCTAssertTrue(
|
let acResult = nextContext.checkStateIs(.clearAlreadyScannedBlocks)
|
||||||
nextState == .clearAlreadyScannedBlocks,
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
"nextContext after .scan is expected to be .clearAlreadyScannedBlocks but received \(nextState)"
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testScanAction_NextAction is not expected to fail. \(error)")
|
XCTFail("testScanAction_NextAction is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -52,7 +56,7 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
let loggerMock = LoggerMock()
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
||||||
let syncContext: ActionContext = .init(state: .scan)
|
let syncContext = ActionContextMock.default()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await scanAction.run(with: syncContext) { _ in }
|
_ = try await scanAction.run(with: syncContext) { _ in }
|
||||||
|
@ -75,7 +79,7 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
transactionRepositoryMock.lastScannedHeightReturnValue = 2001
|
transactionRepositoryMock.lastScannedHeightReturnValue = 2001
|
||||||
|
|
||||||
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
||||||
let syncContext = await setupActionContext()
|
let syncContext = ActionContextMock.default()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await scanAction.run(with: syncContext) { _ in }
|
_ = try await scanAction.run(with: syncContext) { _ in }
|
||||||
|
@ -104,19 +108,4 @@ final class ScanActionTests: ZcashTestCase {
|
||||||
configProvider: CompactBlockProcessor.ConfigProvider(config: config)
|
configProvider: CompactBlockProcessor.ConfigProvider(config: config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupActionContext() async -> ActionContext {
|
|
||||||
let syncContext: ActionContext = .init(state: .scan)
|
|
||||||
|
|
||||||
let syncControlData = SyncControlData(
|
|
||||||
latestBlockHeight: 2000,
|
|
||||||
latestScannedHeight: 1000,
|
|
||||||
firstUnenhancedHeight: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
await syncContext.update(syncControlData: syncControlData)
|
|
||||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
|
||||||
|
|
||||||
return syncContext
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
//
|
||||||
|
// UpdateChainTipActionTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 25.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import TestUtils
|
||||||
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
|
final class UpdateChainTipActionTests: ZcashTestCase {
|
||||||
|
var underlyingChainName = ""
|
||||||
|
var underlyingNetworkType = NetworkType.testnet
|
||||||
|
var underlyingSaplingActivationHeight: BlockHeight?
|
||||||
|
var underlyingConsensusBranchID = ""
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
underlyingChainName = "test"
|
||||||
|
underlyingNetworkType = .testnet
|
||||||
|
underlyingSaplingActivationHeight = nil
|
||||||
|
underlyingConsensusBranchID = "c2d6d0b4"
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateChainTipAction_UpdateChainTipTimeTriggered() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let updateChainTipAction = await setupAction(loggerMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.prevState = .idle
|
||||||
|
context.underlyingLastChainTipUpdateTime = 0.0
|
||||||
|
context.updateLastChainTipUpdateTimeClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await updateChainTipAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.processSuggestedScanRanges)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testUpdateChainTipAction_UpdateChainTipTimeTriggered is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateChainTipAction_UpdateChainTipPrevActionTriggered() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let updateChainTipAction = await setupAction(loggerMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.prevState = .updateSubtreeRoots
|
||||||
|
context.underlyingLastChainTipUpdateTime = Date().timeIntervalSince1970
|
||||||
|
context.updateLastChainTipUpdateTimeClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await updateChainTipAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.processSuggestedScanRanges)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testUpdateChainTipAction_UpdateChainTipPrevActionTriggered is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateChainTipAction_UpdateChainTipSkipped() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let updateChainTipAction = await setupAction(loggerMock)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.prevState = .enhance
|
||||||
|
context.underlyingLastChainTipUpdateTime = Date().timeIntervalSince1970
|
||||||
|
context.updateLastChainTipUpdateTimeClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await updateChainTipAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.download)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testUpdateChainTipAction_UpdateChainTipSkipped is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupAction(
|
||||||
|
_ loggerMock: LoggerMock = LoggerMock()
|
||||||
|
) async -> UpdateChainTipAction {
|
||||||
|
let config: CompactBlockProcessor.Configuration = .standard(
|
||||||
|
for: ZcashNetworkBuilder.network(for: underlyingNetworkType), walletBirthday: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
let rustBackendMock = ZcashRustBackendWeldingMock(
|
||||||
|
consensusBranchIdForHeightClosure: { height in
|
||||||
|
XCTAssertEqual(height, 2, "")
|
||||||
|
return -1026109260
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await rustBackendMock.setUpdateChainTipHeightClosure( { _ in } )
|
||||||
|
|
||||||
|
let lightWalletdInfoMock = LightWalletdInfoMock()
|
||||||
|
lightWalletdInfoMock.underlyingConsensusBranchID = underlyingConsensusBranchID
|
||||||
|
lightWalletdInfoMock.underlyingSaplingActivationHeight = UInt64(underlyingSaplingActivationHeight ?? config.saplingActivation)
|
||||||
|
lightWalletdInfoMock.underlyingBlockHeight = 2
|
||||||
|
lightWalletdInfoMock.underlyingChainName = underlyingChainName
|
||||||
|
|
||||||
|
let serviceMock = LightWalletServiceMock()
|
||||||
|
serviceMock.getInfoReturnValue = lightWalletdInfoMock
|
||||||
|
serviceMock.latestBlockHeightReturnValue = 1
|
||||||
|
|
||||||
|
mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in rustBackendMock }
|
||||||
|
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in serviceMock }
|
||||||
|
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||||
|
|
||||||
|
return UpdateChainTipAction(container: mockContainer)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
//
|
||||||
|
// UpdateSubtreeRootsActionTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 25.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import TestUtils
|
||||||
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
|
final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
||||||
|
var underlyingChainName = ""
|
||||||
|
var underlyingNetworkType = NetworkType.testnet
|
||||||
|
var underlyingSaplingActivationHeight: BlockHeight?
|
||||||
|
var underlyingConsensusBranchID = ""
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
|
||||||
|
underlyingChainName = "test"
|
||||||
|
underlyingNetworkType = .testnet
|
||||||
|
underlyingSaplingActivationHeight = nil
|
||||||
|
underlyingConsensusBranchID = "c2d6d0b4"
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateSubtreeRootsAction_getSubtreeRootsFailure() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
let updateSubtreeRootsActionAction = tupple.action
|
||||||
|
tupple.serviceMock.getSubtreeRootsClosure = { _ in
|
||||||
|
AsyncThrowingStream { continuation in continuation.finish(throwing: ZcashError.serviceSubmitFailed(.invalidBlock)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.updateSupportedSyncAlgorithmClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
if let nextContextMock = nextContext as? ActionContextMock {
|
||||||
|
if let supportedSyncAlgorithm = nextContextMock.updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm {
|
||||||
|
XCTAssertTrue(
|
||||||
|
supportedSyncAlgorithm == .linear,
|
||||||
|
"supportedSyncAlgorithm is expected to be .linear but received \(supportedSyncAlgorithm)"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContextMock` supportedSyncAlgorithm not set")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContext` is not the ActionContextMock")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let debugArguments = loggerMock.debugFileFunctionLineReceivedArguments {
|
||||||
|
XCTAssertTrue(debugArguments.message.contains("getSubtreeRoots failed with error"))
|
||||||
|
} else {
|
||||||
|
XCTFail("`debugArguments` unavailable.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.computeSyncControlData)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testUpdateSubtreeRootsAction_getSubtreeRootsFailure is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateSubtreeRootsAction_getSubtreeRootsEmpty() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
let updateSubtreeRootsActionAction = tupple.action
|
||||||
|
tupple.serviceMock.getSubtreeRootsClosure = { _ in
|
||||||
|
AsyncThrowingStream { continuation in continuation.finish() }
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.updateSupportedSyncAlgorithmClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
if let nextContextMock = nextContext as? ActionContextMock {
|
||||||
|
if let supportedSyncAlgorithm = nextContextMock.updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm {
|
||||||
|
XCTAssertTrue(
|
||||||
|
supportedSyncAlgorithm == .linear,
|
||||||
|
"supportedSyncAlgorithm is expected to be .linear but received \(supportedSyncAlgorithm)"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContextMock` supportedSyncAlgorithm not set")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContext` is not the ActionContextMock")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug() is not expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.computeSyncControlData)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testUpdateSubtreeRootsAction_getSubtreeRootsEmpty is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateSubtreeRootsAction_RootsAvailablePutRootsSuccess() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
let updateSubtreeRootsActionAction = tupple.action
|
||||||
|
tupple.serviceMock.getSubtreeRootsClosure = { _ in
|
||||||
|
AsyncThrowingStream { continuation in
|
||||||
|
continuation.yield(SubtreeRoot())
|
||||||
|
continuation.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await tupple.rustBackendMock.setPutSaplingSubtreeRootsStartIndexRootsClosure( { _, _ in } )
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.updateSupportedSyncAlgorithmClosure = { _ in }
|
||||||
|
|
||||||
|
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
if let nextContextMock = nextContext as? ActionContextMock {
|
||||||
|
if let supportedSyncAlgorithm = nextContextMock.updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm {
|
||||||
|
XCTAssertTrue(
|
||||||
|
supportedSyncAlgorithm == .spendBeforeSync,
|
||||||
|
"supportedSyncAlgorithm is expected to be .linear but received \(supportedSyncAlgorithm)"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContextMock` supportedSyncAlgorithm not set")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XCTFail("`nextContext` is not the ActionContextMock")
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug() is not expected to be called.")
|
||||||
|
|
||||||
|
let acResult = nextContext.checkStateIs(.updateChainTip)
|
||||||
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
|
} catch {
|
||||||
|
XCTFail("testUpdateSubtreeRootsAction_RootsAvailablePutRootsSuccess is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateSubtreeRootsAction_RootsAvailablePutRootsFailure() async throws {
|
||||||
|
let loggerMock = LoggerMock()
|
||||||
|
|
||||||
|
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||||
|
|
||||||
|
let tupple = setupAction(loggerMock)
|
||||||
|
let updateSubtreeRootsActionAction = tupple.action
|
||||||
|
tupple.serviceMock.getSubtreeRootsClosure = { _ in
|
||||||
|
AsyncThrowingStream { continuation in
|
||||||
|
continuation.yield(SubtreeRoot())
|
||||||
|
continuation.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await tupple.rustBackendMock.setPutSaplingSubtreeRootsStartIndexRootsThrowableError("putSaplingFailed")
|
||||||
|
|
||||||
|
do {
|
||||||
|
let context = ActionContextMock.default()
|
||||||
|
context.updateSupportedSyncAlgorithmClosure = { _ in }
|
||||||
|
|
||||||
|
_ = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
|
||||||
|
|
||||||
|
XCTFail("updateSubtreeRootsActionAction.run(with:) is excpected to fail but didn't.")
|
||||||
|
} catch ZcashError.compactBlockProcessorPutSaplingSubtreeRoots {
|
||||||
|
// this is expected result of this test
|
||||||
|
} catch {
|
||||||
|
XCTFail("testUpdateSubtreeRootsAction_RootsAvailablePutRootsFailure is not expected to fail. \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupAction(
|
||||||
|
_ loggerMock: LoggerMock = LoggerMock()
|
||||||
|
) -> (
|
||||||
|
action: UpdateSubtreeRootsAction,
|
||||||
|
serviceMock: LightWalletServiceMock,
|
||||||
|
rustBackendMock: ZcashRustBackendWeldingMock
|
||||||
|
) {
|
||||||
|
let config: CompactBlockProcessor.Configuration = .standard(
|
||||||
|
for: ZcashNetworkBuilder.network(for: underlyingNetworkType), walletBirthday: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
let rustBackendMock = ZcashRustBackendWeldingMock(
|
||||||
|
consensusBranchIdForHeightClosure: { height in
|
||||||
|
XCTAssertEqual(height, 2, "")
|
||||||
|
return -1026109260
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let lightWalletdInfoMock = LightWalletdInfoMock()
|
||||||
|
lightWalletdInfoMock.underlyingConsensusBranchID = underlyingConsensusBranchID
|
||||||
|
lightWalletdInfoMock.underlyingSaplingActivationHeight = UInt64(underlyingSaplingActivationHeight ?? config.saplingActivation)
|
||||||
|
lightWalletdInfoMock.underlyingBlockHeight = 2
|
||||||
|
lightWalletdInfoMock.underlyingChainName = underlyingChainName
|
||||||
|
|
||||||
|
let serviceMock = LightWalletServiceMock()
|
||||||
|
serviceMock.getInfoReturnValue = lightWalletdInfoMock
|
||||||
|
|
||||||
|
mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in rustBackendMock }
|
||||||
|
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in serviceMock }
|
||||||
|
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||||
|
|
||||||
|
return (
|
||||||
|
action:
|
||||||
|
UpdateSubtreeRootsAction(
|
||||||
|
container: mockContainer,
|
||||||
|
configProvider: CompactBlockProcessor.ConfigProvider(config: config)),
|
||||||
|
serviceMock: serviceMock,
|
||||||
|
rustBackendMock: rustBackendMock
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,12 +28,11 @@ final class ValidateServerActionTests: ZcashTestCase {
|
||||||
let validateServerAction = setupAction()
|
let validateServerAction = setupAction()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let nextContext = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
let context = ActionContextMock.default()
|
||||||
let nextState = await nextContext.state
|
let nextContext = try await validateServerAction.run(with: context) { _ in }
|
||||||
XCTAssertTrue(
|
|
||||||
nextState == .fetchUTXO,
|
let acResult = nextContext.checkStateIs(.fetchUTXO)
|
||||||
"nextContext after .validateServer is expected to be .fetchUTXO but received \(nextState)"
|
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("testValidateServerAction_NextAction is not expected to fail. \(error)")
|
XCTFail("testValidateServerAction_NextAction is not expected to fail. \(error)")
|
||||||
}
|
}
|
||||||
|
@ -45,7 +44,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
||||||
let validateServerAction = setupAction()
|
let validateServerAction = setupAction()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||||
XCTFail("testValidateServerAction_ChainNameError is expected to fail.")
|
XCTFail("testValidateServerAction_ChainNameError is expected to fail.")
|
||||||
} catch ZcashError.compactBlockProcessorChainName(let chainName) {
|
} catch ZcashError.compactBlockProcessorChainName(let chainName) {
|
||||||
XCTAssertEqual(chainName, "invalid")
|
XCTAssertEqual(chainName, "invalid")
|
||||||
|
@ -63,7 +62,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
||||||
let validateServerAction = setupAction()
|
let validateServerAction = setupAction()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||||
XCTFail("testValidateServerAction_NetworkMatchError is expected to fail.")
|
XCTFail("testValidateServerAction_NetworkMatchError is expected to fail.")
|
||||||
} catch let ZcashError.compactBlockProcessorNetworkMismatch(expected, found) {
|
} catch let ZcashError.compactBlockProcessorNetworkMismatch(expected, found) {
|
||||||
XCTAssertEqual(expected, .mainnet)
|
XCTAssertEqual(expected, .mainnet)
|
||||||
|
@ -82,7 +81,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
||||||
let validateServerAction = setupAction()
|
let validateServerAction = setupAction()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||||
XCTFail("testValidateServerAction_SaplingActivationError is expected to fail.")
|
XCTFail("testValidateServerAction_SaplingActivationError is expected to fail.")
|
||||||
} catch let ZcashError.compactBlockProcessorSaplingActivationMismatch(expected, found) {
|
} catch let ZcashError.compactBlockProcessorSaplingActivationMismatch(expected, found) {
|
||||||
XCTAssertEqual(expected, 280_000)
|
XCTAssertEqual(expected, 280_000)
|
||||||
|
@ -101,7 +100,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
||||||
let validateServerAction = setupAction()
|
let validateServerAction = setupAction()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||||
XCTFail("testValidateServerAction_ConsensusBranchIDError_InvalidRemoteBranch is expected to fail.")
|
XCTFail("testValidateServerAction_ConsensusBranchIDError_InvalidRemoteBranch is expected to fail.")
|
||||||
} catch ZcashError.compactBlockProcessorConsensusBranchID {
|
} catch ZcashError.compactBlockProcessorConsensusBranchID {
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -118,7 +117,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
||||||
let validateServerAction = setupAction()
|
let validateServerAction = setupAction()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||||
XCTFail("testValidateServerAction_ConsensusBranchIDError_ValidRemoteBranch is expected to fail.")
|
XCTFail("testValidateServerAction_ConsensusBranchIDError_ValidRemoteBranch is expected to fail.")
|
||||||
} catch let ZcashError.compactBlockProcessorWrongConsensusBranchId(expected, found) {
|
} catch let ZcashError.compactBlockProcessorWrongConsensusBranchId(expected, found) {
|
||||||
XCTAssertEqual(expected, -1026109260)
|
XCTAssertEqual(expected, -1026109260)
|
||||||
|
|
|
@ -475,6 +475,25 @@ class SynchronizerOfflineTests: ZcashTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testLinearIsSetAsDefault() async throws {
|
||||||
|
let databases = TemporaryDbBuilder.build()
|
||||||
|
let initializer = Initializer(
|
||||||
|
cacheDbURL: nil,
|
||||||
|
fsBlockDbRoot: databases.fsCacheDbRoot,
|
||||||
|
generalStorageURL: testGeneralStorageDirectory,
|
||||||
|
dataDbURL: databases.dataDB,
|
||||||
|
endpoint: LightWalletEndpoint(address: "lightwalletd.electriccoin.co", port: 9067, secure: true),
|
||||||
|
network: ZcashNetworkBuilder.network(for: .mainnet),
|
||||||
|
spendParamsURL: try __spendParamsURL(),
|
||||||
|
outputParamsURL: try __outputParamsURL(),
|
||||||
|
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
|
||||||
|
alias: .default,
|
||||||
|
loggingPolicy: .default(.debug)
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertTrue(initializer.syncAlgorithm == .linear, "Spend before Sync is a beta feature so linear syncing is set to default.")
|
||||||
|
}
|
||||||
|
|
||||||
func synchronizerState(for internalSyncStatus: InternalSyncStatus) -> SynchronizerState {
|
func synchronizerState(for internalSyncStatus: InternalSyncStatus) -> SynchronizerState {
|
||||||
SynchronizerState(
|
SynchronizerState(
|
||||||
syncSessionID: .nullID,
|
syncSessionID: .nullID,
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// ActionContext+tests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 24.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
|
enum ActionContextResult: Equatable {
|
||||||
|
case `true`
|
||||||
|
case isNotMock
|
||||||
|
case called(Int)
|
||||||
|
case nilState
|
||||||
|
case wrongState(CBPState)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActionContext {
|
||||||
|
func checkStateIs(_ expectedState: CBPState) -> ActionContextResult {
|
||||||
|
guard let nextContextMock = self as? ActionContextMock else {
|
||||||
|
return .isNotMock
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextContextMock.updateStateCallsCount != 1 {
|
||||||
|
return .called(nextContextMock.updateStateCallsCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let updateStateReceivedState = nextContextMock.updateStateReceivedState else {
|
||||||
|
return .nilState
|
||||||
|
}
|
||||||
|
|
||||||
|
if updateStateReceivedState != expectedState {
|
||||||
|
return .wrongState(updateStateReceivedState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActionContextMock {
|
||||||
|
static func `default`() -> ActionContextMock {
|
||||||
|
let context = ActionContextMock()
|
||||||
|
|
||||||
|
context.underlyingState = .idle
|
||||||
|
context.updateStateClosure = { _ in }
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
@testable import ZcashLightClientKit
|
@testable import ZcashLightClientKit
|
||||||
|
|
||||||
|
extension ActionContext { }
|
||||||
extension BlockDownloader { }
|
extension BlockDownloader { }
|
||||||
extension BlockDownloaderService { }
|
extension BlockDownloaderService { }
|
||||||
extension BlockEnhancer { }
|
extension BlockEnhancer { }
|
||||||
|
|
|
@ -7,6 +7,193 @@ import Foundation
|
||||||
|
|
||||||
|
|
||||||
// MARK: - AutoMockable protocols
|
// MARK: - AutoMockable protocols
|
||||||
|
class ActionContextMock: ActionContext {
|
||||||
|
|
||||||
|
|
||||||
|
init(
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
var state: CBPState {
|
||||||
|
get { return underlyingState }
|
||||||
|
}
|
||||||
|
var underlyingState: CBPState!
|
||||||
|
var prevState: CBPState?
|
||||||
|
var syncControlData: SyncControlData {
|
||||||
|
get { return underlyingSyncControlData }
|
||||||
|
}
|
||||||
|
var underlyingSyncControlData: SyncControlData!
|
||||||
|
var preferredSyncAlgorithm: SyncAlgorithm {
|
||||||
|
get { return underlyingPreferredSyncAlgorithm }
|
||||||
|
}
|
||||||
|
var underlyingPreferredSyncAlgorithm: SyncAlgorithm!
|
||||||
|
var supportedSyncAlgorithm: SyncAlgorithm?
|
||||||
|
var requestedRewindHeight: BlockHeight?
|
||||||
|
var totalProgressRange: CompactBlockRange {
|
||||||
|
get { return underlyingTotalProgressRange }
|
||||||
|
}
|
||||||
|
var underlyingTotalProgressRange: CompactBlockRange!
|
||||||
|
var processedHeight: BlockHeight {
|
||||||
|
get { return underlyingProcessedHeight }
|
||||||
|
}
|
||||||
|
var underlyingProcessedHeight: BlockHeight!
|
||||||
|
var lastChainTipUpdateTime: TimeInterval {
|
||||||
|
get { return underlyingLastChainTipUpdateTime }
|
||||||
|
}
|
||||||
|
var underlyingLastChainTipUpdateTime: TimeInterval!
|
||||||
|
var lastScannedHeight: BlockHeight?
|
||||||
|
var lastEnhancedHeight: BlockHeight?
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateStateCallsCount = 0
|
||||||
|
var updateStateCalled: Bool {
|
||||||
|
return updateStateCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateStateReceivedState: CBPState?
|
||||||
|
var updateStateClosure: ((CBPState) async -> Void)?
|
||||||
|
|
||||||
|
func update(state: CBPState) async {
|
||||||
|
updateStateCallsCount += 1
|
||||||
|
updateStateReceivedState = state
|
||||||
|
await updateStateClosure!(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateSyncControlDataCallsCount = 0
|
||||||
|
var updateSyncControlDataCalled: Bool {
|
||||||
|
return updateSyncControlDataCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateSyncControlDataReceivedSyncControlData: SyncControlData?
|
||||||
|
var updateSyncControlDataClosure: ((SyncControlData) async -> Void)?
|
||||||
|
|
||||||
|
func update(syncControlData: SyncControlData) async {
|
||||||
|
updateSyncControlDataCallsCount += 1
|
||||||
|
updateSyncControlDataReceivedSyncControlData = syncControlData
|
||||||
|
await updateSyncControlDataClosure!(syncControlData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateTotalProgressRangeCallsCount = 0
|
||||||
|
var updateTotalProgressRangeCalled: Bool {
|
||||||
|
return updateTotalProgressRangeCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateTotalProgressRangeReceivedTotalProgressRange: CompactBlockRange?
|
||||||
|
var updateTotalProgressRangeClosure: ((CompactBlockRange) async -> Void)?
|
||||||
|
|
||||||
|
func update(totalProgressRange: CompactBlockRange) async {
|
||||||
|
updateTotalProgressRangeCallsCount += 1
|
||||||
|
updateTotalProgressRangeReceivedTotalProgressRange = totalProgressRange
|
||||||
|
await updateTotalProgressRangeClosure!(totalProgressRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateProcessedHeightCallsCount = 0
|
||||||
|
var updateProcessedHeightCalled: Bool {
|
||||||
|
return updateProcessedHeightCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateProcessedHeightReceivedProcessedHeight: BlockHeight?
|
||||||
|
var updateProcessedHeightClosure: ((BlockHeight) async -> Void)?
|
||||||
|
|
||||||
|
func update(processedHeight: BlockHeight) async {
|
||||||
|
updateProcessedHeightCallsCount += 1
|
||||||
|
updateProcessedHeightReceivedProcessedHeight = processedHeight
|
||||||
|
await updateProcessedHeightClosure!(processedHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateLastChainTipUpdateTimeCallsCount = 0
|
||||||
|
var updateLastChainTipUpdateTimeCalled: Bool {
|
||||||
|
return updateLastChainTipUpdateTimeCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateLastChainTipUpdateTimeReceivedLastChainTipUpdateTime: TimeInterval?
|
||||||
|
var updateLastChainTipUpdateTimeClosure: ((TimeInterval) async -> Void)?
|
||||||
|
|
||||||
|
func update(lastChainTipUpdateTime: TimeInterval) async {
|
||||||
|
updateLastChainTipUpdateTimeCallsCount += 1
|
||||||
|
updateLastChainTipUpdateTimeReceivedLastChainTipUpdateTime = lastChainTipUpdateTime
|
||||||
|
await updateLastChainTipUpdateTimeClosure!(lastChainTipUpdateTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateLastScannedHeightCallsCount = 0
|
||||||
|
var updateLastScannedHeightCalled: Bool {
|
||||||
|
return updateLastScannedHeightCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateLastScannedHeightReceivedLastScannedHeight: BlockHeight?
|
||||||
|
var updateLastScannedHeightClosure: ((BlockHeight) async -> Void)?
|
||||||
|
|
||||||
|
func update(lastScannedHeight: BlockHeight) async {
|
||||||
|
updateLastScannedHeightCallsCount += 1
|
||||||
|
updateLastScannedHeightReceivedLastScannedHeight = lastScannedHeight
|
||||||
|
await updateLastScannedHeightClosure!(lastScannedHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateLastDownloadedHeightCallsCount = 0
|
||||||
|
var updateLastDownloadedHeightCalled: Bool {
|
||||||
|
return updateLastDownloadedHeightCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateLastDownloadedHeightReceivedLastDownloadedHeight: BlockHeight?
|
||||||
|
var updateLastDownloadedHeightClosure: ((BlockHeight) async -> Void)?
|
||||||
|
|
||||||
|
func update(lastDownloadedHeight: BlockHeight) async {
|
||||||
|
updateLastDownloadedHeightCallsCount += 1
|
||||||
|
updateLastDownloadedHeightReceivedLastDownloadedHeight = lastDownloadedHeight
|
||||||
|
await updateLastDownloadedHeightClosure!(lastDownloadedHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateLastEnhancedHeightCallsCount = 0
|
||||||
|
var updateLastEnhancedHeightCalled: Bool {
|
||||||
|
return updateLastEnhancedHeightCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateLastEnhancedHeightReceivedLastEnhancedHeight: BlockHeight?
|
||||||
|
var updateLastEnhancedHeightClosure: ((BlockHeight?) async -> Void)?
|
||||||
|
|
||||||
|
func update(lastEnhancedHeight: BlockHeight?) async {
|
||||||
|
updateLastEnhancedHeightCallsCount += 1
|
||||||
|
updateLastEnhancedHeightReceivedLastEnhancedHeight = lastEnhancedHeight
|
||||||
|
await updateLastEnhancedHeightClosure!(lastEnhancedHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateSupportedSyncAlgorithmCallsCount = 0
|
||||||
|
var updateSupportedSyncAlgorithmCalled: Bool {
|
||||||
|
return updateSupportedSyncAlgorithmCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm: SyncAlgorithm?
|
||||||
|
var updateSupportedSyncAlgorithmClosure: ((SyncAlgorithm) async -> Void)?
|
||||||
|
|
||||||
|
func update(supportedSyncAlgorithm: SyncAlgorithm) async {
|
||||||
|
updateSupportedSyncAlgorithmCallsCount += 1
|
||||||
|
updateSupportedSyncAlgorithmReceivedSupportedSyncAlgorithm = supportedSyncAlgorithm
|
||||||
|
await updateSupportedSyncAlgorithmClosure!(supportedSyncAlgorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - update
|
||||||
|
|
||||||
|
var updateRequestedRewindHeightCallsCount = 0
|
||||||
|
var updateRequestedRewindHeightCalled: Bool {
|
||||||
|
return updateRequestedRewindHeightCallsCount > 0
|
||||||
|
}
|
||||||
|
var updateRequestedRewindHeightReceivedRequestedRewindHeight: BlockHeight?
|
||||||
|
var updateRequestedRewindHeightClosure: ((BlockHeight) async -> Void)?
|
||||||
|
|
||||||
|
func update(requestedRewindHeight: BlockHeight) async {
|
||||||
|
updateRequestedRewindHeightCallsCount += 1
|
||||||
|
updateRequestedRewindHeightReceivedRequestedRewindHeight = requestedRewindHeight
|
||||||
|
await updateRequestedRewindHeightClosure!(requestedRewindHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
class BlockDownloaderMock: BlockDownloader {
|
class BlockDownloaderMock: BlockDownloader {
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue