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
Original file line number Diff line number Diff line change
Expand Up @@ -574,10 +574,16 @@ class PaykitPaymentRequestRepo @Inject constructor(
mode: PaykitPaymentRequestRefreshMode = PaykitPaymentRequestRefreshMode.FULL,
): Result<Unit> = refresh(mode, forceFresh = true)

internal suspend fun refreshAfterStateChange(
mode: PaykitPaymentRequestRefreshMode,
priority: Priority,
): Result<Unit> = refresh(mode, forceFresh = true, priority = priority)

private suspend fun refresh(
mode: PaykitPaymentRequestRefreshMode,
forceFresh: Boolean,
messagePriority: Priority = Priority.Background,
priority: Priority = if (forceFresh) Priority.Ordered else Priority.Background,
): Result<Unit> {
val generation = stateGeneration.get()
val expectedIdentity = activeIdentity
Expand All @@ -599,13 +605,12 @@ class PaykitPaymentRequestRepo @Inject constructor(
completedRefreshGeneration = -1L
RefreshCheckpoint(actionVersion, proofVersion)
} ?: return@refresh
val priority = if (forceFresh) Priority.Ordered else Priority.Background
val snapshot = runSuspendCatching {
fetchRequestSnapshot(
expectedIdentity,
mode,
priority,
if (forceFresh) Priority.Ordered else messagePriority,
if (forceFresh) priority else messagePriority,
observationOnly = true,
)
}
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,7 @@ class AppViewModel @Inject constructor(
mode: PaykitPaymentRequestRefreshMode = PaykitPaymentRequestRefreshMode.FULL,
forceFresh: Boolean = false,
messagePriority: Priority = Priority.Background,
refreshPriority: Priority? = null,
onlyWhileActive: Boolean = false,
reconcileProofs: Boolean = mode == PaykitPaymentRequestRefreshMode.FULL,
): Result<Unit> {
Expand Down Expand Up @@ -1186,7 +1187,9 @@ class AppViewModel @Inject constructor(
// held from presentation until the allowances are refreshed too.
val releaseCoverage = paykitAllowanceRepo.beginCoverageUpdate()
val result = try {
val refreshed = if (forceFresh) {
val refreshed = if (forceFresh && refreshPriority != null) {
paykitPaymentRequestRepo.refreshAfterStateChange(mode, refreshPriority)
} else if (forceFresh) {
paykitPaymentRequestRepo.refreshAfterStateChange(mode)
} else {
paykitPaymentRequestRepo.refresh(mode, messagePriority)
Expand Down Expand Up @@ -1540,6 +1543,7 @@ class AppViewModel @Inject constructor(
refreshIncomingPaykitPaymentRequests(
PaykitPaymentRequestRefreshMode.STORED,
forceFresh = true,
refreshPriority = Priority.Background,
onlyWhileActive = true,
reconcileProofs = true,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,33 +408,42 @@ class PaykitPaymentRequestRepoTest : BaseUnitTest(StandardTestDispatcher()) {

@Test
fun `warm request observations remain readable while refresh waits and clear on identity change`() = test {
val record = paymentRequestRecord()
whenever(paykitSdkService.allPaymentRequests(anyOrNull())).thenReturn(listOf(record))
sut.refresh(PaykitPaymentRequestRefreshMode.STORED).getOrThrow()
val pending = sut.pendingRequests.value
val history = sut.paymentRequestHistory.value
assertTrue(pending.isNotEmpty())
val reading = CompletableDeferred<Unit>()
val resume = CompletableDeferred<Unit>()
whenever(paykitSdkService.allPaymentRequests(anyOrNull())).doSuspendableAnswer {
reading.complete(Unit)
resume.await()
listOf(record)
for (forceFresh in listOf(false, true)) {
sut.activate(LOCAL_IDENTITY)
val record = paymentRequestRecord()
whenever(paykitSdkService.allPaymentRequests(anyOrNull())).thenReturn(listOf(record))
sut.refresh(PaykitPaymentRequestRefreshMode.STORED).getOrThrow()
val pending = sut.pendingRequests.value
val history = sut.paymentRequestHistory.value
assertTrue(pending.isNotEmpty())
val reading = CompletableDeferred<Unit>()
val resume = CompletableDeferred<Unit>()
whenever(paykitSdkService.allPaymentRequests(anyOrNull())).doSuspendableAnswer {
reading.complete(Unit)
resume.await()
listOf(record)
}
val refresh = async {
if (forceFresh) {
sut.refreshAfterStateChange(PaykitPaymentRequestRefreshMode.STORED, Priority.Background)
} else {
sut.refresh(PaykitPaymentRequestRefreshMode.STORED)
}
}
reading.await()
val started = testScheduler.currentTime
assertEquals(pending, sut.pendingRequests.value)
assertEquals(history, sut.paymentRequestHistory.value)
assertEquals(0L, testScheduler.currentTime - started)
assertFalse(refresh.isCompleted)
sut.activate(SECOND_IDENTITY)
assertTrue(sut.pendingRequests.value.isEmpty())
assertTrue(sut.paymentRequestHistory.value.isEmpty())
resume.complete(Unit)
refresh.await().getOrThrow()
assertTrue(sut.pendingRequests.value.isEmpty())
assertTrue(sut.paymentRequestHistory.value.isEmpty())
}
val refresh = async { sut.refresh(PaykitPaymentRequestRefreshMode.STORED) }
reading.await()
val started = testScheduler.currentTime
assertEquals(pending, sut.pendingRequests.value)
assertEquals(history, sut.paymentRequestHistory.value)
assertEquals(0L, testScheduler.currentTime - started)
assertFalse(refresh.isCompleted)
sut.activate(SECOND_IDENTITY)
assertTrue(sut.pendingRequests.value.isEmpty())
assertTrue(sut.paymentRequestHistory.value.isEmpty())
resume.complete(Unit)
refresh.await().getOrThrow()
assertTrue(sut.pendingRequests.value.isEmpty())
assertTrue(sut.paymentRequestHistory.value.isEmpty())
}

@Test
Expand Down Expand Up @@ -517,7 +526,7 @@ class PaykitPaymentRequestRepoTest : BaseUnitTest(StandardTestDispatcher()) {

@Test
fun `checkpoint reload preserves refresh admission and filters local proofs on retry`() = test {
for (forceFresh in listOf(false, true)) {
for (priority in listOf(null, Priority.Ordered, Priority.Background)) {
val record = paymentRequestRecord()
val other = paymentRequestRecord(id = "another-request")
val records = listOf(record, other)
Expand Down Expand Up @@ -545,8 +554,8 @@ class PaykitPaymentRequestRepoTest : BaseUnitTest(StandardTestDispatcher()) {
}.whenever(paykitSdkService).allPaymentRequests(anyOrNull(), any())
clearInvocations(paykitSdkService)
val refresh = async {
if (forceFresh) {
sut.refreshAfterStateChange(PaykitPaymentRequestRefreshMode.STORED)
if (priority != null) {
sut.refreshAfterStateChange(PaykitPaymentRequestRefreshMode.STORED, priority)
} else {
sut.refresh(PaykitPaymentRequestRefreshMode.STORED)
}
Expand All @@ -558,7 +567,7 @@ class PaykitPaymentRequestRepoTest : BaseUnitTest(StandardTestDispatcher()) {
resume.complete(Unit)

val result = refresh.await()
if (forceFresh) {
if (priority == Priority.Ordered) {
result.getOrThrow()
assertEquals(listOf("another-request"), sut.pendingRequests.value.map { it.paymentRequestId })
} else {
Expand All @@ -567,12 +576,19 @@ class PaykitPaymentRequestRepoTest : BaseUnitTest(StandardTestDispatcher()) {
assertEquals(warmHistory, sut.paymentRequestHistory.value)
verify(paykitSdkService, never()).linkedPeers(Priority.Background)
}
val priority = if (forceFresh) Priority.Ordered else Priority.Background
verify(paykitSdkService).allPaymentRequests(LOCAL_IDENTITY, priority)
verify(paykitSdkService).allPaymentRequests(LOCAL_IDENTITY, priority ?: Priority.Background)

paymentSubmissionActive.value = false
val latest = paymentRequestRecord(id = "new-request")
whenever(paykitSdkService.allPaymentRequests(anyOrNull())).thenReturn(records + latest)
proofStateVersion.value += 1
clearInvocations(paykitSdkService)
sut.refresh(PaykitPaymentRequestRefreshMode.STORED).getOrThrow()
assertEquals(listOf("another-request"), sut.pendingRequests.value.map { it.paymentRequestId })
verify(paykitSdkService).observeState(LOCAL_IDENTITY, Priority.Background)
assertEquals(
listOf("another-request", "new-request"),
sut.pendingRequests.value.map { it.paymentRequestId },
)
assertTrue(sut.paymentRequestHistory.value.any { it.id == requestId })
}
}
Expand Down
79 changes: 70 additions & 9 deletions app/src/test/java/to/bitkit/viewmodels/AppViewModelSendFlowTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ class AppViewModelSendFlowTest : BaseUnitTest() {
whenever(paykitPaymentRequestRepo.isCreatingRequest).thenReturn(MutableStateFlow(false))
whenever { paykitPaymentRequestRepo.refreshEligibleTargets(any(), any()) }.thenReturn(Result.success(Unit))
whenever { paykitPaymentRequestRepo.refreshAfterStateChange(any()) }.thenReturn(Result.success(Unit))
whenever { paykitPaymentRequestRepo.refreshAfterStateChange(any(), any()) }.thenReturn(Result.success(Unit))
whenever { paykitPaymentRequestRepo.isSubscriptionNotificationHandled(any(), any()) }.thenReturn(false)
whenever { paykitPaymentRequestRepo.refresh(any(), any()) }.doSuspendableAnswer {
paykitPaymentRequestRepo.refresh(it.getArgument(0))
Expand Down Expand Up @@ -11133,10 +11134,13 @@ class AppViewModelSendFlowTest : BaseUnitTest() {
usdtStateVersion.value++
runCurrent()
}
verify(paykitPaymentRequestRepo, never()).refreshAfterStateChange(any())
verify(paykitPaymentRequestRepo, never()).refreshAfterStateChange(any(), any())
paymentSubmissionActive.value = false
runCurrent()
verify(paykitPaymentRequestRepo).refreshAfterStateChange(PaykitPaymentRequestRefreshMode.STORED)
verify(paykitPaymentRequestRepo).refreshAfterStateChange(
PaykitPaymentRequestRefreshMode.STORED,
Priority.Background,
)
}

@Test
Expand All @@ -11150,18 +11154,21 @@ class AppViewModelSendFlowTest : BaseUnitTest() {
proofStateVersion.value++
runCurrent()
}
verify(paykitPaymentRequestRepo, never()).refreshAfterStateChange(any())
verify(paykitPaymentRequestRepo, never()).refreshAfterStateChange(any(), any())
verify(paykitPaymentProofRepo, never()).reconcile()

paymentSubmissionActive.value = false
runCurrent()
verify(paykitPaymentRequestRepo).refreshAfterStateChange(PaykitPaymentRequestRefreshMode.STORED)
verify(paykitPaymentRequestRepo).refreshAfterStateChange(
PaykitPaymentRequestRefreshMode.STORED,
Priority.Background,
)
verify(paykitPaymentProofRepo).reconcile()
paymentSubmissionActive.value = true
runCurrent()
paymentSubmissionActive.value = false
runCurrent()
verify(paykitPaymentRequestRepo, times(1)).refreshAfterStateChange(any())
verify(paykitPaymentRequestRepo, times(1)).refreshAfterStateChange(any(), any())
verify(paykitPaymentProofRepo, times(1)).reconcile()
}

Expand All @@ -11176,19 +11183,70 @@ class AppViewModelSendFlowTest : BaseUnitTest() {
proofStateVersion.value++
runCurrent()
}
verify(paykitPaymentRequestRepo, never()).refreshAfterStateChange(any())
verify(paykitPaymentRequestRepo, never()).refreshAfterStateChange(any(), any())

sut.startPaykitPaymentRequestPolling()
runCurrent()
verify(paykitPaymentRequestRepo).refreshAfterStateChange(PaykitPaymentRequestRefreshMode.STORED)
verify(paykitPaymentRequestRepo).refreshAfterStateChange(
PaykitPaymentRequestRefreshMode.STORED,
Priority.Background,
)
sut.stopPaykitPaymentRequestPolling()
runCurrent()
sut.startPaykitPaymentRequestPolling()
runCurrent()
verify(paykitPaymentRequestRepo, times(1)).refreshAfterStateChange(any())
verify(paykitPaymentRequestRepo, times(1)).refreshAfterStateChange(any(), any())
sut.stopPaykitPaymentRequestPolling()
}

@Test
fun `proof display refresh defers after admitted reconciliation and recovers on home resume`() = test {
whenever(paykitPaymentRequestRepo.refresh(any())).thenReturn(Result.success(Unit))
pubkyPublicKey.value = testPublicKey
enablePaykitUi()
runCurrent()
val reconciliation = CompletableDeferred<Unit>()
var reconciled = false
whenever(paykitPaymentProofRepo.reconcile()).doSuspendableAnswer {
reconciliation.await()
reconciled = true
}
whenever(
paykitPaymentRequestRepo.refreshAfterStateChange(
PaykitPaymentRequestRefreshMode.STORED,
Priority.Background,
),
).doSuspendableAnswer {
assertTrue(reconciled)
assertTrue(paymentSubmissionActive.value)
Result.failure(PaykitPaymentRequestError.OperationInProgress)
}
clearInvocations(paykitPaymentRequestRepo, paykitPaymentProofRepo)

proofStateVersion.value++
runCurrent()
verify(paykitPaymentProofRepo).reconcile()
assertFalse(reconciled)
paymentSubmissionActive.value = true
reconciliation.complete(Unit)
runCurrent()

assertTrue(reconciled)
verify(paykitPaymentRequestRepo).refreshAfterStateChange(
PaykitPaymentRequestRefreshMode.STORED,
Priority.Background,
)
verify(paykitPaymentRequestRepo, never()).refreshAfterStateChange(any())
paymentSubmissionActive.value = false
runCurrent()
verify(paykitPaymentRequestRepo, times(1)).refreshAfterStateChange(any(), any())

sut.onHomeResumed()
runCurrent()
verify(paykitPaymentRequestRepo).refresh(PaykitPaymentRequestRefreshMode.FULL, Priority.Background)
verify(paykitPaymentProofRepo, times(2)).reconcile()
}

@Test
fun `paid recurring request refreshes subscription state immediately`() = test {
val address = "bcrt1qrecurringpaymentrequest"
Expand Down Expand Up @@ -11238,7 +11296,10 @@ class AppViewModelSendFlowTest : BaseUnitTest() {
confirmCurrentPayment()

completionStarted.await()
verify(paykitPaymentRequestRepo).refreshAfterStateChange(PaykitPaymentRequestRefreshMode.STORED)
verify(paykitPaymentRequestRepo).refreshAfterStateChange(
PaykitPaymentRequestRefreshMode.STORED,
Priority.Background,
)
verify(paykitPaymentProofRepo).reconcile()
verify(paykitPaymentRequestRepo, never()).refresh(PaykitPaymentRequestRefreshMode.FULL)
finishCompletion.complete(Unit)
Expand Down
1 change: 1 addition & 0 deletions changelog.d/next/1639.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Background payment-request updates now give priority to payment review and submission.
Loading