Merge pull request #1124 from LukasKorba/1122-Implement-FileManager-protocol-and-dependency

- ZcashFileManager protocol
- MigrateLegacyCacheDBAction refactored to be dependent on ZcashFileManager
- ZcashFileManager mock added
This commit is contained in:
Lukas Korba 2023-05-24 09:40:20 +02:00 committed by GitHub
commit e267435d84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 137 additions and 24 deletions

View File

@ -12,12 +12,14 @@ final class MigrateLegacyCacheDBAction {
private let internalSyncProgress: InternalSyncProgress
private let storage: CompactBlockRepository
private let transactionRepository: TransactionRepository
private let fileManager: ZcashFileManager
init(container: DIContainer, config: CompactBlockProcessor.Configuration) {
self.config = config
internalSyncProgress = container.resolve(InternalSyncProgress.self)
storage = container.resolve(CompactBlockRepository.self)
transactionRepository = container.resolve(TransactionRepository.self)
fileManager = container.resolve(ZcashFileManager.self)
}
private func updateState(_ context: ActionContext) async -> ActionContext {
@ -48,13 +50,13 @@ extension MigrateLegacyCacheDBAction: Action {
// if the URL provided is not readable, it means that the client has a reference
// to the cacheDb file but it has been deleted in a prior sync cycle. there's
// nothing to do here.
guard FileManager.default.isReadableFile(atPath: legacyCacheDbURL.path) else {
guard fileManager.isReadableFile(atPath: legacyCacheDbURL.path) else {
return await updateState(context)
}
do {
// if there's a readable file at the provided URL, delete it.
try FileManager.default.removeItem(at: legacyCacheDbURL)
try fileManager.removeItem(at: legacyCacheDbURL)
} catch {
throw ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb(error)
}

View File

@ -37,6 +37,7 @@ actor CompactBlockProcessor {
private let service: LightWalletService
private let storage: CompactBlockRepository
private let transactionRepository: TransactionRepository
private let fileManager: ZcashFileManager
private var retryAttempts: Int = 0
private var backoffTimer: Timer?
@ -195,6 +196,7 @@ actor CompactBlockProcessor {
self.config = config
self.transactionRepository = container.resolve(TransactionRepository.self)
self.accountRepository = accountRepository
self.fileManager = container.resolve(ZcashFileManager.self)
}
deinit {
@ -372,8 +374,8 @@ extension CompactBlockProcessor {
private func wipeLegacyCacheDbIfNeeded() {
guard let cacheDbURL = config.cacheDbURL else { return }
guard FileManager.default.isDeletableFile(atPath: cacheDbURL.pathExtension) else { return }
try? FileManager.default.removeItem(at: cacheDbURL)
guard fileManager.isDeletableFile(atPath: cacheDbURL.pathExtension) else { return }
try? fileManager.removeItem(at: cacheDbURL)
}
}

View File

@ -91,6 +91,10 @@ enum Dependencies {
let logger = di.resolve(Logger.self)
return InternalSyncProgress(alias: alias, storage: UserDefaults.standard, logger: logger)
}
container.register(type: ZcashFileManager.self, isSingleton: true) { _ in
FileManager.default
}
}
static func setupCompactBlockProcessor(

View File

@ -0,0 +1,16 @@
//
// ZcashFileManager.swift
//
//
// Created by Lukáš Korba on 23.05.2023.
//
import Foundation
protocol ZcashFileManager {
func isReadableFile(atPath path: String) -> Bool
func removeItem(at URL: URL) throws
func isDeletableFile(atPath path: String) -> Bool
}
extension FileManager: ZcashFileManager { }

View File

@ -124,7 +124,7 @@ final class ChecksBeforeSyncActionTests: ZcashTestCase {
_ internalSyncProgressStorageMock: InternalSyncProgressStorageMock = InternalSyncProgressStorageMock(),
_ loggerMock: LoggerMock = LoggerMock()
) -> ChecksBeforeSyncAction {
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { di in
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { _ in
InternalSyncProgress(alias: .default, storage: internalSyncProgressStorageMock, logger: loggerMock)
}

View File

@ -20,7 +20,7 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
underlyingScanRange = nil
}
func testComputeSyncRangesActionTests_computeTotalProgressRange_noDownloadNoScanRange() async throws {
func testComputeSyncRangesAction_computeTotalProgressRange_noDownloadNoScanRange() async throws {
let computeSyncRangesAction = setupAction()
let syncRanges = setupSyncRanges()
@ -29,11 +29,11 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
XCTAssertTrue(
totalProgressRange == 0...0,
"testComputeSyncRangesActionTests_computeTotalProgressRange_A is expected to be 0...0 but received \(totalProgressRange)"
"testComputeSyncRangesAction_computeTotalProgressRange_noDownloadNoScanRange is expected to be 0...0 but received \(totalProgressRange)"
)
}
func testComputeSyncRangesActionTests_computeTotalProgressRange_ValidRange() async throws {
func testComputeSyncRangesAction_computeTotalProgressRange_ValidRange() async throws {
let computeSyncRangesAction = setupAction()
underlyingDownloadRange = CompactBlockRange(uncheckedBounds: (1000, 2000))
@ -45,11 +45,11 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
XCTAssertTrue(
totalProgressRange == expectedRange,
"testComputeSyncRangesActionTests_computeTotalProgressRange_ValidRange is expected to be \(expectedRange) but received \(totalProgressRange)"
"testComputeSyncRangesAction_computeTotalProgressRange_ValidRange is expected to be \(expectedRange) but received \(totalProgressRange)"
)
}
func testComputeSyncRangesActionTests_finishProcessingCase() async throws {
func testComputeSyncRangesAction_finishProcessingCase() async throws {
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
@ -67,8 +67,14 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
do {
let nextContext = try await computeSyncRangesAction.run(with: syncContext) { _ in }
XCTAssertTrue(blockDownloaderServiceMock.lastDownloadedBlockHeightCalled, "downloaderService.lastDownloadedBlockHeight() is expected to be called.")
XCTAssertTrue(latestBlocksDataProviderMock.updateScannedDataCalled, "latestBlocksDataProvider.updateScannedData() is expected to be called.")
XCTAssertTrue(
blockDownloaderServiceMock.lastDownloadedBlockHeightCalled,
"downloaderService.lastDownloadedBlockHeight() is expected to be called."
)
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.")
@ -78,11 +84,11 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
"nextContext after .computeSyncRanges is expected to be .finished but received \(nextState)"
)
} catch {
XCTFail("testComputeSyncRangesActionTests_FinishProcessing is not expected to fail. \(error)")
XCTFail("testComputeSyncRangesAction_finishProcessingCase is not expected to fail. \(error)")
}
}
func testComputeSyncRangesActionTests_checksBeforeSyncCase() async throws {
func testComputeSyncRangesAction_checksBeforeSyncCase() async throws {
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
@ -101,8 +107,14 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
do {
let nextContext = try await computeSyncRangesAction.run(with: syncContext) { _ in }
XCTAssertTrue(blockDownloaderServiceMock.lastDownloadedBlockHeightCalled, "downloaderService.lastDownloadedBlockHeight() is expected to be called.")
XCTAssertTrue(latestBlocksDataProviderMock.updateScannedDataCalled, "latestBlocksDataProvider.updateScannedData() is expected to be called.")
XCTAssertTrue(
blockDownloaderServiceMock.lastDownloadedBlockHeightCalled,
"downloaderService.lastDownloadedBlockHeight() is expected to be called."
)
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.")
@ -112,11 +124,11 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
"nextContext after .computeSyncRanges is expected to be .checksBeforeSync but received \(nextState)"
)
} catch {
XCTFail("testComputeSyncRangesActionTests_checksBeforeSyncCase is not expected to fail. \(error)")
XCTFail("testComputeSyncRangesAction_checksBeforeSyncCase is not expected to fail. \(error)")
}
}
func testComputeSyncRangesActionTests_waitCase() async throws {
func testComputeSyncRangesAction_waitCase() async throws {
let blockDownloaderServiceMock = BlockDownloaderServiceMock()
let latestBlocksDataProviderMock = LatestBlocksDataProviderMock()
let internalSyncProgressStorageMock = InternalSyncProgressStorageMock()
@ -138,8 +150,14 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
do {
let nextContext = try await computeSyncRangesAction.run(with: syncContext) { _ in }
XCTAssertTrue(blockDownloaderServiceMock.lastDownloadedBlockHeightCalled, "downloaderService.lastDownloadedBlockHeight() is expected to be called.")
XCTAssertTrue(latestBlocksDataProviderMock.updateScannedDataCalled, "latestBlocksDataProvider.updateScannedData() is expected to be called.")
XCTAssertTrue(
blockDownloaderServiceMock.lastDownloadedBlockHeightCalled,
"downloaderService.lastDownloadedBlockHeight() is expected to be called."
)
XCTAssertTrue(
latestBlocksDataProviderMock.updateScannedDataCalled,
"latestBlocksDataProvider.updateScannedData() is expected to be called."
)
XCTAssertTrue(latestBlocksDataProviderMock.updateBlockDataCalled, "latestBlocksDataProvider.updateBlockData() is expected to be called.")
XCTAssertTrue(loggerMock.infoFileFunctionLineCalled, "logger.info() is expected to be called.")
@ -149,7 +167,7 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
"nextContext after .computeSyncRanges is expected to be .finished but received \(nextState)"
)
} catch {
XCTFail("testComputeSyncRangesActionTests_waitCase is not expected to fail. \(error)")
XCTFail("testComputeSyncRangesAction_waitCase is not expected to fail. \(error)")
}
}
@ -180,7 +198,7 @@ final class ComputeSyncRangesActionTests: ZcashTestCase {
_ internalSyncProgressStorageMock: InternalSyncProgressStorageMock = InternalSyncProgressStorageMock(),
_ loggerMock: LoggerMock = LoggerMock()
) -> ComputeSyncRangesAction {
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { di in
mockContainer.register(type: InternalSyncProgress.self, isSingleton: true) { _ in
InternalSyncProgress(alias: .default, storage: internalSyncProgressStorageMock, logger: loggerMock)
}

View File

@ -20,7 +20,7 @@ final class DownloadActionTests: ZcashTestCase {
transactionRepositoryMock.lastScannedHeightReturnValue = 1
blockDownloaderMock.setSyncRangeBatchSizeClosure = { _, _ in }
blockDownloaderMock.setDownloadLimitClosure = { _ in }
blockDownloaderMock.startDownloadMaxBlockBufferSizeClosure = { _ in }
blockDownloaderMock.startDownloadMaxBlockBufferSizeClosure = { _ in }
blockDownloaderMock.waitUntilRequestedBlocksAreDownloadedInClosure = { _ in }
let downloadAction = setupAction(
@ -69,7 +69,10 @@ final class DownloadActionTests: ZcashTestCase {
do {
let nextContext = try await downloadAction.run(with: syncContext) { _ in }
XCTAssertFalse(transactionRepositoryMock.lastScannedHeightCalled, "transactionRepository.lastScannedHeight() is not expected to be called.")
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.")

View File

@ -27,6 +27,7 @@ extension SaplingParametersHandler { }
extension Synchronizer { }
extension TransactionRepository { }
extension UTXOFetcher { }
extension ZcashFileManager { }
extension ZcashRustBackendWelding { }
// sourcery:end:

View File

@ -2012,6 +2012,73 @@ class UTXOFetcherMock: UTXOFetcher {
}
}
}
class ZcashFileManagerMock: ZcashFileManager {
init(
) {
}
// MARK: - isReadableFile
var isReadableFileAtPathCallsCount = 0
var isReadableFileAtPathCalled: Bool {
return isReadableFileAtPathCallsCount > 0
}
var isReadableFileAtPathReceivedPath: String?
var isReadableFileAtPathReturnValue: Bool!
var isReadableFileAtPathClosure: ((String) -> Bool)?
func isReadableFile(atPath path: String) -> Bool {
isReadableFileAtPathCallsCount += 1
isReadableFileAtPathReceivedPath = path
if let closure = isReadableFileAtPathClosure {
return closure(path)
} else {
return isReadableFileAtPathReturnValue
}
}
// MARK: - removeItem
var removeItemAtThrowableError: Error?
var removeItemAtCallsCount = 0
var removeItemAtCalled: Bool {
return removeItemAtCallsCount > 0
}
var removeItemAtReceivedURL: URL?
var removeItemAtClosure: ((URL) throws -> Void)?
func removeItem(at URL: URL) throws {
if let error = removeItemAtThrowableError {
throw error
}
removeItemAtCallsCount += 1
removeItemAtReceivedURL = URL
try removeItemAtClosure!(URL)
}
// MARK: - isDeletableFile
var isDeletableFileAtPathCallsCount = 0
var isDeletableFileAtPathCalled: Bool {
return isDeletableFileAtPathCallsCount > 0
}
var isDeletableFileAtPathReceivedPath: String?
var isDeletableFileAtPathReturnValue: Bool!
var isDeletableFileAtPathClosure: ((String) -> Bool)?
func isDeletableFile(atPath path: String) -> Bool {
isDeletableFileAtPathCallsCount += 1
isDeletableFileAtPathReceivedPath = path
if let closure = isDeletableFileAtPathClosure {
return closure(path)
} else {
return isDeletableFileAtPathReturnValue
}
}
}
actor ZcashRustBackendWeldingMock: ZcashRustBackendWelding {