ZcashLightClientKit/Tests/NetworkTests/BlockStreamingTest.swift

221 lines
8.2 KiB
Swift

//
// BlockStreamingTest.swift
// ZcashLightClientKit-Unit-Tests
//
// Created by Francisco Gindre on 5/25/21.
//
import XCTest
@testable import TestUtils
@testable import ZcashLightClientKit
class BlockStreamingTest: ZcashTestCase {
let testFileManager = FileManager()
var rustBackend: ZcashRustBackendWelding!
var endpoint: LightWalletEndpoint!
var service: LightWalletService!
var storage: FSCompactBlockRepository!
var internalSyncProgress: InternalSyncProgress!
var processorConfig: CompactBlockProcessor.Configuration!
var latestBlockHeight: BlockHeight!
var startHeight: BlockHeight!
override func setUp() async throws {
try await super.setUp()
logger = OSLogger(logLevel: .debug)
rustBackend = ZcashRustBackend.makeForTests(fsBlockDbRoot: testTempDirectory, networkType: .testnet)
logger = OSLogger(logLevel: .debug)
Dependencies.setup(
in: mockContainer,
urls: Initializer.URLs(
fsBlockDbRoot: testTempDirectory,
dataDbURL: try! __dataDbURL(),
generalStorageURL: testGeneralStorageDirectory,
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL()
),
alias: .default,
networkType: .testnet,
endpoint: LightWalletEndpointBuilder.default,
loggingPolicy: .default(.debug)
)
mockContainer.mock(type: LatestBlocksDataProvider.self, isSingleton: true) { _ in LatestBlocksDataProviderMock() }
mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in self.rustBackend }
let endpoint = LightWalletEndpoint(
address: LightWalletEndpointBuilder.eccTestnet.host,
port: 9067,
secure: true,
singleCallTimeoutInMillis: 10000,
streamingCallTimeoutInMillis: 10000
)
let service = LightWalletServiceFactory(endpoint: endpoint).make()
latestBlockHeight = try await service.latestBlockHeight()
startHeight = latestBlockHeight - 10_000
}
override func tearDownWithError() throws {
try super.tearDownWithError()
rustBackend = nil
try? FileManager.default.removeItem(at: __dataDbURL())
endpoint = nil
service = nil
storage = nil
internalSyncProgress = nil
processorConfig = nil
}
private func makeDependencies(timeout: Int64) async throws {
let endpoint = LightWalletEndpoint(
address: LightWalletEndpointBuilder.eccTestnet.host,
port: 9067,
secure: true,
singleCallTimeoutInMillis: timeout,
streamingCallTimeoutInMillis: timeout
)
self.endpoint = endpoint
service = LightWalletServiceFactory(endpoint: endpoint).make()
storage = FSCompactBlockRepository(
fsBlockDbRoot: testTempDirectory,
metadataStore: FSMetadataStore.live(
fsBlockDbRoot: testTempDirectory,
rustBackend: rustBackend,
logger: logger
),
blockDescriptor: .live,
contentProvider: DirectoryListingProviders.defaultSorted,
logger: logger
)
try await storage.create()
let internalSyncProgressStorage = InternalSyncProgressMemoryStorage()
try await internalSyncProgressStorage.set(startHeight, for: InternalSyncProgress.Key.latestDownloadedBlockHeight.rawValue)
internalSyncProgress = InternalSyncProgress(alias: .default, storage: internalSyncProgressStorage, logger: logger)
processorConfig = CompactBlockProcessor.Configuration.standard(
for: ZcashNetworkBuilder.network(for: .testnet),
walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight
)
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in
LightWalletServiceFactory(endpoint: endpoint).make()
}
let transactionRepositoryMock = TransactionRepositoryMock()
transactionRepositoryMock.lastScannedHeightReturnValue = startHeight
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
let blockDownloader = BlockDownloaderImpl(
service: service,
downloaderService: BlockDownloaderServiceImpl(service: service, storage: storage),
storage: storage,
internalSyncProgress: internalSyncProgress,
metrics: SDKMetrics(),
logger: logger
)
mockContainer.mock(type: BlockDownloader.self, isSingleton: true) { _ in blockDownloader }
}
func testStream() async throws {
try await makeDependencies(timeout: 10000)
var blocks: [ZcashCompactBlock] = []
let stream = service.blockStream(startHeight: startHeight, endHeight: latestBlockHeight)
do {
for try await compactBlock in stream {
blocks.append(compactBlock)
}
} catch {
XCTFail("failed with error: \(error)")
}
XCTAssertEqual(blocks.count, latestBlockHeight - startHeight + 1)
}
func testStreamCancellation() async throws {
try await makeDependencies(timeout: 10000)
let action = DownloadAction(container: mockContainer, configProvider: CompactBlockProcessor.ConfigProvider(config: processorConfig))
let syncRanges = SyncRanges(
latestBlockHeight: latestBlockHeight,
downloadRange: startHeight...latestBlockHeight,
scanRange: nil,
enhanceRange: nil,
fetchUTXORange: nil,
latestScannedHeight: startHeight,
latestDownloadedBlockHeight: startHeight
)
let context = ActionContext(state: .download)
await context.update(syncRanges: syncRanges)
let expectation = XCTestExpectation()
let cancelableTask = Task {
do {
_ = try await action.run(with: context, didUpdate: { _ in })
let lastDownloadedHeight = try await internalSyncProgress.latestDownloadedBlockHeight
// Just to be sure that download was interrupted before download was finished.
XCTAssertLessThan(lastDownloadedHeight, latestBlockHeight)
expectation.fulfill()
} catch {
XCTFail("Downloading failed with error: \(error)")
expectation.fulfill()
}
}
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
cancelableTask.cancel()
}
await fulfillment(of: [expectation], timeout: 5)
await action.stop()
}
func testStreamTimeout() async throws {
try await makeDependencies(timeout: 100)
let action = DownloadAction(container: mockContainer, configProvider: CompactBlockProcessor.ConfigProvider(config: processorConfig))
let syncRanges = SyncRanges(
latestBlockHeight: latestBlockHeight,
downloadRange: startHeight...latestBlockHeight,
scanRange: nil,
enhanceRange: nil,
fetchUTXORange: nil,
latestScannedHeight: startHeight,
latestDownloadedBlockHeight: startHeight
)
let context = ActionContext(state: .download)
await context.update(syncRanges: syncRanges)
let date = Date()
do {
_ = try await action.run(with: context, didUpdate: { _ in })
XCTFail("It is expected that this downloading fails.")
} catch {
if let lwdError = error as? ZcashError {
switch lwdError {
case .serviceBlockStreamFailed:
XCTAssert(true)
default:
XCTFail("LWD Service error found, but should have been a timeLimit reached \(lwdError)")
}
} else {
XCTFail("Error should have been a timeLimit reached Error")
}
}
let now = Date()
let elapsed = now.timeIntervalSince(date)
print("took \(elapsed) seconds")
await action.stop()
}
}