diff --git a/Package.swift b/Package.swift index e5755ae92..452bbbd85 100644 --- a/Package.swift +++ b/Package.swift @@ -19,7 +19,7 @@ let package = Package( targets: ["Lockman"]) ], dependencies: [ - .package(url: "https://github.com/pointfreeco/swift-composable-architecture", exact: "1.22.3"), + .package(url: "https://github.com/pointfreeco/swift-composable-architecture", exact: "1.23.0"), .package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"603.0.0"), .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.3"), .package(url: "https://github.com/apple/swift-collections.git", from: "1.1.0"), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 1e28ac132..139c9a642 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -19,7 +19,7 @@ let package = Package( targets: ["Lockman"]) ], dependencies: [ - .package(url: "https://github.com/pointfreeco/swift-composable-architecture", exact: "1.22.3"), + .package(url: "https://github.com/pointfreeco/swift-composable-architecture", exact: "1.23.0"), .package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"603.0.0"), .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.3"), .package(url: "https://github.com/apple/swift-collections.git", from: "1.1.0"), diff --git a/Sources/Lockman/Composable/Effect+Lockman.swift b/Sources/Lockman/Composable/Effect+Lockman.swift index c7dc9488a..cd90108c4 100644 --- a/Sources/Lockman/Composable/Effect+Lockman.swift +++ b/Sources/Lockman/Composable/Effect+Lockman.swift @@ -82,15 +82,18 @@ extension Effect { unlockOption: unlockOption, onSuccess: { action, unlock in let shouldBeCancellable = action.createLockmanInfo().isCancellationTarget - let cancellableEffect = shouldBeCancellable ? concatenatedEffect.cancellable(id: boundaryId) : concatenatedEffect + let cancellableEffect = + shouldBeCancellable ? concatenatedEffect.cancellable(id: boundaryId) : concatenatedEffect return Effect.concatenate([cancellableEffect, .run { _ in unlock() }]) }, onSuccessWithPrecedingCancellation: { action, error, unlock in let shouldBeCancellable = action.createLockmanInfo().isCancellationTarget - let cancellableEffect = shouldBeCancellable ? concatenatedEffect.cancellable(id: boundaryId) : concatenatedEffect + let cancellableEffect = + shouldBeCancellable ? concatenatedEffect.cancellable(id: boundaryId) : concatenatedEffect let completeEffect = Effect.concatenate([cancellableEffect, .run { _ in unlock() }]) - - let cancellationError = LockmanCancellationError(action: action, boundaryId: boundaryId, reason: error) + + let cancellationError = LockmanCancellationError( + action: action, boundaryId: boundaryId, reason: error) if let lockFailure = lockFailure { return .concatenate([ .run { send in await lockFailure(cancellationError, send) }, @@ -101,7 +104,8 @@ extension Effect { return .concatenate([.cancel(id: boundaryId), completeEffect]) }, onCancel: { action, error in - let cancellationError = LockmanCancellationError(action: action, boundaryId: boundaryId, reason: error) + let cancellationError = LockmanCancellationError( + action: action, boundaryId: boundaryId, reason: error) if let lockFailure = lockFailure { return .run { send in await lockFailure(cancellationError, send) } } @@ -200,15 +204,18 @@ extension Effect { unlockOption: unlockOption, onSuccess: { action, unlock in let shouldBeCancellable = action.createLockmanInfo().isCancellationTarget - let cancellableEffect = shouldBeCancellable ? operation.cancellable(id: boundaryId) : operation + let cancellableEffect = + shouldBeCancellable ? operation.cancellable(id: boundaryId) : operation return Effect.concatenate([cancellableEffect, .run { _ in unlock() }]) }, onSuccessWithPrecedingCancellation: { action, error, unlock in let shouldBeCancellable = action.createLockmanInfo().isCancellationTarget - let cancellableEffect = shouldBeCancellable ? operation.cancellable(id: boundaryId) : operation + let cancellableEffect = + shouldBeCancellable ? operation.cancellable(id: boundaryId) : operation let completeEffect = Effect.concatenate([cancellableEffect, .run { _ in unlock() }]) - - let cancellationError = LockmanCancellationError(action: action, boundaryId: boundaryId, reason: error) + + let cancellationError = LockmanCancellationError( + action: action, boundaryId: boundaryId, reason: error) if let lockFailure = lockFailure { return .concatenate([ .run { send in await lockFailure(cancellationError, send) }, @@ -219,7 +226,8 @@ extension Effect { return .concatenate([.cancel(id: boundaryId), completeEffect]) }, onCancel: { action, error in - let cancellationError = LockmanCancellationError(action: action, boundaryId: boundaryId, reason: error) + let cancellationError = LockmanCancellationError( + action: action, boundaryId: boundaryId, reason: error) if let lockFailure = lockFailure { return .run { send in await lockFailure(cancellationError, send) } } diff --git a/Sources/Lockman/Composable/LockmanReducer.swift b/Sources/Lockman/Composable/LockmanReducer.swift index 15e6c33e7..eaf3f7f6d 100644 --- a/Sources/Lockman/Composable/LockmanReducer.swift +++ b/Sources/Lockman/Composable/LockmanReducer.swift @@ -99,9 +99,12 @@ public struct LockmanReducer: Reducer { let effect = self.base.reduce(into: &state, action: action) let shouldBeCancellable = lockmanAction.createLockmanInfo().isCancellationTarget let cancellableEffect = shouldBeCancellable ? effect.cancellable(id: boundaryId) : effect - let completeEffect = Effect.concatenate([cancellableEffect, .run { _ in unlock() }]) - - let cancellationError = LockmanCancellationError(action: lockmanAction, boundaryId: boundaryId, reason: error) + let completeEffect = Effect.concatenate([ + cancellableEffect, .run { _ in unlock() }, + ]) + + let cancellationError = LockmanCancellationError( + action: lockmanAction, boundaryId: boundaryId, reason: error) if let lockFailure = lockFailure { return .concatenate([ .run { send in await lockFailure(cancellationError, send) }, @@ -112,7 +115,8 @@ public struct LockmanReducer: Reducer { return .concatenate([.cancel(id: boundaryId), completeEffect]) }, onCancel: { _, error in - let cancellationError = LockmanCancellationError(action: lockmanAction, boundaryId: boundaryId, reason: error) + let cancellationError = LockmanCancellationError( + action: lockmanAction, boundaryId: boundaryId, reason: error) if let lockFailure = lockFailure { return .run { send in await lockFailure(cancellationError, send) } } diff --git a/Sources/Lockman/Core/LockmanManager.swift b/Sources/Lockman/Core/LockmanManager.swift index 2661ccfd7..9f9dc5225 100644 --- a/Sources/Lockman/Core/LockmanManager.swift +++ b/Sources/Lockman/Core/LockmanManager.swift @@ -322,7 +322,7 @@ extension LockmanManager { /// action: action, /// boundaryId: boundaryId, /// unlockOption: nil, - /// onSuccess: { _, unlock in + /// onSuccess: { _, unlock in /// defer { unlock() } /// performWork() /// return true @@ -346,29 +346,31 @@ extension LockmanManager { boundaryId: B, unlockOption: LockmanUnlockOption?, onSuccess: (A, @escaping @Sendable () -> Void) -> T, - onSuccessWithPrecedingCancellation: (A, any LockmanPrecedingCancellationError, @escaping @Sendable () -> Void) -> T, + onSuccessWithPrecedingCancellation: ( + A, any LockmanPrecedingCancellationError, @escaping @Sendable () -> Void + ) -> T, onCancel: (A, any LockmanError) -> T, onError: (A, any Error) -> T ) -> T { do { // Capture lockmanInfo once to ensure consistent uniqueId throughout lock lifecycle let lockmanInfo = action.createLockmanInfo() - + // Acquire lock with integrated unlock token let result = try acquireLock( lockmanInfo: lockmanInfo, boundaryId: boundaryId, unlockOption: unlockOption ?? action.unlockOption ) - + // Handle lock result with callback execution switch result { case .success(let unlockToken): return onSuccess(action, unlockToken.callAsFunction) - + case .successWithPrecedingCancellation(let unlockToken, let error): return onSuccessWithPrecedingCancellation(action, error, unlockToken.callAsFunction) - + case .cancel(let error): return onCancel(action, error) } diff --git a/Sources/Lockman/Core/LockmanResult.swift b/Sources/Lockman/Core/LockmanResult.swift index 9c968c0a1..642fd7bb2 100644 --- a/Sources/Lockman/Core/LockmanResult.swift +++ b/Sources/Lockman/Core/LockmanResult.swift @@ -1,4 +1,3 @@ - /// The result of attempting to acquire a lock with integrated unlock capability. /// /// This enum represents the possible outcomes when a strategy attempts diff --git a/Sources/Lockman/Core/Strategies/Core/LockmanStrategyResult.swift b/Sources/Lockman/Core/Strategies/Core/LockmanStrategyResult.swift index f063aa9b6..7cc776abf 100644 --- a/Sources/Lockman/Core/Strategies/Core/LockmanStrategyResult.swift +++ b/Sources/Lockman/Core/Strategies/Core/LockmanStrategyResult.swift @@ -74,10 +74,13 @@ extension LockmanStrategyResult { return true case (.cancel(let lhsError), .cancel(let rhsError)): return String(describing: lhsError) == String(describing: rhsError) - case (.successWithPrecedingCancellation(let lhsError), .successWithPrecedingCancellation(let rhsError)): + case ( + .successWithPrecedingCancellation(let lhsError), + .successWithPrecedingCancellation(let rhsError) + ): return String(describing: lhsError) == String(describing: rhsError) default: return false } } -} \ No newline at end of file +} diff --git a/Tests/LockmanTests/Supports/TestSupport.swift b/Tests/LockmanTests/Supports/TestSupport.swift index 9f28ac5a4..f7dba60d8 100644 --- a/Tests/LockmanTests/Supports/TestSupport.swift +++ b/Tests/LockmanTests/Supports/TestSupport.swift @@ -167,7 +167,9 @@ public final class TestSingleExecutionStrategy: LockmanStrategy, @unchecked Send LockmanStrategyId(name: "TestSingleExecutionStrategy") } - public func canLock(boundaryId: B, info: TestLockmanInfo) -> LockmanStrategyResult { + public func canLock(boundaryId: B, info: TestLockmanInfo) + -> LockmanStrategyResult + { lock.withLock { if lockedActions.contains(info.actionId) { let error = LockmanCancellationError( diff --git a/Tests/LockmanTests/Unit/Core/LockmanManagerGenericLockTests.swift b/Tests/LockmanTests/Unit/Core/LockmanManagerGenericLockTests.swift index d235d8ed6..b2c782886 100644 --- a/Tests/LockmanTests/Unit/Core/LockmanManagerGenericLockTests.swift +++ b/Tests/LockmanTests/Unit/Core/LockmanManagerGenericLockTests.swift @@ -1,4 +1,5 @@ import XCTest + @testable import Lockman final class LockmanManagerGenericLockTests: XCTestCase { @@ -6,24 +7,24 @@ final class LockmanManagerGenericLockTests: XCTestCase { super.setUp() LockmanManager.config.reset() } - + override func tearDown() { LockmanManager.cleanup.all() super.tearDown() } - + // MARK: - Success Tests - + func testGenericLock_Success_CallsOnSuccessWithUnlockToken() async throws { let container = LockmanStrategyContainer() let strategy = TestSingleExecutionStrategy() try container.register(strategy) - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.test let boundaryId = TestBoundaryId.test var unlockCalled = false - + let result: String = LockmanManager.lock( action: action, boundaryId: boundaryId, @@ -47,22 +48,22 @@ final class LockmanManagerGenericLockTests: XCTestCase { return "failure" } ) - + XCTAssertEqual(result, "success") XCTAssertTrue(unlockCalled) } } - + func testGenericLock_Success_WithExplicitUnlockOption() async throws { let container = LockmanStrategyContainer() let strategy = TestSingleExecutionStrategy() try container.register(strategy) - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.increment let boundaryId = TestBoundaryId.test var unlockCalled = false - + let result: Int = LockmanManager.lock( action: action, boundaryId: boundaryId, @@ -82,23 +83,23 @@ final class LockmanManagerGenericLockTests: XCTestCase { return 0 } ) - + XCTAssertEqual(result, 42) XCTAssertTrue(unlockCalled) } } - + // MARK: - Cancel Tests - + func testGenericLock_Cancel_CallsOnCancel() async throws { let container = LockmanStrategyContainer() let strategy = TestSingleExecutionStrategy() try container.register(strategy) - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.test // Use same action to trigger conflict let boundaryId = TestBoundaryId.test - + // First lock acquisition - don't unlock to simulate ongoing operation let firstResult: Bool = LockmanManager.lock( action: action, @@ -113,7 +114,7 @@ final class LockmanManagerGenericLockTests: XCTestCase { onError: { _, _ in false } ) XCTAssertTrue(firstResult) - + // Second lock acquisition with same action should be cancelled var cancelCalled = false let result: String = LockmanManager.lock( @@ -139,23 +140,23 @@ final class LockmanManagerGenericLockTests: XCTestCase { return "failure" } ) - + XCTAssertEqual(result, "cancelled") XCTAssertTrue(cancelCalled) } } - + // MARK: - Error Tests - + func testGenericLock_StrategyNotRegistered_CallsOnError() async throws { let container = LockmanStrategyContainer() // Don't register any strategy - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.test let boundaryId = TestBoundaryId.test var errorCalled = false - + let result: String = LockmanManager.lock( action: action, boundaryId: boundaryId, @@ -179,53 +180,65 @@ final class LockmanManagerGenericLockTests: XCTestCase { return "error" } ) - + XCTAssertEqual(result, "error") XCTAssertTrue(errorCalled) } } - + // MARK: - Type Safety Tests - + func testGenericLock_DifferentReturnTypes_Success() async throws { let container = LockmanStrategyContainer() let strategy = TestSingleExecutionStrategy() try container.register(strategy) - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.test let boundaryId = TestBoundaryId.test - + // Test with Bool return type let boolResult: Bool = LockmanManager.lock( action: action, boundaryId: boundaryId, unlockOption: nil, - onSuccess: { _, unlock in unlock(); return true }, - onSuccessWithPrecedingCancellation: { _, _, unlock in unlock(); return false }, + onSuccess: { _, unlock in + unlock() + return true + }, + onSuccessWithPrecedingCancellation: { _, _, unlock in + unlock() + return false + }, onCancel: { _, _ in false }, onError: { _, _ in false } ) XCTAssertTrue(boolResult) - + // Test with Int return type let intResult: Int = LockmanManager.lock( action: action, boundaryId: boundaryId, unlockOption: nil, - onSuccess: { _, unlock in unlock(); return 123 }, - onSuccessWithPrecedingCancellation: { _, _, unlock in unlock(); return 0 }, + onSuccess: { _, unlock in + unlock() + return 123 + }, + onSuccessWithPrecedingCancellation: { _, _, unlock in + unlock() + return 0 + }, onCancel: { _, _ in 0 }, onError: { _, _ in 0 } ) XCTAssertEqual(intResult, 123) - + // Test with custom struct return type struct TestResult { let value: String let count: Int } - + let customResult: TestResult = LockmanManager.lock( action: action, boundaryId: boundaryId, @@ -241,24 +254,24 @@ final class LockmanManagerGenericLockTests: XCTestCase { onCancel: { _, _ in TestResult(value: "cancel", count: -1) }, onError: { _, _ in TestResult(value: "error", count: -2) } ) - + XCTAssertEqual(customResult.value, "test") XCTAssertEqual(customResult.count, 42) } } - + // MARK: - Unlock Token Behavior Tests - + func testGenericLock_UnlockToken_IdempotentCalls() async throws { let container = LockmanStrategyContainer() let strategy = TestSingleExecutionStrategy() try container.register(strategy) - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.test let boundaryId = TestBoundaryId.test var unlockCallCount = 0 - + let result: String = LockmanManager.lock( action: action, boundaryId: boundaryId, @@ -277,27 +290,27 @@ final class LockmanManagerGenericLockTests: XCTestCase { onCancel: { _, _ in "failure" }, onError: { _, _ in "failure" } ) - + XCTAssertEqual(result, "success") - XCTAssertEqual(unlockCallCount, 3) // All calls should be recorded, even if unlock is idempotent internally + XCTAssertEqual(unlockCallCount, 3) // All calls should be recorded, even if unlock is idempotent internally } } - + // MARK: - Action Default UnlockOption Tests - + func testGenericLock_UsesActionDefaultUnlockOption_WhenNilProvided() async throws { let container = LockmanStrategyContainer() let strategy = TestSingleExecutionStrategy() try container.register(strategy) - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.test let boundaryId = TestBoundaryId.test - + let result: String = LockmanManager.lock( action: action, boundaryId: boundaryId, - unlockOption: nil, // Should use action's default + unlockOption: nil, // Should use action's default onSuccess: { receivedAction, unlock in // The action's unlockOption should be used XCTAssertEqual(receivedAction.unlockOption, .immediate) @@ -308,24 +321,24 @@ final class LockmanManagerGenericLockTests: XCTestCase { onCancel: { _, _ in "failure" }, onError: { _, _ in "failure" } ) - + XCTAssertEqual(result, "success") } } - + func testGenericLock_OverridesActionDefaultUnlockOption_WhenExplicitProvided() async throws { let container = LockmanStrategyContainer() let strategy = TestSingleExecutionStrategy() try container.register(strategy) - + await LockmanManager.withTestContainer(container) { let action = SharedTestAction.test let boundaryId = TestBoundaryId.test - + let result: String = LockmanManager.lock( action: action, boundaryId: boundaryId, - unlockOption: .delayed(0.1), // Should override action's default + unlockOption: .delayed(0.1), // Should override action's default onSuccess: { receivedAction, unlock in // The original action's unlockOption should be preserved XCTAssertEqual(receivedAction.unlockOption, .immediate) @@ -336,8 +349,8 @@ final class LockmanManagerGenericLockTests: XCTestCase { onCancel: { _, _ in "failure" }, onError: { _, _ in "failure" } ) - + XCTAssertEqual(result, "success") } } -} \ No newline at end of file +} diff --git a/Tests/LockmanTests/Unit/Core/LockmanResultTests.swift b/Tests/LockmanTests/Unit/Core/LockmanResultTests.swift index 670e77f29..8e41617e4 100644 --- a/Tests/LockmanTests/Unit/Core/LockmanResultTests.swift +++ b/Tests/LockmanTests/Unit/Core/LockmanResultTests.swift @@ -53,7 +53,7 @@ final class LockmanResultTests: XCTestCase { // Test boundary ID type alias for easier usage private typealias TestBoundary = TestBoundaryId - + // Helper method to create unlock token for testing private func createTestUnlockToken( boundaryId: TestBoundary = TestBoundary.test, @@ -83,7 +83,7 @@ final class LockmanResultTests: XCTestCase { switch result { case .success(let token): XCTAssertNotNil(token) - // Verify unlock token exists (info properties are internal) + // Verify unlock token exists (info properties are internal) default: XCTFail("Should match .success case") } @@ -91,7 +91,8 @@ final class LockmanResultTests: XCTestCase { func testLockmanResultSuccessWithPrecedingCancellationCase() { // Test .successWithPrecedingCancellation case with unlockToken - let testInfo = TestLockmanInfo(actionId: "testAction", strategyId: "TestSingleExecutionStrategy") + let testInfo = TestLockmanInfo( + actionId: "testAction", strategyId: "TestSingleExecutionStrategy") let error = TestPrecedingError(message: "Previous operation cancelled", cancelledInfo: testInfo) let unlockToken = createTestUnlockToken(info: testInfo) let result = LockmanResult.successWithPrecedingCancellation( @@ -104,7 +105,7 @@ final class LockmanResultTests: XCTestCase { case .successWithPrecedingCancellation(let token, let precedingError): // Test unlock token XCTAssertNotNil(token) - + // Test error XCTAssertEqual(precedingError.errorDescription, "Previous operation cancelled") XCTAssertTrue(precedingError is TestPrecedingError) @@ -135,7 +136,8 @@ final class LockmanResultTests: XCTestCase { func testLockmanResultSendableConformance() async { // Test Sendable conformance with concurrent access - let testInfo = TestLockmanInfo(actionId: "concurrentTest", strategyId: "TestSingleExecutionStrategy") + let testInfo = TestLockmanInfo( + actionId: "concurrentTest", strategyId: "TestSingleExecutionStrategy") let precedingError = TestPrecedingError( message: "Concurrent test error", cancelledInfo: testInfo) let unlockToken = createTestUnlockToken(info: testInfo) @@ -198,7 +200,8 @@ final class LockmanResultTests: XCTestCase { func testLockmanResultWithPrecedingCancellationErrorProtocol() { // Test with LockmanPrecedingCancellationError protocol - let testInfo = TestLockmanInfo(actionId: "protocolTest", strategyId: "TestSingleExecutionStrategy") + let testInfo = TestLockmanInfo( + actionId: "protocolTest", strategyId: "TestSingleExecutionStrategy") let precedingError = TestPrecedingError( message: "Preceding error test", cancelledInfo: testInfo) let unlockToken = createTestUnlockToken(info: testInfo) @@ -211,7 +214,7 @@ final class LockmanResultTests: XCTestCase { case .successWithPrecedingCancellation(let token, let error): // Test unlock token XCTAssertNotNil(token) - + // Test LockmanPrecedingCancellationError protocol conformance XCTAssertTrue(error is any LockmanPrecedingCancellationError) XCTAssertTrue(error is any LockmanError) // Should also conform to base protocol @@ -226,7 +229,8 @@ final class LockmanResultTests: XCTestCase { func testLockmanResultExhaustivePatternMatching() { // Test all cases in a single comprehensive function - let testInfo = TestLockmanInfo(actionId: "comprehensiveTest", strategyId: "TestSingleExecutionStrategy") + let testInfo = TestLockmanInfo( + actionId: "comprehensiveTest", strategyId: "TestSingleExecutionStrategy") let unlockToken = createTestUnlockToken(info: testInfo) let results: [LockmanResult] = [ @@ -261,7 +265,8 @@ final class LockmanResultTests: XCTestCase { func testLockmanResultNestedSwitchHandling() { // Test nested pattern matching scenarios - let testInfo = TestLockmanInfo(actionId: "nestedTest", strategyId: "TestSingleExecutionStrategy") + let testInfo = TestLockmanInfo( + actionId: "nestedTest", strategyId: "TestSingleExecutionStrategy") func processResult(_ result: LockmanResult) -> String { switch result { @@ -317,7 +322,8 @@ final class LockmanResultTests: XCTestCase { // Example 1: Success case (most common) let unlockToken = createTestUnlockToken(info: testInfo) - let successResult = LockmanResult.success(unlockToken: unlockToken) + let successResult = LockmanResult.success( + unlockToken: unlockToken) switch successResult { case .success(let token): // Operation can proceed immediately without any additional cleanup @@ -331,10 +337,11 @@ final class LockmanResultTests: XCTestCase { message: "Lower priority operation cancelled", cancelledInfo: testInfo ) - let precedingResult = LockmanResult.successWithPrecedingCancellation( - unlockToken: createTestUnlockToken(info: testInfo), - error: precedingError - ) + let precedingResult = LockmanResult + .successWithPrecedingCancellation( + unlockToken: createTestUnlockToken(info: testInfo), + error: precedingError + ) switch precedingResult { case .successWithPrecedingCancellation(let token, let error): @@ -388,7 +395,8 @@ final class LockmanResultTests: XCTestCase { var boundaryId: any LockmanBoundaryId { boundary } } - let testInfo = TestLockmanInfo(actionId: "realErrorTest", strategyId: "TestSingleExecutionStrategy") + let testInfo = TestLockmanInfo( + actionId: "realErrorTest", strategyId: "TestSingleExecutionStrategy") // Test with conflict error let conflictResult = LockmanResult.cancel( @@ -410,21 +418,22 @@ final class LockmanResultTests: XCTestCase { } // Test with priority error - let priorityResult = LockmanResult.successWithPrecedingCancellation( - unlockToken: createTestUnlockToken(info: testInfo), - error: PriorityError( - cancelledInfo: testInfo, - boundary: TestBoundaryId.test, - newPriority: 10, - oldPriority: 5 + let priorityResult = LockmanResult + .successWithPrecedingCancellation( + unlockToken: createTestUnlockToken(info: testInfo), + error: PriorityError( + cancelledInfo: testInfo, + boundary: TestBoundaryId.test, + newPriority: 10, + oldPriority: 5 + ) ) - ) switch priorityResult { case .successWithPrecedingCancellation(let token, let error): // Test unlock token XCTAssertNotNil(token) - + // Test priority error if let priorityError = error as? PriorityError { XCTAssertEqual(priorityError.newPriority, 10)