mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-15 07:28:30 +00:00
Fix BackupSubscriptionCheckJob tests.
This commit is contained in:
@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.testing.runSync
|
||||
import org.thoughtcrime.securesms.testing.success
|
||||
import org.whispersystems.signalservice.api.SignalServiceDataStore
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender
|
||||
import org.whispersystems.signalservice.api.account.AccountApi
|
||||
import org.whispersystems.signalservice.api.archive.ArchiveApi
|
||||
import org.whispersystems.signalservice.api.attachment.AttachmentApi
|
||||
import org.whispersystems.signalservice.api.donations.DonationsApi
|
||||
@@ -56,6 +57,7 @@ class InstrumentationApplicationDependencyProvider(val application: Application,
|
||||
private val recipientCache: LiveRecipientCache
|
||||
private var signalServiceMessageSender: SignalServiceMessageSender? = null
|
||||
private var billingApi: BillingApi = mockk()
|
||||
private var accountApi: AccountApi = mockk()
|
||||
|
||||
init {
|
||||
runSync {
|
||||
@@ -118,6 +120,8 @@ class InstrumentationApplicationDependencyProvider(val application: Application,
|
||||
|
||||
override fun provideBillingApi(): BillingApi = billingApi
|
||||
|
||||
override fun provideAccountApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket): AccountApi = accountApi
|
||||
|
||||
override fun provideSignalServiceNetworkAccess(): SignalServiceNetworkAccess {
|
||||
return serviceNetworkAccessMock
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import assertk.assertions.isTrue
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkAll
|
||||
import io.mockk.verify
|
||||
import okio.IOException
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@@ -30,6 +30,7 @@ import org.signal.donations.InAppPaymentType
|
||||
import org.thoughtcrime.securesms.backup.DeletionState
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.components.settings.app.backups.BackupStateObserver
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
@@ -38,6 +39,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.net.SignalNetwork
|
||||
import org.thoughtcrime.securesms.testing.SignalActivityRule
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
@@ -47,6 +49,7 @@ import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription.ChargeFailure
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration
|
||||
import org.whispersystems.signalservice.internal.push.WhoAmIResponse
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
import kotlin.time.Duration.Companion.days
|
||||
@@ -68,13 +71,21 @@ class BackupSubscriptionCheckJobTest {
|
||||
every { RemoteConfig.internalUser } returns true
|
||||
|
||||
coEvery { AppDependencies.billingApi.getApiAvailability() } returns BillingResponseCode.OK
|
||||
coEvery { AppDependencies.billingApi.queryPurchases() } returns mockk()
|
||||
coEvery { AppDependencies.billingApi.queryProduct() } returns null
|
||||
|
||||
coEvery { AppDependencies.billingApi.queryPurchases() } returns BillingPurchaseResult.Success(
|
||||
purchaseState = BillingPurchaseState.PURCHASED,
|
||||
purchaseToken = "test-token",
|
||||
isAcknowledged = true,
|
||||
isAutoRenewing = true,
|
||||
purchaseTime = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
coEvery { AppDependencies.billingApi.queryProduct() } returns BillingProduct(price = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")))
|
||||
|
||||
SignalStore.backup.backupTier = MessageBackupTier.PAID
|
||||
|
||||
mockkObject(RecurringInAppPaymentRepository)
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription()
|
||||
)
|
||||
|
||||
@@ -97,6 +108,18 @@ class BackupSubscriptionCheckJobTest {
|
||||
}
|
||||
}
|
||||
|
||||
every { BackupRepository.resetInitializedStateAndAuthCredentials() } returns Unit
|
||||
|
||||
mockkObject(BackupStateObserver)
|
||||
every { BackupStateObserver.notifyBackupStateChanged() } returns Unit
|
||||
|
||||
mockkObject(SignalNetwork)
|
||||
every { AppDependencies.accountApi.whoAmI() } returns NetworkResult.Success(
|
||||
WhoAmIResponse(
|
||||
number = "+1234567890"
|
||||
)
|
||||
)
|
||||
|
||||
every { AppDependencies.donationsApi.putSubscription(any()) } returns NetworkResult.Success(Unit)
|
||||
|
||||
insertSubscriber()
|
||||
@@ -173,9 +196,18 @@ class BackupSubscriptionCheckJobTest {
|
||||
assertEarlyExit(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenPrePendingRecurringTransaction_whenIRun_thenIExpectSuccessAndEarlyExit() {
|
||||
insertPrePendingInAppPayment()
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
val result = job.run()
|
||||
|
||||
assertEarlyExit(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenAPendingPayment_whenIRun_thenIExpectSuccessAndEarlyExit() {
|
||||
mockProduct()
|
||||
insertPendingInAppPayment()
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
@@ -187,9 +219,7 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenInactiveSubscription_whenIRun_thenIExpectStateMismatchDetected() {
|
||||
mockProduct()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = false)
|
||||
)
|
||||
|
||||
@@ -201,9 +231,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenRepositoryFailure_whenIRun_thenIExpectFailureResult() {
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.failure(
|
||||
RuntimeException("Network error")
|
||||
fun givenAnApplicationErrorWhenAccessingTheActiveSubscription_whenIRun_thenIExpectAFailure() {
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.ApplicationError(
|
||||
RuntimeException("Application Error")
|
||||
)
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
@@ -213,8 +243,10 @@ class BackupSubscriptionCheckJobTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenBillingApiReturnsAFailure_whenIRun_thenIExpectFailureResult() {
|
||||
coEvery { AppDependencies.billingApi.queryPurchases() } returns BillingPurchaseResult.BillingUnavailable
|
||||
fun givenANetworkErrorWhenAccessingTheActiveSubscription_whenIRun_thenIExpectAFailure() {
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.NetworkError(
|
||||
IOException()
|
||||
)
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
val result = job.run()
|
||||
@@ -223,10 +255,32 @@ class BackupSubscriptionCheckJobTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenPastDueSubscription_whenIRun_thenIExpectStateMismatchDetected() {
|
||||
mockProduct()
|
||||
fun givenAStatusCodeErrorWhenAccessingTheActiveSubscription_whenIRun_thenIExpectAMismatch() {
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.StatusCodeError(
|
||||
NonSuccessfulResponseCodeException(404)
|
||||
)
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
val result = job.run()
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assertThat(SignalStore.backup.subscriptionStateMismatchDetected).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenBillingApiReturnsAFailure_whenIRun_thenIExpectSuccessAndEarlyExit() {
|
||||
coEvery { AppDependencies.billingApi.queryPurchases() } returns BillingPurchaseResult.BillingUnavailable
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
val result = job.run()
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assertThat(SignalStore.backup.subscriptionStateMismatchDetected).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenPastDueSubscription_whenIRun_thenIExpectStateMismatchDetected() {
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(
|
||||
isActive = false,
|
||||
billingPeriodEndSeconds = System.currentTimeMillis().milliseconds.inWholeSeconds - 1.days.inWholeSeconds,
|
||||
@@ -243,9 +297,7 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenCancelledSubscription_whenIRun_thenIExpectStateMismatchDetected() {
|
||||
mockProduct()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(
|
||||
isActive = false,
|
||||
status = "canceled",
|
||||
@@ -262,9 +314,7 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenFreeBackupTier_whenIRun_thenIExpectSuccessAndEarlyExit() {
|
||||
mockProduct()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
ActiveSubscription.EMPTY
|
||||
)
|
||||
|
||||
@@ -277,25 +327,12 @@ class BackupSubscriptionCheckJobTest {
|
||||
assertThat(SignalStore.backup.subscriptionStateMismatchDetected).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenFailedInAppPayment_whenIRun_thenIExpectStateMismatchDetected() {
|
||||
mockProduct()
|
||||
insertFailedInAppPayment()
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
val result = job.run()
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assertThat(SignalStore.backup.subscriptionStateMismatchDetected).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenActiveSignalSubscriptionWithTokenMismatch_whenIRun_thenIExpectTokenRedemption() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
insertSubscriber("mismatch")
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true)
|
||||
)
|
||||
|
||||
@@ -315,10 +352,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenActiveSubscriptionAndPurchaseWithoutEntitlement_whenIRun_thenIExpectRedemption() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true)
|
||||
)
|
||||
|
||||
@@ -342,10 +378,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenValidActiveState_whenIRun_thenIExpectSuccessAndNoMismatch() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true)
|
||||
)
|
||||
|
||||
@@ -360,10 +395,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenValidInactiveState_whenIRun_thenIExpectSuccessAndNoMismatch() {
|
||||
mockProduct()
|
||||
mockInactivePurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = false)
|
||||
)
|
||||
|
||||
@@ -379,10 +413,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenGooglePlayBillingCanceledWithoutActiveSignalSubscription_whenIRun_thenIExpectValidCancelState() {
|
||||
mockProduct()
|
||||
mockCanceledPurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = false)
|
||||
)
|
||||
|
||||
@@ -395,10 +428,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenGooglePlayBillingCanceledWithFailedSignalSubscription_whenIRun_thenIExpectValidCancelState() {
|
||||
mockProduct()
|
||||
mockCanceledPurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true, status = "past_due", chargeFailure = ChargeFailure("test", "", "", "", ""))
|
||||
)
|
||||
|
||||
@@ -411,11 +443,10 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenInvalidStateConfiguration_whenIRun_thenIExpectStateMismatchDetected() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
|
||||
// Create invalid state: active purchase but no active subscription, with paid tier
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = false)
|
||||
)
|
||||
|
||||
@@ -430,10 +461,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenActiveSubscriptionWithMismatchedZkCredentials_whenIRun_thenIExpectCredentialRefresh() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true)
|
||||
)
|
||||
|
||||
@@ -452,10 +482,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenActiveSubscriptionWithSyncedZkCredentials_whenIRun_thenIExpectNoCredentialRefresh() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true)
|
||||
)
|
||||
|
||||
@@ -472,10 +501,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenActiveSubscriptionWithZkCredentialFailure_whenIRun_thenIExpectCredentialRefresh() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true)
|
||||
)
|
||||
|
||||
@@ -493,11 +521,10 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenSubscriptionWillCancelAtPeriodEnd_whenIRun_thenIExpectValidCancelState() {
|
||||
mockProduct()
|
||||
mockCanceledPurchase()
|
||||
|
||||
// Create subscription that will cancel at period end
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true, cancelled = true) // cancelled = true means willCancelAtPeriodEnd
|
||||
)
|
||||
|
||||
@@ -510,11 +537,10 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
@Test
|
||||
fun givenActiveSubscriptionNotWillCancelAtPeriodEnd_whenIRun_thenIExpectZkSynchronization() {
|
||||
mockProduct()
|
||||
mockActivePurchase()
|
||||
|
||||
// Create active subscription that won't cancel at period end
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns Result.success(
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(isActive = true, cancelled = false)
|
||||
)
|
||||
|
||||
@@ -528,6 +554,37 @@ class BackupSubscriptionCheckJobTest {
|
||||
verify { BackupRepository.getBackupTierWithoutDowngrade() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun givenSubscriptionWillCancelWithValidEntitlement_whenIRun_thenIExpectBackupStateNotification() {
|
||||
mockCanceledPurchase()
|
||||
|
||||
every { AppDependencies.accountApi.whoAmI() } returns NetworkResult.Success(
|
||||
WhoAmIResponse(
|
||||
number = "+1234567890",
|
||||
entitlements = WhoAmIResponse.Entitlements(
|
||||
backup = WhoAmIResponse.BackupEntitlement(
|
||||
backupLevel = 201,
|
||||
expirationSeconds = System.currentTimeMillis() / 1000 + 3600 // 1 hour from now
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
every { RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP) } returns NetworkResult.Success(
|
||||
createActiveSubscription(
|
||||
isActive = true,
|
||||
cancelled = true,
|
||||
chargeFailure = ChargeFailure("test", "", "", "", "")
|
||||
)
|
||||
)
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
val result = job.run()
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
verify { BackupStateObserver.notifyBackupStateChanged() }
|
||||
}
|
||||
|
||||
private fun createActiveSubscription(
|
||||
isActive: Boolean = true,
|
||||
billingPeriodEndSeconds: Long = 2147472000,
|
||||
@@ -553,15 +610,6 @@ class BackupSubscriptionCheckJobTest {
|
||||
)
|
||||
}
|
||||
|
||||
private fun mockProduct() {
|
||||
coEvery { AppDependencies.billingApi.queryProduct() } returns BillingProduct(
|
||||
price = FiatMoney(
|
||||
BigDecimal.ONE,
|
||||
Currency.getInstance("USD")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun insertSubscriber(token: String = IAP_TOKEN) {
|
||||
SignalDatabase.inAppPaymentSubscribers.insertOrReplace(
|
||||
InAppPaymentSubscriberRecord(
|
||||
@@ -575,6 +623,16 @@ class BackupSubscriptionCheckJobTest {
|
||||
)
|
||||
}
|
||||
|
||||
private fun insertPrePendingInAppPayment() {
|
||||
SignalDatabase.inAppPayments.insert(
|
||||
type = InAppPaymentType.RECURRING_BACKUP,
|
||||
state = InAppPaymentTable.State.TRANSACTING,
|
||||
subscriberId = null,
|
||||
endOfPeriod = null,
|
||||
inAppPaymentData = InAppPaymentData()
|
||||
)
|
||||
}
|
||||
|
||||
private fun insertPendingInAppPayment() {
|
||||
SignalDatabase.inAppPayments.insert(
|
||||
type = InAppPaymentType.RECURRING_BACKUP,
|
||||
@@ -593,20 +651,6 @@ class BackupSubscriptionCheckJobTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertFailedInAppPayment() {
|
||||
SignalDatabase.inAppPayments.insert(
|
||||
type = InAppPaymentType.RECURRING_BACKUP,
|
||||
state = InAppPaymentTable.State.END,
|
||||
subscriberId = null,
|
||||
endOfPeriod = null,
|
||||
inAppPaymentData = InAppPaymentData(
|
||||
error = InAppPaymentData.Error(
|
||||
type = InAppPaymentData.Error.Type.PAYMENT_SETUP
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun mockActivePurchase() {
|
||||
coEvery { AppDependencies.billingApi.queryPurchases() } returns BillingPurchaseResult.Success(
|
||||
purchaseState = BillingPurchaseState.PURCHASED,
|
||||
|
||||
@@ -1938,7 +1938,7 @@ object BackupRepository {
|
||||
suspend fun getPaidType(): NetworkResult<MessageBackupsType.Paid> {
|
||||
val productPrice: FiatMoney? = if (SignalStore.backup.backupTierInternalOverride == MessageBackupTier.PAID) {
|
||||
Log.d(TAG, "Accessing price via mock subscription.")
|
||||
RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).getOrNull()?.activeSubscription?.let {
|
||||
RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).successOrNull()?.activeSubscription?.let {
|
||||
FiatMoney.fromSignalNetworkAmount(it.amount, Currency.getInstance(it.currency))
|
||||
}
|
||||
} else if (AppDependencies.billingApi.getApiAvailability().isSuccess) {
|
||||
|
||||
@@ -181,7 +181,7 @@ class MessageBackupsFlowViewModel(
|
||||
RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP)
|
||||
}
|
||||
|
||||
activeSubscription.onSuccess { subscription ->
|
||||
activeSubscription.runIfSuccessful { subscription ->
|
||||
if (subscription.willCancelAtPeriodEnd()) {
|
||||
Log.d(TAG, "Active subscription is cancelled. Clearing tier.")
|
||||
internalStateFlow.update {
|
||||
|
||||
@@ -269,8 +269,27 @@ class BackupStateObserver(
|
||||
|
||||
Log.d(TAG, "[getNetworkBackupState][subscriptionStateMismatchDetected] hasActiveGooglePlayBillingSubscription: $hasActiveGooglePlayBillingSubscription")
|
||||
|
||||
val activeSubscription = withContext(Dispatchers.IO) {
|
||||
RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).getOrNull()
|
||||
val activeSubscriptionResult = withContext(Dispatchers.IO) {
|
||||
RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP)
|
||||
}
|
||||
|
||||
val activeSubscription: ActiveSubscription? = when (activeSubscriptionResult) {
|
||||
is NetworkResult.ApplicationError<ActiveSubscription> -> {
|
||||
Log.w(TAG, "[getNetworkBackupState][subscriptionStateMismatchDetected] Failed to load active subscription due to an application error.", activeSubscriptionResult.getCause(), true)
|
||||
return getStateOnError()
|
||||
}
|
||||
is NetworkResult.NetworkError<ActiveSubscription> -> {
|
||||
Log.w(TAG, "[getNetworkBackupState][subscriptionStateMismatchDetected] Failed to load active subscription due to a network error.", activeSubscriptionResult.getCause(), true)
|
||||
return getStateOnError()
|
||||
}
|
||||
is NetworkResult.StatusCodeError<ActiveSubscription> -> {
|
||||
Log.i(TAG, "[getNetworkBackupState][subscriptionStateMismatchDetected] Failed to load active subscription due to a status code error.", activeSubscriptionResult.getCause(), true)
|
||||
null
|
||||
}
|
||||
is NetworkResult.Success<ActiveSubscription> -> {
|
||||
Log.i(TAG, "[getNetworkBackupState][subscriptionStateMismatchDetected] Successfully loaded active subscription.", true)
|
||||
activeSubscriptionResult.result
|
||||
}
|
||||
}
|
||||
|
||||
val hasActiveSignalSubscription = activeSubscription?.isActive == true
|
||||
@@ -358,10 +377,10 @@ class BackupStateObserver(
|
||||
RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP)
|
||||
}
|
||||
|
||||
return if (activeSubscription.isSuccess) {
|
||||
return if (activeSubscription is NetworkResult.Success) {
|
||||
Log.d(TAG, "[getPaidBackupState] Retrieved subscription details.")
|
||||
|
||||
val subscription = activeSubscription.getOrThrow().activeSubscription
|
||||
val subscription = activeSubscription.successOrThrow().activeSubscription
|
||||
if (subscription != null) {
|
||||
Log.d(TAG, "[getPaidBackupState] Subscription found. Updating UI state with subscription details. Status: ${subscription.status}")
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.badges.Badges
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository.requireSubscriberType
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository.toPaymentSourceType
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository.cancelActiveSubscriptionIfNecessarySync
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository.cancelActiveSubscriptionSync
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository.getActiveSubscriptionSync
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository.rotateSubscriberIdSync
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
||||
@@ -25,6 +29,7 @@ import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
||||
import org.thoughtcrime.securesms.subscription.Subscription
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import org.whispersystems.signalservice.api.storage.IAPSubscriptionId
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import org.whispersystems.signalservice.api.subscriptions.IdempotencyKey
|
||||
@@ -51,7 +56,7 @@ object RecurringInAppPaymentRepository {
|
||||
@CheckResult
|
||||
fun getActiveSubscription(type: InAppPaymentSubscriberRecord.Type): Single<ActiveSubscription> {
|
||||
return Single.fromCallable {
|
||||
getActiveSubscriptionSync(type).getOrThrow()
|
||||
getActiveSubscriptionSync(type).successOrThrow()
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
@@ -77,26 +82,23 @@ object RecurringInAppPaymentRepository {
|
||||
* Gets the active subscription if it exists for the given [InAppPaymentSubscriberRecord.Type]
|
||||
*/
|
||||
@WorkerThread
|
||||
fun getActiveSubscriptionSync(type: InAppPaymentSubscriberRecord.Type): Result<ActiveSubscription> {
|
||||
fun getActiveSubscriptionSync(type: InAppPaymentSubscriberRecord.Type): NetworkResult<ActiveSubscription> {
|
||||
if (type == InAppPaymentSubscriberRecord.Type.BACKUP && SignalStore.backup.backupTierInternalOverride == MessageBackupTier.PAID) {
|
||||
Log.d(TAG, "Returning mock paid subscription.")
|
||||
return Result.success(MOCK_PAID_SUBSCRIPTION)
|
||||
return NetworkResult.Success(MOCK_PAID_SUBSCRIPTION)
|
||||
}
|
||||
|
||||
val response = InAppPaymentsRepository.getSubscriber(type)?.let {
|
||||
donationsService.getSubscription(it.subscriberId)
|
||||
} ?: return Result.success(ActiveSubscription.EMPTY)
|
||||
} ?: return NetworkResult.Success(ActiveSubscription.EMPTY)
|
||||
|
||||
return try {
|
||||
val result = response.resultOrThrow
|
||||
response.result.ifPresent { result ->
|
||||
if (result.isActive && result.activeSubscription.endOfCurrentPeriod > SignalStore.inAppPayments.getLastEndOfPeriod()) {
|
||||
InAppPaymentKeepAliveJob.enqueueAndTrackTime(System.currentTimeMillis().milliseconds)
|
||||
}
|
||||
|
||||
Result.success(result)
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
|
||||
return response.toNetworkResult()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -141,7 +141,22 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
val activeSubscription = RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).getOrNull()
|
||||
val activeSubscriptionResult = RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP)
|
||||
val activeSubscription: ActiveSubscription? = when (activeSubscriptionResult) {
|
||||
is NetworkResult.ApplicationError<ActiveSubscription>, is NetworkResult.NetworkError<ActiveSubscription> -> {
|
||||
Log.w(TAG, "Encountered an app-level or network-level error. Failing.", activeSubscriptionResult.getCause(), true)
|
||||
return Result.failure()
|
||||
}
|
||||
is NetworkResult.StatusCodeError<ActiveSubscription> -> {
|
||||
Log.w(TAG, "Encountered a status-code error.", activeSubscriptionResult.getCause(), true)
|
||||
null
|
||||
}
|
||||
is NetworkResult.Success<ActiveSubscription> -> {
|
||||
Log.i(TAG, "Successfully retrieved the user's active subscription object.", true)
|
||||
activeSubscriptionResult.result
|
||||
}
|
||||
}
|
||||
|
||||
val hasActiveSignalSubscription = activeSubscription?.isActive == true
|
||||
|
||||
checkForFailedOrCanceledSubscriptionState(activeSubscription)
|
||||
@@ -234,7 +249,7 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
|
||||
* the "download your data" notifier sheet.
|
||||
*/
|
||||
private fun checkForFailedOrCanceledSubscriptionState(activeSubscription: ActiveSubscription?) {
|
||||
if (activeSubscription?.willCancelAtPeriodEnd() == true && activeSubscription?.activeSubscription != null) {
|
||||
if (activeSubscription?.willCancelAtPeriodEnd() == true && activeSubscription.activeSubscription != null) {
|
||||
Log.i(TAG, "Subscription either has a payment failure or has been canceled.")
|
||||
|
||||
val response = SignalNetwork.account.whoAmI()
|
||||
|
||||
@@ -92,7 +92,7 @@ class PostRegistrationBackupRedemptionJob : CoroutineJob {
|
||||
}
|
||||
|
||||
info("Attempting to grab price information for records...")
|
||||
val subscription = RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).getOrNull()?.activeSubscription
|
||||
val subscription = RecurringInAppPaymentRepository.getActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP).successOrNull()?.activeSubscription
|
||||
|
||||
val emptyPrice = FiatMoney(BigDecimal.ZERO, Currency.getInstance(Locale.getDefault()))
|
||||
val price: FiatMoney = if (subscription != null) {
|
||||
|
||||
@@ -246,6 +246,16 @@ sealed class NetworkResult<T>(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result if successful, otherwise null.
|
||||
*/
|
||||
fun successOrNull(): T? {
|
||||
return when (this) {
|
||||
is Success -> result
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Throwable] associated with the result, or null if the result is successful.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user