[#1043] Implement DownloadAction

- DownloadAction tests
- BlockDownloader mock

[#1043] Implement DownloadAction (#1110)

- support functions set to private
This commit is contained in:
Lukas Korba 2023-05-21 08:58:29 +02:00
parent 2541fe5a8a
commit cff734201b
3 changed files with 268 additions and 0 deletions

View File

@ -0,0 +1,178 @@
//
// DownloadActionTests.swift
//
//
// Created by Lukáš Korba on 21.05.2023.
//
import XCTest
@testable import TestUtils
@testable import ZcashLightClientKit
final class DownloadActionTests: ZcashTestCase {
var underlyingDownloadAndScanRange: CompactBlockRange?
func testDownloadAction_NextAction() async throws {
let blockDownloaderMock = BlockDownloaderMock()
let transactionRepositoryMock = TransactionRepositoryMock()
transactionRepositoryMock.lastScannedHeightReturnValue = 1
blockDownloaderMock.setSyncRangeBatchSizeClosure = { _, _ in }
blockDownloaderMock.setDownloadLimitClosure = { _ in }
blockDownloaderMock.startDownloadMaxBlockBufferSizeClosure = { _ in }
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInClosure = { _ in }
let downloadAction = setupAction(
blockDownloaderMock,
transactionRepositoryMock
)
underlyingDownloadAndScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
let syncContext = await setupActionContext()
do {
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
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."
)
let nextState = await nextContext.state
XCTAssertTrue(
nextState == .validate,
"nextContext after .download is expected to be .validate but received \(nextState)"
)
} catch {
XCTFail("testDownloadAction_NextAction is not expected to fail. \(error)")
}
}
func testDownloadAction_NoDownloadAndScanRange() async throws {
let blockDownloaderMock = BlockDownloaderMock()
let transactionRepositoryMock = TransactionRepositoryMock()
let downloadAction = setupAction(
blockDownloaderMock,
transactionRepositoryMock
)
let syncContext = await setupActionContext()
do {
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
XCTAssertFalse(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is not expected to be called.")
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 == .validate,
"nextContext after .download is expected to be .validate but received \(nextState)"
)
} 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
)
underlyingDownloadAndScanRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
let syncContext = await setupActionContext()
do {
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
XCTAssertTrue(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is expected to be called.")
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 == .validate,
"nextContext after .download is expected to be .validate but received \(nextState)"
)
} catch {
XCTFail("testDownloadAction_NoDownloadAndScanRange is not expected to fail. \(error)")
}
}
func testDownloadAction_DownloadStops() async throws {
let blockDownloaderMock = BlockDownloaderMock()
blockDownloaderMock.stopDownloadClosure = { }
let downloadAction = setupAction(
blockDownloaderMock
)
await downloadAction.stop()
XCTAssertTrue(blockDownloaderMock.stopDownloadCalled, "downloader.stopDownload() is expected to be called.")
}
private func setupActionContext() async -> ActionContext {
let syncContext: ActionContext = .init(state: .download)
let syncRanges = SyncRanges(
latestBlockHeight: 0,
downloadedButUnscannedRange: nil,
downloadAndScanRange: underlyingDownloadAndScanRange,
enhanceRange: nil,
fetchUTXORange: nil,
latestScannedHeight: nil,
latestDownloadedBlockHeight: nil
)
await syncContext.update(syncRanges: syncRanges)
return syncContext
}
private func setupAction(
_ blockDownloaderMock: BlockDownloaderMock = BlockDownloaderMock(),
_ transactionRepositoryMock: TransactionRepositoryMock = TransactionRepositoryMock(),
_ loggerMock: LoggerMock = LoggerMock()
) -> DownloadAction {
mockContainer.mock(type: BlockDownloader.self, isSingleton: true) { _ in blockDownloaderMock }
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
let config: CompactBlockProcessor.Configuration = .standard(
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
)
return DownloadAction(
container: mockContainer,
config: config
)
}
}

