Skip to content

Commit

Permalink
[Electric-Coin-Company#1176] Cover Spend before Sync with tests
Browse files Browse the repository at this point in the history
- WIP

[Electric-Coin-Company#1176] Cover Spend before Sync with tests

- next batch of updates

[Electric-Coin-Company#1176] Cover Spend before Sync with tests

- last batch of fixes and new tests

[Electric-Coin-Company#1176] Cover Spend before Sync with tests

- package.resolved updated

[Electric-Coin-Company#1176] Cover Spend before Sync with tests (Electric-Coin-Company#1212)

- added tests for brand new actions related Spend before Sync
- RewindActionTests
- UpdateChainTipActionTests
- UpdateSubtreeRootsActionTests
- ProcessSuggestedScanRangesActionTests
  • Loading branch information
LukasKorba committed Aug 28, 2023
1 parent f789fa3 commit 6f8e7b9
Show file tree
Hide file tree
Showing 28 changed files with 1,491 additions and 319 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "c3b5f4ebee758b619aa81e844d791aa1fd35b918"
"revision" : "6a53c9e32520b46f8c70597e27b335105fabfc21"
}
}
],
Expand Down
2 changes: 1 addition & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "c3b5f4ebee758b619aa81e844d791aa1fd35b918"
"revision" : "6a53c9e32520b46f8c70597e27b335105fabfc21"
}
}
],
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.14.0"),
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1"),
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "c3b5f4ebee758b619aa81e844d791aa1fd35b918")
.package(url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", revision: "6a53c9e32520b46f8c70597e27b335105fabfc21")
],
targets: [
.target(
Expand Down
27 changes: 26 additions & 1 deletion Sources/ZcashLightClientKit/Block/Actions/Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,32 @@

import Foundation

actor ActionContext {
protocol ActionContext {
var state: CBPState { get async }
var prevState: CBPState? { get async }
var syncControlData: SyncControlData { get async }
var preferredSyncAlgorithm: SyncAlgorithm { get }
var supportedSyncAlgorithm: SyncAlgorithm? { get async }
var requestedRewindHeight: BlockHeight? { get async }
var totalProgressRange: CompactBlockRange { get async }
var processedHeight: BlockHeight { get async }
var lastChainTipUpdateTime: TimeInterval { get async }
var lastScannedHeight: BlockHeight? { get async }
var lastEnhancedHeight: BlockHeight? { get async }

func update(state: CBPState) async
func update(syncControlData: SyncControlData) async
func update(totalProgressRange: CompactBlockRange) async
func update(processedHeight: BlockHeight) async
func update(lastChainTipUpdateTime: TimeInterval) async
func update(lastScannedHeight: BlockHeight) async
func update(lastDownloadedHeight: BlockHeight) async
func update(lastEnhancedHeight: BlockHeight?) async
func update(supportedSyncAlgorithm: SyncAlgorithm) async
func update(requestedRewindHeight: BlockHeight) async
}

actor ActionContextImpl: ActionContext {
var state: CBPState
var prevState: CBPState?
var syncControlData: SyncControlData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ extension ClearCacheAction: Action {

func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
try await storage.clear()

if await context.prevState == .idle {
await context.update(state: .migrateLegacyCacheDB)
} else {
Expand All @@ -37,6 +38,7 @@ extension ClearCacheAction: Action {
}
}
}

return context
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ extension DownloadAction: Action {
let downloadLimit = await context.syncControlData.latestBlockHeight >= potentialDownloadLimit ? potentialDownloadLimit : batchRangeEnd

logger.debug("Starting download with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")

await downloader.update(latestDownloadedBlockHeight: batchRange.lowerBound, force: true) // SbS
try await downloader.setSyncRange(lastScannedHeight...latestBlockHeight, batchSize: config.batchSize)
await downloader.setDownloadLimit(downloadLimit)
Expand Down
2 changes: 0 additions & 2 deletions Sources/ZcashLightClientKit/Block/Actions/EnhanceAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ final class EnhanceAction {
let blockEnhancer: BlockEnhancer
let configProvider: CompactBlockProcessor.ConfigProvider
let logger: Logger
let transactionRepository: TransactionRepository

init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
blockEnhancer = container.resolve(BlockEnhancer.self)
self.configProvider = configProvider
logger = container.resolve(Logger.self)
transactionRepository = container.resolve(TransactionRepository.self)
}

func decideWhatToDoNext(context: ActionContext, lastScannedHeight: BlockHeight) async -> ActionContext {
Expand Down
4 changes: 2 additions & 2 deletions Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ actor CompactBlockProcessor {
)

let configProvider = ConfigProvider(config: config)
context = ActionContext(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
context = ActionContextImpl(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
actions = Self.makeActions(container: container, configProvider: configProvider)

self.metrics = container.resolve(SDKMetrics.self)
Expand Down Expand Up @@ -638,7 +638,7 @@ extension CompactBlockProcessor {

private func resetContext() async {
let lastEnhancedheight = await context.lastEnhancedHeight
context = ActionContext(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
context = ActionContextImpl(state: .idle, preferredSyncAlgorithm: config.syncAlgorithm)
await context.update(lastEnhancedHeight: lastEnhancedheight)
await compactBlockProgress.reset()
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/NetworkTests/BlockStreamingTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class BlockStreamingTest: ZcashTestCase {
latestScannedHeight: startHeight,
firstUnenhancedHeight: nil
)
let context = ActionContext(state: .download)
let context = ActionContextMock()
await context.update(syncControlData: syncControlData)

let expectation = XCTestExpectation()
Expand Down Expand Up @@ -175,7 +175,7 @@ class BlockStreamingTest: ZcashTestCase {
latestScannedHeight: startHeight,
firstUnenhancedHeight: nil
)
let context = ActionContext(state: .download)
let context = ActionContextMock()
await context.update(syncControlData: syncControlData)

let date = Date()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import XCTest

final class ActionContextStateTests: XCTestCase {
func testPreviousState() async throws {
let syncContext: ActionContext = .init(state: .idle)
let syncContext = ActionContextImpl(state: .idle)

await syncContext.update(state: .clearCache)

Expand All @@ -32,4 +32,15 @@ final class ActionContextStateTests: XCTestCase {
XCTFail("syncContext.prevState is not expected to be nil.")
}
}

func testDefaultSyncAlgorith() async throws {
let syncContext = ActionContextImpl(state: .idle)

let preferredSyncAlgorithm = await syncContext.preferredSyncAlgorithm

XCTAssertTrue(
preferredSyncAlgorithm == .linear,
"ActionContext default preferredSyncAlgorithm is expected to be .linear but received \(preferredSyncAlgorithm)"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,58 @@ import XCTest
final class ClearAlreadyScannedBlocksActionTests: ZcashTestCase {
func testClearAlreadyScannedBlocksAction_NextAction() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()

let clearAlreadyScannedBlocksAction = setupAction(compactBlockRepositoryMock)

do {
let context = ActionContextMock.default()
context.lastScannedHeight = -1

let nextContext = try await clearAlreadyScannedBlocksAction.run(with: context) { _ in }

XCTAssertTrue(compactBlockRepositoryMock.clearUpToCalled, "storage.clear(upTo:) is expected to be called.")

let acResult = nextContext.checkStateIs(.enhance)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearAlreadyScannedBlocksAction_NextAction is not expected to fail. \(error)")
}
}

func testClearAlreadyScannedBlocksAction_LastScanHeightZcashError() async throws {
let clearAlreadyScannedBlocksAction = setupAction()

do {
let context = ActionContextMock()

_ = try await clearAlreadyScannedBlocksAction.run(with: context) { _ in }

XCTFail("testClearAlreadyScannedBlocksAction_LastScanHeightZcashError should throw an error so fail here is unexpected.")
} catch ZcashError.compactBlockProcessorLastScannedHeight {
// it's expected to end up here because we test that error is a specific one and Swift automatically catched it up for us
} catch {
XCTFail(
"""
testClearAlreadyScannedBlocksAction_NextAction is expected to fail
with ZcashError.compactBlockProcessorLastScannedHeight but received \(error)
"""
)
}
}

private func setupAction(
_ compactBlockRepositoryMock: CompactBlockRepositoryMock = CompactBlockRepositoryMock()
) -> ClearAlreadyScannedBlocksAction {
let transactionRepositoryMock = TransactionRepositoryMock()

compactBlockRepositoryMock.clearUpToClosure = { _ in }
transactionRepositoryMock.lastScannedHeightReturnValue = 1

mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }

let clearAlreadyScannedBlocksAction = ClearAlreadyScannedBlocksAction(
return ClearAlreadyScannedBlocksAction(
container: mockContainer
)

do {
let context = ActionContext(state: .clearAlreadyScannedBlocks)
await context.update(lastScannedHeight: -1)

let nextContext = try await clearAlreadyScannedBlocksAction.run(with: context) { _ in }
XCTAssertTrue(compactBlockRepositoryMock.clearUpToCalled, "storage.clear(upTo:) is expected to be called.")
let nextState = await nextContext.state
XCTAssertTrue(
nextState == .enhance,
"nextContext after .clearAlreadyScannedBlocks is expected to be .enhance but received \(nextState)"
)
} catch {
XCTFail("testClearAlreadyScannedBlocksAction_NextAction is not expected to fail. \(error)")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,119 @@ import XCTest
@testable import ZcashLightClientKit

final class ClearCacheActionTests: ZcashTestCase {
func testClearCacheAction_NextAction() async throws {
func testClearCacheAction_MigrationLegacyCacheDB() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()

compactBlockRepositoryMock.clearClosure = { }
let clearCacheAction = setupAction(compactBlockRepositoryMock)

mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }
do {
let context = ActionContextMock.default()
context.prevState = .idle

let clearCacheAction = ClearCacheAction(
container: mockContainer
)
let nextContext = try await clearCacheAction.run(with: context) { _ in }

XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")

let acResult = nextContext.checkStateIs(.migrateLegacyCacheDB)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearCacheAction_MigrationLegacyCacheDB is not expected to fail. \(error)")
}
}

func testClearCacheAction_FinishedLinear() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()

let clearCacheAction = setupAction(compactBlockRepositoryMock)

do {
let nextContext = try await clearCacheAction.run(with: .init(state: .clearCache)) { _ in }
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .linear

let nextContext = try await clearCacheAction.run(with: context) { _ in }

XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
let nextState = await nextContext.state
XCTAssertTrue(
nextState == .finished,
"nextContext after .clearCache is expected to be .finished but received \(nextState)"

let acResult = nextContext.checkStateIs(.finished)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
}
}

func testClearCacheAction_PreferredSbSNoSupportedSyncAlgorithm() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()

let clearCacheAction = setupAction(compactBlockRepositoryMock)

do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync

_ = try await clearCacheAction.run(with: context) { _ in }
} catch ZcashError.compactBlockProcessorSupportedSyncAlgorithm {
XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")
} catch {
XCTFail(
"""
testClearCacheAction_PredferedSbSNoSupportedSyncAlgorithm is expected to fail
with ZcashError.compactBlockProcessorSupportedSyncAlgorithm but received \(error)
"""
)
}
}

func testClearCacheAction_PreferredSbSSupportedSyncAlgorithmLinear() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()

let clearCacheAction = setupAction(compactBlockRepositoryMock)

do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
context.supportedSyncAlgorithm = .linear

let nextContext = try await clearCacheAction.run(with: context) { _ in }

XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")

let acResult = nextContext.checkStateIs(.finished)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
}
}

func testClearCacheAction_PreferedSbSSupportedSyncAlgorithmSbS() async throws {
let compactBlockRepositoryMock = CompactBlockRepositoryMock()

let clearCacheAction = setupAction(compactBlockRepositoryMock)

do {
let context = ActionContextMock.default()
context.underlyingPreferredSyncAlgorithm = .spendBeforeSync
context.supportedSyncAlgorithm = .spendBeforeSync

let nextContext = try await clearCacheAction.run(with: context) { _ in }

XCTAssertTrue(compactBlockRepositoryMock.clearCalled, "storage.clear() is expected to be called.")

let acResult = nextContext.checkStateIs(.processSuggestedScanRanges)
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
} catch {
XCTFail("testClearCacheAction_NextAction is not expected to fail. \(error)")
XCTFail("testClearCacheAction_FinishedLinear is not expected to fail. \(error)")
}
}

private func setupAction(
_ compactBlockRepositoryMock: CompactBlockRepositoryMock = CompactBlockRepositoryMock()
) -> ClearCacheAction {
compactBlockRepositoryMock.clearClosure = { }

mockContainer.mock(type: CompactBlockRepository.self, isSingleton: true) { _ in compactBlockRepositoryMock }

return ClearCacheAction(
container: mockContainer
)
}
}
Loading

0 comments on commit 6f8e7b9

Please sign in to comment.