Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Bitkit/Services/PaykitPaymentRequestService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1755,7 +1755,9 @@ final class PaykitPaymentRequestManager {
let actionGeneration = stateGeneration
let savedPublicKeysSnapshot = savedPublicKeys
isCreatingRequest = true
let review = paymentActivity.beginReview()
defer {
paymentActivity.endReview(review)
if actionGeneration == stateGeneration {
isCreatingRequest = false
}
Expand Down
85 changes: 79 additions & 6 deletions BitkitTests/PaykitPaymentRequestServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9285,23 +9285,39 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
role: .payee,
expiresAt: timestamp(expiresAt)
))
let manager = paymentRequestManager(sdk: sdk)
let activity = PaykitPaymentActivity()
let manager = paymentRequestManager(sdk: sdk, paymentActivity: activity)
await manager.refreshEligibleTargets(savedPublicKeys: [publicKey])
let target = try XCTUnwrap(manager.eligibleTargets.first)

let wasPaused = await PrivatePaykitService.shared.isBackgroundWorkPaused
addTeardownBlock { await PrivatePaykitService.shared.setBackgroundWorkPaused(wasPaused) }
await sdk.pauseNextLinkedPeers()
await sdk.pauseNextProposal()
await sdk.pauseNextProcess()
let proposal = Task {
try await manager.propose(
PaykitPaymentRequestDraft(amount: PaykitAmount(asset: .btc, atomic: 1), note: "Coffee", expiresAt: expiresAt),
to: target
)
}
try await waitUntil { await sdk.linkedPeersIsPaused() }
XCTAssertTrue(activity.shouldDeferRefresh)
XCTAssertFalse(activity.isActive)
await sdk.resumeLinkedPeers()
try await waitUntil { await sdk.proposalIsPaused() }
XCTAssertTrue(activity.shouldDeferRefresh)
await PrivatePaykitService.shared.setBackgroundWorkPaused(true)
await sdk.resumeProposal()
try await waitUntil { await sdk.processIsPaused() }
XCTAssertTrue(activity.shouldDeferRefresh)
let otherReview = activity.beginReview()
await sdk.resumeProcess()
let request = try await proposal.value
XCTAssertTrue(activity.shouldDeferRefresh)
activity.endReview(otherReview)
XCTAssertFalse(activity.shouldDeferRefresh)
XCTAssertFalse(manager.isCreatingRequest)

XCTAssertEqual(request.amount.atomic, 1)
XCTAssertEqual(request.note, "Coffee")
Expand All @@ -9326,6 +9342,47 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
XCTAssertEqual(priorities["delivery"], [.interactive])
}

func testCancelledProposalReleasesReviewAndAllowsAnotherCreation() async throws {
let publicKey = "pubky\(String(repeating: "y", count: 52))"
let expiresAt = Date(timeIntervalSince1970: 1_900_000_000)
let sdk = PaymentRequestSdkMock(records: [])
await sdk.configureRecipients(
peers: [linkedPeer(counterparty: publicKey, state: .linked)],
requestCapabilitiesByPublicKey: [publicKey: true]
)
try await sdk.setProposalResult(paymentRequestRecord(
id: "outgoing", counterparty: publicKey, role: .payee, expiresAt: timestamp(expiresAt)
))
let activity = PaykitPaymentActivity()
let manager = paymentRequestManager(sdk: sdk, paymentActivity: activity)
await manager.refreshEligibleTargets(savedPublicKeys: [publicKey])
let target = try XCTUnwrap(manager.eligibleTargets.first)
let draft = PaykitPaymentRequestDraft(amount: PaykitAmount(asset: .btc, atomic: 1), note: "", expiresAt: expiresAt)
await sdk.pauseNextLinkedPeers()
let proposal = Task { try await manager.propose(draft, to: target) }
try await waitUntil { await sdk.linkedPeersIsPaused() }
XCTAssertTrue(activity.shouldDeferRefresh)

proposal.cancel()
await sdk.resumeLinkedPeers()
do {
_ = try await proposal.value
XCTFail("Expected cancelled recipient discovery")
} catch is CancellationError {}
XCTAssertFalse(activity.shouldDeferRefresh)
XCTAssertFalse(manager.isCreatingRequest)
let cancelledSnapshot = await sdk.snapshot()
XCTAssertTrue(cancelledSnapshot.proposedRequests.isEmpty)
XCTAssertTrue(cancelledSnapshot.processedCounterparties.isEmpty)

let request = try await manager.propose(draft, to: target)
XCTAssertEqual(request.paymentRequestId, "outgoing")
XCTAssertFalse(activity.shouldDeferRefresh)
let snapshot = await sdk.snapshot()
XCTAssertEqual(snapshot.proposedRequests.count, 1)
XCTAssertEqual(snapshot.processedCounterparties, [publicKey])
}