View File

@ -12,6 +12,7 @@
@testable import ZcashLightClientKit
extension BlockDownloader { }
extension BlockEnhancer { }
extension BlockScanner { }
extension BlockValidator { }

View File

@ -7,6 +7,95 @@ import Foundation
// MARK: - AutoMockable protocols
class BlockDownloaderMock: BlockDownloader {
init(
) {
}
// MARK: - setDownloadLimit
var setDownloadLimitCallsCount = 0
var setDownloadLimitCalled: Bool {
return setDownloadLimitCallsCount > 0
}
var setDownloadLimitReceivedLimit: BlockHeight?
var setDownloadLimitClosure: ((BlockHeight) async -> Void)?
func setDownloadLimit(_ limit: BlockHeight) async {
setDownloadLimitCallsCount += 1
setDownloadLimitReceivedLimit = limit
await setDownloadLimitClosure!(limit)
}
// MARK: - setSyncRange
var setSyncRangeBatchSizeThrowableError: Error?
var setSyncRangeBatchSizeCallsCount = 0
var setSyncRangeBatchSizeCalled: Bool {
return setSyncRangeBatchSizeCallsCount > 0
}
var setSyncRangeBatchSizeReceivedArguments: (range: CompactBlockRange, batchSize: Int)?
var setSyncRangeBatchSizeClosure: ((CompactBlockRange, Int) async throws -> Void)?
func setSyncRange(_ range: CompactBlockRange, batchSize: Int) async throws {
if let error = setSyncRangeBatchSizeThrowableError {
throw error
}
setSyncRangeBatchSizeCallsCount += 1
setSyncRangeBatchSizeReceivedArguments = (range: range, batchSize: batchSize)
try await setSyncRangeBatchSizeClosure!(range, batchSize)
}
// MARK: - startDownload
var startDownloadMaxBlockBufferSizeCallsCount = 0
var startDownloadMaxBlockBufferSizeCalled: Bool {
return startDownloadMaxBlockBufferSizeCallsCount > 0
}
var startDownloadMaxBlockBufferSizeReceivedMaxBlockBufferSize: Int?
var startDownloadMaxBlockBufferSizeClosure: ((Int) async -> Void)?
func startDownload(maxBlockBufferSize: Int) async {
startDownloadMaxBlockBufferSizeCallsCount += 1
startDownloadMaxBlockBufferSizeReceivedMaxBlockBufferSize = maxBlockBufferSize
await startDownloadMaxBlockBufferSizeClosure!(maxBlockBufferSize)
}
// MARK: - stopDownload
var stopDownloadCallsCount = 0
var stopDownloadCalled: Bool {
return stopDownloadCallsCount > 0
}
var stopDownloadClosure: (() async -> Void)?
func stopDownload() async {
stopDownloadCallsCount += 1
await stopDownloadClosure!()
}
// MARK: - waitUntilRequestedBlocksAreDownloaded
var waitUntilRequestedBlocksAreDownloadedInThrowableError: Error?
var waitUntilRequestedBlocksAreDownloadedInCallsCount = 0
var waitUntilRequestedBlocksAreDownloadedInCalled: Bool {
return waitUntilRequestedBlocksAreDownloadedInCallsCount > 0
}
var waitUntilRequestedBlocksAreDownloadedInReceivedRange: CompactBlockRange?
var waitUntilRequestedBlocksAreDownloadedInClosure: ((CompactBlockRange) async throws -> Void)?
func waitUntilRequestedBlocksAreDownloaded(in range: CompactBlockRange) async throws {
if let error = waitUntilRequestedBlocksAreDownloadedInThrowableError {
throw error
}
waitUntilRequestedBlocksAreDownloadedInCallsCount += 1
waitUntilRequestedBlocksAreDownloadedInReceivedRange = range
try await waitUntilRequestedBlocksAreDownloadedInClosure!(range)
}
}
class BlockEnhancerMock: BlockEnhancer {