diff --git a/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift b/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift index f9f2466c..6758af8d 100644 --- a/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift +++ b/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift @@ -19,7 +19,7 @@ struct UTXOFetcherConfig { protocol UTXOFetcher { func fetch( at range: CompactBlockRange, - didFetch: (Float) async -> Void + didFetch: @escaping (Float) async -> Void ) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) } @@ -36,7 +36,7 @@ struct UTXOFetcherImpl { extension UTXOFetcherImpl: UTXOFetcher { func fetch( at range: CompactBlockRange, - didFetch: (Float) async -> Void + didFetch: @escaping (Float) async -> Void ) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) { try Task.checkCancellation() diff --git a/Tests/OfflineTests/CompactBlockProcessorActions/FetchUTXOsActionTests.swift b/Tests/OfflineTests/CompactBlockProcessorActions/FetchUTXOsActionTests.swift new file mode 100644 index 00000000..35f1a05b --- /dev/null +++ b/Tests/OfflineTests/CompactBlockProcessorActions/FetchUTXOsActionTests.swift @@ -0,0 +1,61 @@ +// +// FetchUTXOsActionTests.swift +// +// +// Created by Lukáš Korba on 18.05.2023. +// + +import XCTest +@testable import TestUtils +@testable import ZcashLightClientKit + +final class FetchUTXOsActionTests: ZcashTestCase { + func testFetchUTXOsAction_NextAction() async throws { + let loggerMock = LoggerMock() + let uTXOFetcherMock = UTXOFetcherMock() + + loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in } + let insertedEntity = UnspentTransactionOutputEntityMock(address: "addr", txid: Data(), index: 0, script: Data(), valueZat: 1, height: 2) + let skippedEntity = UnspentTransactionOutputEntityMock(address: "addr2", txid: Data(), index: 1, script: Data(), valueZat: 2, height: 3) + uTXOFetcherMock.fetchAtDidFetchReturnValue = (inserted: [insertedEntity], skipped: [skippedEntity]) + + mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock } + mockContainer.mock(type: UTXOFetcher.self, isSingleton: true) { _ in uTXOFetcherMock } + + let fetchUTXOsAction = FetchUTXOsAction(container: mockContainer) + + let syncContext: ActionContext = .init(state: .fetchUTXO) + + let syncRanges = SyncRanges( + latestBlockHeight: 0, + downloadedButUnscannedRange: nil, + downloadAndScanRange: nil, + enhanceRange: nil, + fetchUTXORange: CompactBlockRange(uncheckedBounds: (1000, 2000)), + latestScannedHeight: nil, + latestDownloadedBlockHeight: nil + ) + + await syncContext.update(syncRanges: syncRanges) + + do { + let nextContext = try await fetchUTXOsAction.run(with: syncContext) { event in + guard case .storedUTXOs(let result) = event else { + XCTFail("testFetchUTXOsAction_NextAction event expected to be .storedUTXOs but received \(event)") + return + } + XCTAssertEqual(result.inserted as! [UnspentTransactionOutputEntityMock], [insertedEntity]) + XCTAssertEqual(result.skipped as! [UnspentTransactionOutputEntityMock], [skippedEntity]) + } + XCTAssertTrue(loggerMock.debugFileFunctionLineCalled, "logger.debug(...) is expected to be called.") + XCTAssertTrue(uTXOFetcherMock.fetchAtDidFetchCalled, "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)" + ) + } catch { + XCTFail("testFetchUTXOsAction_NextAction is not expected to fail. \(error)") + } + } +} diff --git a/Tests/TestUtils/Sourcery/AutoMockable.swift b/Tests/TestUtils/Sourcery/AutoMockable.swift index ca273b31..7a0775cb 100644 --- a/Tests/TestUtils/Sourcery/AutoMockable.swift +++ b/Tests/TestUtils/Sourcery/AutoMockable.swift @@ -19,6 +19,7 @@ extension LightWalletService { } extension Logger { } extension Synchronizer { } extension TransactionRepository { } +extension UTXOFetcher { } extension ZcashRustBackendWelding { } // sourcery:end: diff --git a/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift b/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift index 4bcd316e..8a1adb2e 100644 --- a/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift +++ b/Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift @@ -1355,6 +1355,38 @@ class TransactionRepositoryMock: TransactionRepository { } } +} +class UTXOFetcherMock: UTXOFetcher { + + + init( + ) { + } + + // MARK: - fetch + + var fetchAtDidFetchThrowableError: Error? + var fetchAtDidFetchCallsCount = 0 + var fetchAtDidFetchCalled: Bool { + return fetchAtDidFetchCallsCount > 0 + } + var fetchAtDidFetchReceivedArguments: (range: CompactBlockRange, didFetch: (Float) async -> Void)? + var fetchAtDidFetchReturnValue: (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])! + var fetchAtDidFetchClosure: ((CompactBlockRange, @escaping (Float) async -> Void) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]))? + + func fetch(at range: CompactBlockRange, didFetch: @escaping (Float) async -> Void) async throws -> (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity]) { + if let error = fetchAtDidFetchThrowableError { + throw error + } + fetchAtDidFetchCallsCount += 1 + fetchAtDidFetchReceivedArguments = (range: range, didFetch: didFetch) + if let closure = fetchAtDidFetchClosure { + return try await closure(range, didFetch) + } else { + return fetchAtDidFetchReturnValue + } + } + } actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {