[#1046] Implement SaplingParamsAction

- SaplingParamsAction tests
- added TODO for TestCoordinator reset()
- SaplingParametersHandler mock added

[#1046] Implement SaplingParamsAction

- SaplingParamsAction tests
- added TODO for TestCoordinator reset()
- SaplingParametersHandler mock added

[#1046] Implement SaplingParamsAction

- rebased so I get functionality of improved mock checks
- enhanced SaplingParamsAction tests
- enhanced ValidateAction tests
- enhanced ScanAction tests

[#1046] Implement SaplingParamsAction

- scanAction tests more checks added
This commit is contained in:
Lukas Korba 2023-05-18 09:25:40 +02:00 committed by Michal Fousek
parent 960583b00c
commit a2df207f3e
6 changed files with 178 additions and 29 deletions

View File

@ -0,0 +1,38 @@
//
// SaplingParamsActionTests.swift
//
//
// Created by Lukáš Korba on 18.05.2023.
//
import XCTest
@testable import TestUtils
@testable import ZcashLightClientKit
final class SaplingParamsActionTests: ZcashTestCase {
func testSaplingParamsAction_NextAction() async throws {
let loggerMock = LoggerMock()
let saplingParametersHandlerMock = SaplingParametersHandlerMock()
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 == .scanDownloaded,
"nextContext after .handleSaplingParams is expected to be .scanDownloaded but received \(nextState)"
)
} catch {
XCTFail("testSaplingParamsAction_NextAction is not expected to fail. \(error)")
}
}
}

View File

@ -14,22 +14,27 @@ final class ScanActionTests: ZcashTestCase {
let blockScannerMock = BlockScannerMock()
let transactionRepositoryMock = TransactionRepositoryMock()
let loggerMock = LoggerMock()
transactionRepositoryMock.lastScannedHeightReturnValue = 1500
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
blockScannerMock.scanBlocksAtTotalProgressRangeDidScanClosure = { _, _, _ in 2 }
mockContainer.mock(type: BlockScanner.self, isSingleton: true) { _ in blockScannerMock }
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
let config: CompactBlockProcessor.Configuration = .standard(
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
)
let scanAction = ScanAction(
container: mockContainer,
config: config
)
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
let syncContext = await setupActionContext()
do {
let nextContext = try await scanAction.run(with: .init(state: .scan)) { _ in }
let nextContext = try await scanAction.run(with: syncContext) { event in
guard case .progressPartialUpdate(.syncing(let progress)) = event else {
XCTFail("event is expected to be .progressPartialUpdate(.syncing()) but received \(event)")
return
}
XCTAssertEqual(progress.startHeight, BlockHeight(1000))
XCTAssertEqual(progress.targetHeight, BlockHeight(2000))
XCTAssertEqual(progress.progressHeight, BlockHeight(1500))
}
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
XCTAssertTrue(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
let nextState = await nextContext.state
XCTAssertTrue(
nextState == .clearAlreadyScannedBlocks,
@ -39,4 +44,80 @@ final class ScanActionTests: ZcashTestCase {
XCTFail("testScanAction_NextAction is not expected to fail. \(error)")
}
}
func testScanAction_EarlyOutForNoDownloadAndScanRangeSet() async throws {
let blockScannerMock = BlockScannerMock()
let transactionRepositoryMock = TransactionRepositoryMock()
let loggerMock = LoggerMock()
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
let syncContext: ActionContext = .init(state: .scan)
do {
let _ = try await scanAction.run(with: syncContext) { _ in }
XCTAssertFalse(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
XCTAssertFalse(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
} catch {
XCTFail("testScanAction_NextAction is not expected to fail. \(error)")
}
}
func testScanAction_StartRangeHigherThanEndRange() async throws {
let blockScannerMock = BlockScannerMock()
let transactionRepositoryMock = TransactionRepositoryMock()
let loggerMock = LoggerMock()
transactionRepositoryMock.lastScannedHeightReturnValue = 2001
let scanAction = setupAction(blockScannerMock, transactionRepositoryMock, loggerMock)
let syncContext = await setupActionContext()
do {
let _ = try await scanAction.run(with: syncContext) { _ in }
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.")
XCTAssertFalse(blockScannerMock.scanBlocksAtTotalProgressRangeDidScanCalled, "blockScanner.scanBlocks(...) is expected to be called.")
} catch {
XCTFail("testScanAction_NextAction is not expected to fail. \(error)")
}
}
func setupAction(
_ blockScannerMock: BlockScannerMock,
_ transactionRepositoryMock: TransactionRepositoryMock,
_ loggerMock: LoggerMock
) -> ScanAction {
mockContainer.mock(type: BlockScanner.self, isSingleton: true) { _ in blockScannerMock }
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
let config: CompactBlockProcessor.Configuration = .standard(
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
)
return ScanAction(
container: mockContainer,
config: config
)
}
func setupActionContext() async -> ActionContext {
let syncContext: ActionContext = .init(state: .scan)
let syncRanges = SyncRanges(
latestBlockHeight: 0,
downloadedButUnscannedRange: nil,
downloadAndScanRange: CompactBlockRange(uncheckedBounds: (1000, 2000)),
enhanceRange: nil,
fetchUTXORange: nil,
latestScannedHeight: nil,
latestDownloadedBlockHeight: nil
)
await syncContext.update(syncRanges: syncRanges)
await syncContext.update(totalProgressRange: CompactBlockRange(uncheckedBounds: (1000, 2000)))
return syncContext
}
}

View File

@ -13,6 +13,8 @@ final class ValidateActionTests: ZcashTestCase {
func testValidateAction_NextAction() async throws {
let blockValidatorMock = BlockValidatorMock()
blockValidatorMock.validateClosure = { }
mockContainer.mock(type: BlockValidator.self, isSingleton: true) { _ in blockValidatorMock }
let validateAction = ValidateAction(
@ -21,6 +23,7 @@ final class ValidateActionTests: ZcashTestCase {
do {
let nextContext = try await validateAction.run(with: .init(state: .validate)) { _ in }
XCTAssertTrue(blockValidatorMock.validateCalled, "validator.validate() is expected to be called.")
let nextState = await nextContext.state
XCTAssertTrue(
nextState == .scan,

View File

@ -17,6 +17,7 @@ extension BlockValidator { }
extension LightWalletdInfo { }
extension LightWalletService { }
extension Logger { }
extension SaplingParametersHandler { }
extension Synchronizer { }
extension TransactionRepository { }
extension UTXOFetcher { }

View File

@ -426,6 +426,31 @@ class LoggerMock: Logger {
errorFileFunctionLineClosure!(message, file, function, line)
}
}
class SaplingParametersHandlerMock: SaplingParametersHandler {
init(
) {
}
// MARK: - handleIfNeeded
var handleIfNeededThrowableError: Error?
var handleIfNeededCallsCount = 0
var handleIfNeededCalled: Bool {
return handleIfNeededCallsCount > 0
}
var handleIfNeededClosure: (() async throws -> Void)?
func handleIfNeeded() async throws {
if let error = handleIfNeededThrowableError {
throw error
}
handleIfNeededCallsCount += 1
try await handleIfNeededClosure!()
}
}
class SynchronizerMock: Synchronizer {

View File

@ -212,22 +212,23 @@ extension TestCoordinator {
func reset(saplingActivation: BlockHeight, branchID: String, chainName: String) throws {
Task {
await self.synchronizer.blockProcessor.stop()
let config = await self.synchronizer.blockProcessor.config
let newConfig = CompactBlockProcessor.Configuration(
alias: config.alias,
fsBlockCacheRoot: config.fsBlockCacheRoot,
dataDb: config.dataDb,
spendParamsURL: config.spendParamsURL,
outputParamsURL: config.outputParamsURL,
saplingParamsSourceURL: config.saplingParamsSourceURL,
retries: config.retries,
maxBackoffInterval: config.maxBackoffInterval,
rewindDistance: config.rewindDistance,
walletBirthdayProvider: config.walletBirthdayProvider,
saplingActivation: saplingActivation,
network: config.network
)
// TODO: [1102] review and potentially fix/remove commented code https://github.com/zcash/ZcashLightClientKit/issues/1102
// let config = await self.synchronizer.blockProcessor.config
//
// let newConfig = CompactBlockProcessor.Configuration(
// alias: config.alias,
// fsBlockCacheRoot: config.fsBlockCacheRoot,
// dataDb: config.dataDb,
// spendParamsURL: config.spendParamsURL,
// outputParamsURL: config.outputParamsURL,
// saplingParamsSourceURL: config.saplingParamsSourceURL,
// retries: config.retries,
// maxBackoffInterval: config.maxBackoffInterval,
// rewindDistance: config.rewindDistance,
// walletBirthdayProvider: config.walletBirthdayProvider,
// saplingActivation: saplingActivation,
// network: config.network
// )
// await self.synchronizer.blockProcessor.update(config: newConfig)
}