Skip to content
Merged
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 @@ -76,7 +76,9 @@ class ContactPaymentSettingsRepo @Inject constructor(

private suspend fun enable(contacts: List<String>): Result<Unit> {
val previous = settingsStore.data.first()
val canUsePrivateContactPayments = privatePaykitRepo.hasPrivatePaymentAccess()
val canUsePrivateContactPayments = runSuspendCatching {
privatePaykitRepo.hasPrivatePaymentAccess()
}.getOrElse { return Result.failure(it) }
return runSuspendCatching {
settingsStore.update {
it.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class PrivatePaykitRepo @Inject constructor(
suspend fun reconcileReservedReceiveIndexes(): Result<Unit> =
addressReservationRepo.reconcileReservedIndexesWithLdk()

suspend fun hasPrivatePaymentAccess(): Boolean = hasPrivatePaymentAccessForCurrentProfile()
suspend fun hasPrivatePaymentAccess(): Boolean = paykitSdkService.hasPrivatePaymentAccess()

suspend fun prepareSavedContacts(
publicKeys: Collection<String>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.whenever
import to.bitkit.data.SettingsData
import to.bitkit.data.SettingsStore
Expand Down Expand Up @@ -108,6 +109,62 @@ class ContactPaymentSettingsRepoTest : BaseUnitTest() {
verify(pubkyRepo, never()).hasSecretKey()
}

@Test
fun `failed access check preserves settings and allows retry`() = test {
val previous = SettingsData(
sharesPrivatePaykitEndpoints = true,
publicPaykitLightningEnabled = false,
publicPaykitOnchainEnabled = false,
)
settingsFlow.value = previous
val failure = ContactPaymentSettingsTestError("Paykit unavailable")
var accessAvailable = false
whenever(privatePaykitRepo.hasPrivatePaymentAccess()).thenAnswer {
if (!accessAvailable) throw failure
true
}
val sut = createSut()

assertSame(failure, sut.setEnabled(true).exceptionOrNull())
assertEquals(previous, settingsFlow.value)
verify(settingsStore, never()).update(any())
verifyNoInteractions(publicPaykitRepo)
verify(privatePaykitRepo, never()).enableSharingAndPrepareSavedContacts(any<Collection<String>>())
verify(privatePaykitRepo, never()).disableSharingAndPruneUnsavedContactState(any<Collection<String>>())

accessAvailable = true

assertTrue(sut.setEnabled(true).isSuccess)
assertTrue(settingsFlow.value.sharesPrivatePaykitEndpoints)
verify(publicPaykitRepo).syncPublishedEndpoints(publish = true)
verify(privatePaykitRepo).enableSharingAndPrepareSavedContacts(listOf(CONTACT_KEY))
}

@Test
fun `cancelling access check leaves settings unchanged and releases sharing lock`() = test {
var waitForAccess = true
whenever(privatePaykitRepo.hasPrivatePaymentAccess()).doSuspendableAnswer {
if (waitForAccess) awaitCancellation()
true
}
val previous = settingsFlow.value
val sut = createSut()
val enable = async { sut.setEnabled(true) }
runCurrent()
verify(privatePaykitRepo).hasPrivatePaymentAccess()

enable.cancelAndJoin()

assertEquals(previous, settingsFlow.value)
verify(settingsStore, never()).update(any())
verifyNoInteractions(publicPaykitRepo)
verify(privatePaykitRepo, never()).enableSharingAndPrepareSavedContacts(any<Collection<String>>())
verify(privatePaykitRepo, never()).disableSharingAndPruneUnsavedContactState(any<Collection<String>>())
waitForAccess = false

assertTrue(sut.setEnabled(true).isSuccess)
}

@Test
fun `failed publication restores disabled settings`() = test {
whenever { publicPaykitRepo.syncPublishedEndpoints(publish = true) }
Expand Down
18 changes: 10 additions & 8 deletions app/src/test/java/to/bitkit/repositories/PrivatePaykitRepoTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -260,17 +260,19 @@ class PrivatePaykitRepoTest : BaseUnitTest(StandardTestDispatcher()) {
}

@Test
fun `hasPrivatePaymentAccess returns false when the SDK check fails`() = test {
var accessFails = true
whenever(paykitSdkService.hasPrivatePaymentAccess()).thenAnswer {
check(!accessFails) { "Paykit unavailable" }
true
fun `hasPrivatePaymentAccess propagates SDK errors and cancellation`() = test {
val failures = listOf(AppError("Paykit unavailable"), CancellationException("Cancelled"))
var failure: Throwable = failures.first()
whenever(paykitSdkService.hasPrivatePaymentAccess()).thenAnswer { throw failure }
for (nextFailure in failures) {
failure = nextFailure
assertEquals(failure, assertFailsWith<Throwable> { sut.hasPrivatePaymentAccess() })
}
}

assertFalse(sut.hasPrivatePaymentAccess())

@Test
fun `prepareSavedContacts propagates identity errors and cancellation`() = test {
settingsData.value = SettingsData(sharesPrivatePaykitEndpoints = true, publicPaykitLightningEnabled = false)
accessFails = false
val failures = listOf(AppError("Identity unavailable"), CancellationException("Cancelled"))
var failure: Throwable = failures.first()
whenever(paykitSdkService.identityStatus()).thenAnswer { throw failure }
Expand Down
1 change: 1 addition & 0 deletions changelog.d/next/1444.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Contact payment setup now reports temporary access failures instead of silently disabling private payments.
Loading