func testProposalsOnlyInspectAndDrainSelectedSavedTarget() async throws {
let publicKey = "pubky\(String(repeating: "y", count: 52))"
let unrelatedKey = "pubky\(String(repeating: "a", count: 52))"
Expand Down Expand Up @@ -9491,14 +9548,17 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
id: "outgoing", counterparty: publicKey, role: .payee, proposalOutboundMessageId: 7
))
await sdk.setPaymentRequestListError(error)
let manager = paymentRequestManager(sdk: sdk)
let activity = PaykitPaymentActivity()
let manager = paymentRequestManager(sdk: sdk, paymentActivity: activity)
await manager.refreshEligibleTargets(savedPublicKeys: [publicKey])

let request = try await manager.propose(
PaykitPaymentRequestDraft(amount: PaykitAmount(asset: .btc, atomic: 1), note: "", expiresAt: expiresAt),
to: XCTUnwrap(manager.eligibleTargets.first)
)

XCTAssertFalse(activity.shouldDeferRefresh)
XCTAssertFalse(manager.isCreatingRequest)
XCTAssertEqual(request.deliveryStatus, .queued)
XCTAssertEqual(manager.outgoingRequests.map(\.paymentRequestId), ["outgoing"])
let snapshot = await sdk.snapshot()
Expand Down Expand Up @@ -9615,14 +9675,17 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
} else {
await sdk.failNextProcess()
}
let manager = paymentRequestManager(sdk: sdk)
let activity = PaykitPaymentActivity()
let manager = paymentRequestManager(sdk: sdk, paymentActivity: activity)
await manager.refreshEligibleTargets(savedPublicKeys: [publicKey])

let request = try await manager.propose(
PaykitPaymentRequestDraft(amount: PaykitAmount(asset: .btc, atomic: 1), note: "", expiresAt: expiresAt),
to: XCTUnwrap(manager.eligibleTargets.first)
)

XCTAssertFalse(activity.shouldDeferRefresh)
XCTAssertFalse(manager.isCreatingRequest)
XCTAssertEqual(request.deliveryStatus, .queued)
XCTAssertEqual(manager.outgoingRequests.map(\.paymentRequestId), ["outgoing"])
let snapshot = await sdk.snapshot()
Expand All @@ -9647,7 +9710,8 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
role: .payee,
expiresAt: timestamp(expiresAt)
))
let manager = paymentRequestManager(sdk: sdk)
let activity = PaykitPaymentActivity()
let manager = paymentRequestManager(sdk: sdk, paymentActivity: activity)
await manager.refreshEligibleTargets(savedPublicKeys: [publicKey])
let target = try XCTUnwrap(manager.eligibleTargets.first)

Expand All @@ -9662,6 +9726,8 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
} catch let error as PaykitPaymentRequestError {
XCTAssertEqual(error, .requestUnavailable)
}
XCTAssertFalse(activity.shouldDeferRefresh)
XCTAssertFalse(manager.isCreatingRequest)
let snapshot = await sdk.snapshot()
XCTAssertTrue(snapshot.proposedRequests.isEmpty)
}
Expand Down Expand Up @@ -9706,7 +9772,8 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
expiresAt: timestamp(expiresAt)
))
await sdk.pauseNextProposal()
let manager = paymentRequestManager(sdk: sdk)
let activity = PaykitPaymentActivity()
let manager = paymentRequestManager(sdk: sdk, paymentActivity: activity)
await manager.refreshEligibleTargets(savedPublicKeys: [publicKey])
let target = try XCTUnwrap(manager.eligibleTargets.first)

Expand All @@ -9717,10 +9784,12 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
)
}
try await waitUntil { await sdk.proposalIsPaused() }
XCTAssertTrue(activity.shouldDeferRefresh)
manager.clear()
await sdk.resumeProposal()

let committedRequest = try await task.value
XCTAssertFalse(activity.shouldDeferRefresh)
XCTAssertEqual(committedRequest.paymentRequestId, "outgoing")
XCTAssertTrue(manager.outgoingRequests.isEmpty)
XCTAssertTrue(manager.eligibleTargets.isEmpty)
Expand All @@ -9741,7 +9810,8 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
expiresAt: timestamp(expiresAt)
))
await sdk.pauseNextProposal()
let manager = paymentRequestManager(sdk: sdk)
let activity = PaykitPaymentActivity()
let manager = paymentRequestManager(sdk: sdk, paymentActivity: activity)
await manager.refreshEligibleTargets(savedPublicKeys: [publicKey])
let target = try XCTUnwrap(manager.eligibleTargets.first)

Expand All @@ -9752,6 +9822,7 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
)
}
try await waitUntil { await sdk.proposalIsPaused() }
XCTAssertTrue(activity.shouldDeferRefresh)
await sdk.setActiveIdentity("pubky\(String(repeating: "a", count: 52))")
await sdk.resumeProposal()

Expand All @@ -9761,6 +9832,8 @@ final class PaykitPaymentRequestServiceTests: XCTestCase {
} catch let error as PaykitPaymentRequestError {
XCTAssertEqual(error, .requestUnavailable)
}
XCTAssertFalse(activity.shouldDeferRefresh)
XCTAssertFalse(manager.isCreatingRequest)
let snapshot = await sdk.snapshot()
XCTAssertTrue(snapshot.proposedRequests.isEmpty)
XCTAssertTrue(manager.outgoingRequests.isEmpty)
Expand Down
Loading