[#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
80eb3b1c8d
commit
95536638e5
|
@ -158,7 +158,7 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "1411d9a839a62523997dae113150b2beccd6b3fc"
|
||||
"revision" : "6a53c9e32520b46f8c70597e27b335105fabfc21"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state" : {
|
||||
"revision" : "1411d9a839a62523997dae113150b2beccd6b3fc"
|
||||
"revision" : "6a53c9e32520b46f8c70597e27b335105fabfc21"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -7,7 +7,32 @@
|
|||
|
||||
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 prevState: CBPState?
|
||||
var syncControlData: SyncControlData
|
||||
|
|
|
@ -20,6 +20,7 @@ extension ClearCacheAction: Action {
|
|||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
try await storage.clear()
|
||||
|
||||
if await context.prevState == .idle {
|
||||
await context.update(state: .migrateLegacyCacheDB)
|
||||
} else {
|
||||
|
@ -37,6 +38,7 @@ extension ClearCacheAction: Action {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ extension DownloadAction: Action {
|
|||
let downloadLimit = await context.syncControlData.latestBlockHeight >= potentialDownloadLimit ? potentialDownloadLimit : batchRangeEnd
|
||||
|
||||
logger.debug("Starting download with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
||||
|
||||
await downloader.update(latestDownloadedBlockHeight: batchRange.lowerBound, force: true) // SbS
|
||||
try await downloader.setSyncRange(lastScannedHeight...latestBlockHeight, batchSize: config.batchSize)
|
||||
await downloader.setDownloadLimit(downloadLimit)
|
||||
|
|
|
@ -11,13 +11,11 @@ final class EnhanceAction {
|
|||
let blockEnhancer: BlockEnhancer
|
||||
let configProvider: CompactBlockProcessor.ConfigProvider
|
||||
let logger: Logger
|
||||
let transactionRepository: TransactionRepository
|
||||
|
||||
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
|
||||
blockEnhancer = container.resolve(BlockEnhancer.self)
|
||||
self.configProvider = configProvider
|
||||
logger = container.resolve(Logger.self)
|
||||
transactionRepository = container.resolve(TransactionRepository.self)
|
||||
}
|
||||
|
||||
func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
|
||||
|
|
|
@ -193,7 +193,7 @@ actor CompactBlockProcessor {
|
|||
)
|
||||
|
||||
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)
|
||||
|
||||
self.metrics = container.resolve(SDKMetrics.self)
|
||||
|
@ -638,7 +638,7 @@ extension CompactBlockProcessor {
|
|||
|
||||
private func resetContext() async {
|
||||
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 compactBlockProgress.reset()
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
latestScannedHeight: startHeight,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
let context = ActionContext(state: .download)
|
||||
let context = ActionContextMock()
|
||||
await context.update(syncControlData: syncControlData)
|
||||
|
||||
let expectation = XCTestExpectation()
|
||||
|
@ -175,7 +175,7 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
latestScannedHeight: startHeight,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
let context = ActionContext(state: .download)
|
||||
let context = ActionContextMock()
|
||||
await context.update(syncControlData: syncControlData)
|
||||
|
||||
let date = Date()
|
||||
|
|
|
@ -11,7 +11,7 @@ import XCTest
|
|||
|
||||
final class ActionContextStateTests: XCTestCase {
|
||||
func testPreviousState() async throws {
|
||||
let syncContext: ActionContext = .init(state: .idle)
|
||||
let syncContext = ActionContextImpl(state: .idle)
|
||||
|
||||
await syncContext.update(state: .clearCache)
|
||||
|
||||
|
@ -32,4 +32,15 @@ final class ActionContextStateTests: XCTestCase {
|
|||
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 {
|
||||
func testClearAlreadyScannedBlocksAction_NextAction() async throws {
|
||||
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()
|
||||
|
||||
compactBlockRepositoryMock.clearUpToClosure = { _ in }
|
||||
|
@ -19,24 +61,9 @@ final class ClearAlreadyScannedBlocksActionTests: ZcashTestCase {
|
|||
|
||||
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
||||
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
|
||||
|
||||
let clearAlreadyScannedBlocksAction = ClearAlreadyScannedBlocksAction(
|
||||
|
||||
return ClearAlreadyScannedBlocksAction(
|
||||
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
|
||||
|
||||
final class ClearCacheActionTests: ZcashTestCase {
|
||||
func testClearCacheAction_NextAction() async throws {
|
||||
func testClearCacheAction_MigrationLegacyCacheDB() async throws {
|
||||
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 = { }
|
||||
|
||||
mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
|
||||
|
||||
let clearCacheAction = ClearCacheAction(
|
||||
return ClearCacheAction(
|
||||
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,
|
||||
loggerMock
|
||||
)
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 123
|
||||
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 123
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
||||
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateBlockDataCalled,
|
||||
"latestBlocksDataProvider.updateBlockData() is expected to be called."
|
||||
)
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .finished,
|
||||
"nextContext after .computeSyncControlData is expected to be .finished but received \(nextState)"
|
||||
)
|
||||
checkLatestBlocksDataProvider(latestBlocksDataProviderMock)
|
||||
checkActionContext(nextContext, expectedNextState: .finished)
|
||||
|
||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug() is expected to be called.")
|
||||
} catch {
|
||||
XCTFail("testComputeSyncControlDataAction_finishProcessingCase is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testComputeSyncControlDataAction_fetchUTXOsCase() async throws {
|
||||
func testComputeSyncControlDataAction_DownloadCase() async throws {
|
||||
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
|
||||
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
|
||||
let loggerMock = LoggerMock()
|
||||
|
@ -65,45 +57,51 @@ final class ComputeSyncControlDataActionTests: ZcashTestCase {
|
|||
latestBlocksDataProviderMock,
|
||||
loggerMock
|
||||
)
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 10
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 1234
|
||||
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 123
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
let nextContext = try await computeSyncControlDataAction.run(with: syncContext) { _ in }
|
||||
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"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.")
|
||||
checkLatestBlocksDataProvider(latestBlocksDataProviderMock)
|
||||
checkActionContext(nextContext, expectedNextState: .download)
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .download,
|
||||
"nextContext after .computeSyncControlData is expected to be .download but received \(nextState)"
|
||||
)
|
||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug() is expected to be called.")
|
||||
} catch {
|
||||
XCTFail("testComputeSyncControlDataAction_checksBeforeSyncCase is not expected to fail. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func setupSyncControlData() -> SyncControlData {
|
||||
SyncControlData(
|
||||
latestBlockHeight: 0,
|
||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
private func setupActionContext() async -> ActionContextMock {
|
||||
let syncContext = ActionContextMock()
|
||||
|
||||
syncContext.updateLastScannedHeightClosure = { _ in }
|
||||
syncContext.updateLastDownloadedHeightClosure = { _ in }
|
||||
syncContext.updateSyncControlDataClosure = { _ in }
|
||||
syncContext.updateTotalProgressRangeClosure = { _ in }
|
||||
syncContext.updateStateClosure = { _ in }
|
||||
syncContext.underlyingState = .idle
|
||||
|
||||
return syncContext
|
||||
}
|
||||
|
||||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .computeSyncControlData)
|
||||
private func setupDefaultMocksAndReturnAction(
|
||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> ComputeSyncControlDataAction {
|
||||
latestBlocksDataProviderMock.updateScannedDataClosure = { }
|
||||
latestBlocksDataProviderMock.updateBlockDataClosure = { }
|
||||
latestBlocksDataProviderMock.updateUnenhancedDataClosure = { }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
await syncContext.update(syncControlData: setupSyncControlData())
|
||||
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
|
||||
|
||||
return syncContext
|
||||
return setupAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
loggerMock
|
||||
)
|
||||
}
|
||||
|
||||
private func setupAction(
|
||||
|
@ -125,23 +123,44 @@ final class ComputeSyncControlDataActionTests: ZcashTestCase {
|
|||
)
|
||||
}
|
||||
|
||||
private func setupDefaultMocksAndReturnAction(
|
||||
_ blockDownloaderServiceMock: BlockDownloaderServiceMock = BlockDownloaderServiceMock(),
|
||||
_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock = LatestBlocksDataProviderMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
) -> ComputeSyncControlDataAction {
|
||||
blockDownloaderServiceMock.lastDownloadedBlockHeightReturnValue = 1
|
||||
latestBlocksDataProviderMock.underlyingLatestBlockHeight = 1
|
||||
latestBlocksDataProviderMock.underlyingLatestScannedHeight = 1
|
||||
latestBlocksDataProviderMock.updateScannedDataClosure = { }
|
||||
latestBlocksDataProviderMock.updateBlockDataClosure = { }
|
||||
latestBlocksDataProviderMock.updateUnenhancedDataClosure = { }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
private func checkLatestBlocksDataProvider(_ latestBlocksDataProviderMock: LatestBlocksDataProviderMock) {
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateScannedDataCalled,
|
||||
"latestBlocksDataProvider.updateScannedData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateBlockDataCalled,
|
||||
"latestBlocksDataProvider.updateBlockData() is expected to be called."
|
||||
)
|
||||
XCTAssertTrue(
|
||||
latestBlocksDataProviderMock.updateUnenhancedDataCalled,
|
||||
"latestBlocksDataProvider.updateUnenhancedData() is expected to be called."
|
||||
)
|
||||
}
|
||||
|
||||
private func checkActionContext(_ actionContext: ActionContext, expectedNextState: CBPState) {
|
||||
guard let nextContextMock = actionContext as? ActionContextMock else {
|
||||
return XCTFail("Result of run(with:) is expected to be an ActionContextMock")
|
||||
}
|
||||
|
||||
return setupAction(
|
||||
blockDownloaderServiceMock,
|
||||
latestBlocksDataProviderMock,
|
||||
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 underlyingScanRange: CompactBlockRange?
|
||||
|
||||
func testDownloadAction_NextAction() async throws {
|
||||
func testDownloadAction_FullPass() async throws {
|
||||
let blockDownloaderMock = BlockDownloaderMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
|
||||
|
@ -32,25 +32,69 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
await syncContext.update(lastScannedHeight: 1000)
|
||||
let syncContext = ActionContextMock.default()
|
||||
syncContext.lastScannedHeight = 1000
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
|
||||
do {
|
||||
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(
|
||||
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInCalled,
|
||||
"downloader.waitUntilRequestedBlocksAreDownloaded() is expected to be called."
|
||||
blockDownloaderMock.setSyncRangeBatchSizeCallsCount == 1,
|
||||
"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(
|
||||
nextState == .scan,
|
||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
||||
blockDownloaderMock.startDownloadMaxBlockBufferSizeCallsCount == 0,
|
||||
"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 {
|
||||
XCTFail("testDownloadAction_NextAction is not expected to fail. \(error)")
|
||||
}
|
||||
|
@ -65,7 +109,13 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
transactionRepositoryMock
|
||||
)
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
let syncContext = ActionContextMock.default()
|
||||
syncContext.lastScannedHeight = 1000
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 999,
|
||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
|
||||
do {
|
||||
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."
|
||||
)
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .scan,
|
||||
"nextContext after .download is expected to be .scan but received \(nextState)"
|
||||
)
|
||||
let acResult = nextContext.checkStateIs(.scan)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
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 {
|
||||
let blockDownloaderMock = BlockDownloaderMock()
|
||||
|
||||
|
@ -142,21 +152,7 @@ final class DownloadActionTests: ZcashTestCase {
|
|||
|
||||
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(
|
||||
_ blockDownloaderMock: BlockDownloaderMock = BlockDownloaderMock(),
|
||||
_ transactionRepositoryMock: TransactionRepositoryMock = TransactionRepositoryMock(),
|
||||
|
|
|
@ -25,14 +25,12 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
func testEnhanceAction_decideWhatToDoNext_NoDownloadAndScanRange() async throws {
|
||||
let enhanceAction = setupAction()
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1)
|
||||
let nextState = await nextContext.state
|
||||
let syncContext = setupActionContext()
|
||||
|
||||
XCTAssertTrue(
|
||||
nextState == .clearCache,
|
||||
"testEnhanceAction_decideWhatToDoNext_NoDownloadAndScanRange is expected to be .clearCache but received \(nextState)"
|
||||
)
|
||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1)
|
||||
|
||||
let acResult = nextContext.checkStateIs(.clearCache)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
}
|
||||
|
||||
func testEnhanceAction_decideWhatToDoNext_NothingToDownloadAndScanLeft() async throws {
|
||||
|
@ -40,14 +38,12 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 2000)
|
||||
let nextState = await nextContext.state
|
||||
let syncContext = setupActionContext()
|
||||
|
||||
XCTAssertTrue(
|
||||
nextState == .clearCache,
|
||||
"testEnhanceAction_decideWhatToDoNext_NothingToDownloadAndScanLeft is expected to be .clearCache but received \(nextState)"
|
||||
)
|
||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 2000)
|
||||
|
||||
let acResult = nextContext.checkStateIs(.clearCache)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
}
|
||||
|
||||
func testEnhanceAction_decideWhatToDoNext_UpdateChainTipExpected() async throws {
|
||||
|
@ -55,28 +51,62 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
underlyingScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1500)
|
||||
let nextState = await nextContext.state
|
||||
let syncContext = setupActionContext()
|
||||
|
||||
XCTAssertTrue(
|
||||
nextState == .updateChainTip,
|
||||
"testEnhanceAction_decideWhatToDoNext_DownloadExpected is expected to be .updateChainTip but received \(nextState)"
|
||||
)
|
||||
let nextContext = await enhanceAction.decideWhatToDoNext(context: syncContext, lastScannedHeight: 1500)
|
||||
|
||||
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 {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
||||
let enhanceAction = setupAction(blockEnhancerMock)
|
||||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock
|
||||
let syncContext = setupActionContext()
|
||||
syncContext.lastScannedHeight = 1
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: underlyingScanRange?.lowerBound,
|
||||
firstUnenhancedHeight: 2000
|
||||
)
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
||||
|
@ -88,18 +118,17 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
|
||||
func testEnhanceAction_1000BlocksConditionNotFulfilled() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1
|
||||
let enhanceAction = setupAction(blockEnhancerMock)
|
||||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock
|
||||
let syncContext = setupActionContext()
|
||||
syncContext.lastScannedHeight = 1000
|
||||
syncContext.lastEnhancedHeight = 1000
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: 1000,
|
||||
firstUnenhancedHeight: 1000
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
do {
|
||||
_ = try await enhanceAction.run(with: syncContext) { _ in }
|
||||
|
@ -111,10 +140,7 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
|
||||
func testEnhanceAction_EnhancementOfBlocksCalled_FoundTransactions() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1500
|
||||
|
||||
|
||||
let transaction = ZcashTransaction.Overview(
|
||||
accountId: 0,
|
||||
blockTime: 1.0,
|
||||
|
@ -138,14 +164,17 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
return [transaction]
|
||||
}
|
||||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock
|
||||
let enhanceAction = setupAction(blockEnhancerMock)
|
||||
|
||||
let syncContext = setupActionContext()
|
||||
syncContext.lastScannedHeight = 2000
|
||||
syncContext.lastEnhancedHeight = 1500
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: 1,
|
||||
firstUnenhancedHeight: 1000
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
syncContext.updateLastEnhancedHeightClosure = { _ in }
|
||||
|
||||
do {
|
||||
_ = try await enhanceAction.run(with: syncContext) { event in
|
||||
|
@ -168,9 +197,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
|
||||
func testEnhanceAction_EnhancementOfBlocksCalled_minedTransaction() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 1500
|
||||
|
||||
let transaction = ZcashTransaction.Overview(
|
||||
accountId: 0,
|
||||
|
@ -203,14 +229,17 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
return nil
|
||||
}
|
||||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock
|
||||
let enhanceAction = setupAction(blockEnhancerMock)
|
||||
|
||||
let syncContext = setupActionContext()
|
||||
syncContext.lastScannedHeight = 2000
|
||||
syncContext.lastEnhancedHeight = 1500
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: 1,
|
||||
firstUnenhancedHeight: 1000
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
syncContext.updateLastEnhancedHeightClosure = { _ in }
|
||||
|
||||
do {
|
||||
_ = try await enhanceAction.run(with: syncContext) { event in
|
||||
|
@ -229,9 +258,6 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
|
||||
func testEnhanceAction_EnhancementOfBlocksCalled_usingSmallRange_minedTransaction() async throws {
|
||||
let blockEnhancerMock = BlockEnhancerMock()
|
||||
let transactionRepositoryMock = TransactionRepositoryMock()
|
||||
|
||||
transactionRepositoryMock.lastScannedHeightReturnValue = 2000
|
||||
|
||||
let transaction = ZcashTransaction.Overview(
|
||||
accountId: 0,
|
||||
|
@ -264,15 +290,18 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
return nil
|
||||
}
|
||||
|
||||
let enhanceAction = setupAction(
|
||||
blockEnhancerMock,
|
||||
transactionRepositoryMock
|
||||
let enhanceAction = setupAction(blockEnhancerMock)
|
||||
|
||||
let syncContext = setupActionContext()
|
||||
syncContext.lastScannedHeight = 2000
|
||||
syncContext.lastEnhancedHeight = 1500
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: 1,
|
||||
firstUnenhancedHeight: 1000
|
||||
)
|
||||
|
||||
underlyingEnhanceRange = CompactBlockRange(uncheckedBounds: (1900, 2000))
|
||||
|
||||
let syncContext = await setupActionContext()
|
||||
|
||||
syncContext.updateLastEnhancedHeightClosure = { _ in }
|
||||
|
||||
do {
|
||||
_ = try await enhanceAction.run(with: syncContext) { event in
|
||||
if case .progressPartialUpdate = event { return }
|
||||
|
@ -288,18 +317,14 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private func setupActionContext() async -> ActionContext {
|
||||
let syncContext: ActionContext = .init(state: .enhance)
|
||||
|
||||
let syncControlData = SyncControlData(
|
||||
private func setupActionContext() -> ActionContextMock {
|
||||
let syncContext = ActionContextMock.default()
|
||||
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 2000,
|
||||
latestScannedHeight: underlyingScanRange?.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
|
||||
}
|
||||
|
|
|
@ -24,16 +24,13 @@ final class FetchUTXOsActionTests: ZcashTestCase {
|
|||
|
||||
let fetchUTXOsAction = FetchUTXOsAction(container: mockContainer)
|
||||
|
||||
let syncContext: ActionContext = .init(state: .fetchUTXO)
|
||||
|
||||
let syncControlData = SyncControlData(
|
||||
let syncContext = ActionContextMock.default()
|
||||
syncContext.underlyingSyncControlData = SyncControlData(
|
||||
latestBlockHeight: 0,
|
||||
latestScannedHeight: 0,
|
||||
firstUnenhancedHeight: nil
|
||||
)
|
||||
|
||||
await syncContext.update(syncControlData: syncControlData)
|
||||
|
||||
do {
|
||||
let nextContext = try await fetchUTXOsAction.run(with: syncContext) { event in
|
||||
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(uTXOFetcherMock.fetchDidFetchCalled, "utxoFetcher.fetch() is expected to be called.")
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .handleSaplingParams,
|
||||
"nextContext after .fetchUTXO is expected to be .handleSaplingParams but received \(nextState)"
|
||||
)
|
||||
|
||||
let acResult = nextContext.checkStateIs(.handleSaplingParams)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
XCTFail("testFetchUTXOsAction_NextAction is not expected to fail. \(error)")
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
|
||||
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(
|
||||
|
@ -44,11 +45,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .validateServer,
|
||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
||||
)
|
||||
let acResult = nextContext.checkStateIs(.validateServer)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
XCTFail("testMigrateLegacyCacheDBAction_noCacheDbURL is not expected to fail. \(error)")
|
||||
}
|
||||
|
@ -68,7 +66,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
|
||||
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.")
|
||||
} catch ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL {
|
||||
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
||||
|
@ -104,7 +103,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
|
||||
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(
|
||||
|
@ -114,11 +114,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
XCTAssertFalse(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is not expected to be called.")
|
||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .validateServer,
|
||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
||||
)
|
||||
let acResult = nextContext.checkStateIs(.validateServer)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
XCTFail("testMigrateLegacyCacheDBAction_aliasDoesntMatchDefault is not expected to fail. \(error)")
|
||||
}
|
||||
|
@ -142,8 +139,9 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
|
||||
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(
|
||||
transactionRepositoryMock.lastScannedHeightCalled,
|
||||
|
@ -152,11 +150,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
XCTAssertTrue(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
||||
XCTAssertFalse(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is not expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .validateServer,
|
||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
||||
)
|
||||
let acResult = nextContext.checkStateIs(.validateServer)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
XCTFail("testMigrateLegacyCacheDBAction_isNotReadableFile is not expected to fail. \(error)")
|
||||
}
|
||||
|
@ -181,7 +176,8 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
|
||||
do {
|
||||
_ = try await migrateLegacyCacheDBAction.run(with: .init(state: .migrateLegacyCacheDB)) { _ in }
|
||||
let context = ActionContextMock.default()
|
||||
_ = try await migrateLegacyCacheDBAction.run(with: context) { _ in }
|
||||
} catch ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb {
|
||||
XCTAssertFalse(compactBlockRepositoryMock.createCalled, "storage.create() is not expected to be called.")
|
||||
XCTAssertFalse(
|
||||
|
@ -218,17 +214,15 @@ final class MigrateLegacyCacheDBActionTests: ZcashTestCase {
|
|||
)
|
||||
|
||||
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(zcashFileManagerMock.isReadableFileAtPathCalled, "fileManager.isReadableFile() is expected to be called.")
|
||||
XCTAssertTrue(zcashFileManagerMock.removeItemAtCalled, "fileManager.removeItem() is expected to be called.")
|
||||
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .validateServer,
|
||||
"nextContext after .migrateLegacyCacheDB is expected to be .validateServer but received \(nextState)"
|
||||
)
|
||||
let acResult = nextContext.checkStateIs(.validateServer)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
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
|
||||
|
||||
final class SaplingParamsActionTests: ZcashTestCase {
|
||||
func testSaplingParamsAction_NextAction() async throws {
|
||||
func testSaplingParamsAction_NextAction_linearSync() async throws {
|
||||
let loggerMock = LoggerMock()
|
||||
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 }
|
||||
saplingParametersHandlerMock.handleIfNeededClosure = { }
|
||||
|
||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||
mockContainer.mock(type: SaplingParametersHandler.self, isSingleton: true) { _ in saplingParametersHandlerMock }
|
||||
|
||||
let saplingParamsActionAction = 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)")
|
||||
}
|
||||
return SaplingParamsAction(
|
||||
container: mockContainer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,16 @@ final class ScanActionTests: ZcashTestCase {
|
|||
blockScannerMock.scanBlocksAtTotalProgressRangeDidScanClosure = { _, _, _ in 2 }
|
||||
|
||||
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 {
|
||||
let nextContext = try await scanAction.run(with: syncContext) { event in
|
||||
guard case .progressPartialUpdate(.syncing(let progress)) = event else {
|
||||
|
@ -36,11 +42,9 @@ final class ScanActionTests: ZcashTestCase {
|
|||
}
|
||||
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
|
||||
XCTAssertTrue(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .clearAlreadyScannedBlocks,
|
||||
"nextContext after .scan is expected to be .clearAlreadyScannedBlocks but received \(nextState)"
|
||||
)
|
||||
|
||||
let acResult = nextContext.checkStateIs(.clearAlreadyScannedBlocks)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
XCTFail("testScanAction_NextAction is not expected to fail. \(error)")
|
||||
}
|
||||
|
@ -52,7 +56,7 @@ final class ScanActionTests: ZcashTestCase {
|
|||
let loggerMock = LoggerMock()
|
||||
|
||||
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
||||
let syncContext: ActionContext = .init(state: .scan)
|
||||
let syncContext = ActionContextMock.default()
|
||||
|
||||
do {
|
||||
_ = try await scanAction.run(with: syncContext) { _ in }
|
||||
|
@ -75,7 +79,7 @@ final class ScanActionTests: ZcashTestCase {
|
|||
transactionRepositoryMock.lastScannedHeightReturnValue = 2001
|
||||
|
||||
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
|
||||
let syncContext = await setupActionContext()
|
||||
let syncContext = ActionContextMock.default()
|
||||
|
||||
do {
|
||||
_ = try await scanAction.run(with: syncContext) { _ in }
|
||||
|
@ -104,19 +108,4 @@ final class ScanActionTests: ZcashTestCase {
|
|||
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()
|
||||
|
||||
do {
|
||||
let nextContext = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
||||
let nextState = await nextContext.state
|
||||
XCTAssertTrue(
|
||||
nextState == .fetchUTXO,
|
||||
"nextContext after .validateServer is expected to be .fetchUTXO but received \(nextState)"
|
||||
)
|
||||
let context = ActionContextMock.default()
|
||||
let nextContext = try await validateServerAction.run(with: context) { _ in }
|
||||
|
||||
let acResult = nextContext.checkStateIs(.fetchUTXO)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
XCTFail("testValidateServerAction_NextAction is not expected to fail. \(error)")
|
||||
}
|
||||
|
@ -45,7 +44,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
|||
let validateServerAction = setupAction()
|
||||
|
||||
do {
|
||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
||||
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||
XCTFail("testValidateServerAction_ChainNameError is expected to fail.")
|
||||
} catch ZcashError.compactBlockProcessorChainName(let chainName) {
|
||||
XCTAssertEqual(chainName, "invalid")
|
||||
|
@ -63,7 +62,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
|||
let validateServerAction = setupAction()
|
||||
|
||||
do {
|
||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
||||
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||
XCTFail("testValidateServerAction_NetworkMatchError is expected to fail.")
|
||||
} catch let ZcashError.compactBlockProcessorNetworkMismatch(expected, found) {
|
||||
XCTAssertEqual(expected, .mainnet)
|
||||
|
@ -82,7 +81,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
|||
let validateServerAction = setupAction()
|
||||
|
||||
do {
|
||||
_ = try await validateServerAction.run(with: .init(state: .validateServer)) { _ in }
|
||||
_ = try await validateServerAction.run(with: ActionContextMock()) { _ in }
|
||||
XCTFail("testValidateServerAction_SaplingActivationError is expected to fail.")
|
||||
} catch let ZcashError.compactBlockProcessorSaplingActivationMismatch(expected, found) {
|
||||
XCTAssertEqual(expected, 280_000)
|
||||
|
@ -101,7 +100,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
|||
let validateServerAction = setupAction()
|
||||
|
||||
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.")
|
||||
} catch ZcashError.compactBlockProcessorConsensusBranchID {
|
||||
} catch {
|
||||
|
@ -118,7 +117,7 @@ final class ValidateServerActionTests: ZcashTestCase {
|
|||
let validateServerAction = setupAction()
|
||||
|
||||
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.")
|
||||
} catch let ZcashError.compactBlockProcessorWrongConsensusBranchId(expected, found) {
|
||||
XCTAssertEqual(expected, -1026109260)
|
||||
|
|
|
@ -469,6 +469,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 {
|
||||
SynchronizerState(
|
||||
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
|
||||
|
||||
extension ActionContext { }
|
||||
extension BlockDownloader { }
|
||||
extension BlockDownloaderService { }
|
||||
extension BlockEnhancer { }
|
||||
|
|
|
@ -7,6 +7,193 @@ import Foundation
|
|||
|
||||
|
||||
// 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 {
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue