mirror of
https://github.com/ProtonMail/protoncore_android.git
synced 2026-05-15 09:50:41 +00:00
fix(plan): Fetching subscription would crash if app is offline.
This commit is contained in:
+11
-8
@@ -18,7 +18,6 @@
|
||||
|
||||
package me.proton.core.payment.presentation.viewmodel
|
||||
|
||||
import java.util.Optional
|
||||
import android.app.Activity
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -52,7 +51,7 @@ import me.proton.core.payment.domain.usecase.PaymentProvider.GoogleInAppPurchase
|
||||
import me.proton.core.payment.presentation.LogTag
|
||||
import me.proton.core.payment.presentation.viewmodel.ProtonPaymentEvent.Error
|
||||
import me.proton.core.plan.domain.entity.DynamicPlan
|
||||
import me.proton.core.plan.domain.entity.SubscriptionManagement
|
||||
import me.proton.core.plan.domain.entity.SubscriptionManagement.GOOGLE_MANAGED
|
||||
import me.proton.core.plan.domain.usecase.GetDynamicSubscription
|
||||
import me.proton.core.plan.domain.usecase.PerformGiapPurchase
|
||||
import me.proton.core.presentation.app.ActivityProvider
|
||||
@@ -60,6 +59,7 @@ import me.proton.core.presentation.viewmodel.ProtonViewModel
|
||||
import me.proton.core.util.kotlin.CoreLogger
|
||||
import me.proton.core.util.kotlin.coroutine.ResultCollector
|
||||
import me.proton.core.util.kotlin.coroutine.launchWithResultContext
|
||||
import java.util.Optional
|
||||
import javax.inject.Inject
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
@@ -106,12 +106,15 @@ internal class ProtonPaymentButtonViewModel @Inject constructor(
|
||||
val lastEvent = flow {
|
||||
emit(ProtonPaymentEvent.Loading)
|
||||
|
||||
val subscription = userId?.let { getCurrentSubscription(it) }
|
||||
if (subscription?.external != null && subscription.external == SubscriptionManagement.GOOGLE_MANAGED
|
||||
&& subscription.deeplink != null
|
||||
) {
|
||||
emit(Error.SubscriptionManagedByOtherApp(userId, subscription.deeplink!!))
|
||||
return@flow
|
||||
if (userId != null) {
|
||||
val subscription = getCurrentSubscription(userId)
|
||||
if (subscription == null) {
|
||||
emit(Error.Generic(Exception("Could not get current subscription.")))
|
||||
return@flow
|
||||
} else if (subscription.external == GOOGLE_MANAGED && subscription.deeplink != null) {
|
||||
emit(Error.SubscriptionManagedByOtherApp(userId, subscription.deeplink!!))
|
||||
return@flow
|
||||
}
|
||||
}
|
||||
|
||||
when (resolvedPaymentProvider) {
|
||||
|
||||
+20
-2
@@ -27,9 +27,8 @@ import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.launch
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.observability.domain.ObservabilityManager
|
||||
import me.proton.core.observability.domain.metrics.CheckoutBillingSubscribeTotal
|
||||
import me.proton.core.observability.domain.metrics.CheckoutGiapBillingCreatePaymentTokenTotal
|
||||
import me.proton.core.observability.domain.metrics.CheckoutGiapBillingLaunchBillingTotal
|
||||
import me.proton.core.observability.domain.metrics.CheckoutGiapBillingProductQueryTotal
|
||||
import me.proton.core.observability.domain.metrics.CheckoutGiapBillingQuerySubscriptionsTotal
|
||||
@@ -44,6 +43,7 @@ import me.proton.core.payment.domain.usecase.GetPreferredPaymentProvider
|
||||
import me.proton.core.payment.domain.usecase.PaymentProvider
|
||||
import me.proton.core.payment.presentation.viewmodel.ProtonPaymentButtonViewModel.ButtonState
|
||||
import me.proton.core.plan.domain.entity.DynamicPlan
|
||||
import me.proton.core.plan.domain.entity.DynamicSubscription
|
||||
import me.proton.core.plan.domain.usecase.GetDynamicSubscription
|
||||
import me.proton.core.plan.domain.usecase.PerformGiapPurchase
|
||||
import me.proton.core.presentation.app.ActivityProvider
|
||||
@@ -82,6 +82,7 @@ class ProtonPaymentButtonViewModelTest : CoroutinesTest by CoroutinesTest() {
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
convertToObservabilityGiapStatus = FakeConvertToObservabilityGiapStatus()
|
||||
coEvery { getCurrentSubscription(any()) } returns mockk<DynamicSubscription>()
|
||||
tested = ProtonPaymentButtonViewModel(
|
||||
activityProvider,
|
||||
Optional.of(convertToObservabilityGiapStatus),
|
||||
@@ -337,6 +338,23 @@ class ProtonPaymentButtonViewModelTest : CoroutinesTest by CoroutinesTest() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `subscription is null`() = coroutinesTest {
|
||||
// GIVEN
|
||||
coEvery { getCurrentSubscription(any()) } returns null
|
||||
val events = tested.paymentEvents(1)
|
||||
|
||||
events.test {
|
||||
// WHEN
|
||||
tested.onPayClicked(1, "CHF", 12, PaymentProvider.CardPayment, mockk(), UserId("uid")).join()
|
||||
|
||||
// THEN
|
||||
assertEquals(ProtonPaymentEvent.Loading, awaitItem())
|
||||
val err = assertIs<ProtonPaymentEvent.Error.Generic>(awaitItem())
|
||||
assertEquals("Could not get current subscription.", err.throwable.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeConvertToObservabilityGiapStatus : ConvertToObservabilityGiapStatus {
|
||||
|
||||
+1
-7
@@ -25,8 +25,6 @@ import me.proton.core.payment.domain.usecase.GoogleServicesUtils
|
||||
import me.proton.core.payment.domain.usecase.PaymentProvider
|
||||
import me.proton.core.plan.domain.SupportUpgradePaidPlans
|
||||
import me.proton.core.plan.domain.entity.SubscriptionManagement
|
||||
import me.proton.core.user.domain.UserManager
|
||||
import me.proton.core.user.domain.extension.canReadSubscription
|
||||
import java.util.Optional
|
||||
import javax.inject.Inject
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
@@ -36,17 +34,13 @@ class CanUpgradeFromMobile @Inject constructor(
|
||||
private val getAvailablePaymentProviders: GetAvailablePaymentProviders,
|
||||
private val getCurrentSubscription: GetDynamicSubscription,
|
||||
private val googleServicesUtils: Optional<GoogleServicesUtils>,
|
||||
private val userManager: UserManager
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(userId: UserId): Boolean {
|
||||
if (!supportPaidPlans) {
|
||||
return false
|
||||
}
|
||||
if (!userManager.getUser(userId).canReadSubscription()) {
|
||||
return false
|
||||
}
|
||||
val subscription = getCurrentSubscription(userId)
|
||||
val subscription = getCurrentSubscription(userId) ?: return false
|
||||
if (subscription.external != null && subscription.external != SubscriptionManagement.GOOGLE_MANAGED) {
|
||||
return false
|
||||
}
|
||||
|
||||
+11
-6
@@ -19,21 +19,26 @@
|
||||
package me.proton.core.plan.domain.usecase
|
||||
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.payment.domain.repository.PaymentsRepository
|
||||
import me.proton.core.plan.domain.entity.DynamicSubscription
|
||||
import me.proton.core.plan.domain.repository.PlansRepository
|
||||
import me.proton.core.user.domain.UserManager
|
||||
import me.proton.core.user.domain.extension.canReadSubscription
|
||||
import me.proton.core.util.kotlin.runCatchingCheckedExceptions
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Gets current active dynamic subscription a user has.
|
||||
* Authorized. This means that it could only be used for upgrades. New accounts created during sign ups logically do not
|
||||
* have existing subscriptions.
|
||||
* NOTE: You may want to call [me.proton.core.user.domain.extension.canReadSubscription] before calling this.
|
||||
*/
|
||||
public class GetDynamicSubscription @Inject constructor(
|
||||
private val plansRepository: PlansRepository
|
||||
private val plansRepository: PlansRepository,
|
||||
private val userManager: UserManager
|
||||
) {
|
||||
public suspend operator fun invoke(userId: UserId): DynamicSubscription {
|
||||
return plansRepository.getDynamicSubscriptions(userId).first()
|
||||
}
|
||||
public suspend operator fun invoke(userId: UserId): DynamicSubscription? = runCatchingCheckedExceptions {
|
||||
when {
|
||||
userManager.getUser(userId).canReadSubscription() -> plansRepository.getDynamicSubscriptions(userId).first()
|
||||
else -> null
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
+15
-24
@@ -44,7 +44,7 @@ class CanUpgradeFromMobileTest {
|
||||
@MockK
|
||||
private lateinit var getAvailablePaymentProviders: GetAvailablePaymentProviders
|
||||
|
||||
@MockK(relaxed = true)
|
||||
@MockK
|
||||
private lateinit var getCurrentSubscription: GetDynamicSubscription
|
||||
|
||||
@MockK
|
||||
@@ -53,9 +53,6 @@ class CanUpgradeFromMobileTest {
|
||||
@MockK
|
||||
private lateinit var optionalGoogleServicesUtils: Optional<GoogleServicesUtils>
|
||||
|
||||
@MockK
|
||||
private lateinit var userManager: UserManager
|
||||
|
||||
private val testUserId = UserId("user-id")
|
||||
private lateinit var useCase: CanUpgradeFromMobile
|
||||
|
||||
@@ -66,8 +63,7 @@ class CanUpgradeFromMobileTest {
|
||||
supportPaidPlans = true,
|
||||
getAvailablePaymentProviders = getAvailablePaymentProviders,
|
||||
getCurrentSubscription = getCurrentSubscription,
|
||||
googleServicesUtils = optionalGoogleServicesUtils,
|
||||
userManager = userManager
|
||||
googleServicesUtils = optionalGoogleServicesUtils
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,8 +74,7 @@ class CanUpgradeFromMobileTest {
|
||||
supportPaidPlans = false,
|
||||
getAvailablePaymentProviders = getAvailablePaymentProviders,
|
||||
getCurrentSubscription = getCurrentSubscription,
|
||||
googleServicesUtils = optionalGoogleServicesUtils,
|
||||
userManager = userManager
|
||||
googleServicesUtils = optionalGoogleServicesUtils
|
||||
)
|
||||
// WHEN
|
||||
val result = useCase(testUserId)
|
||||
@@ -91,7 +86,8 @@ class CanUpgradeFromMobileTest {
|
||||
fun `can upgrade returns false when no payment providers available`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { getAvailablePaymentProviders() } returns emptySet()
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.NoOrganization }
|
||||
coEvery { getCurrentSubscription(testUserId) } returns
|
||||
mockk { every { external } returns SubscriptionManagement.GOOGLE_MANAGED }
|
||||
// WHEN
|
||||
val result = useCase(testUserId)
|
||||
// THEN
|
||||
@@ -102,7 +98,8 @@ class CanUpgradeFromMobileTest {
|
||||
fun `can upgrade returns false when only PayPal payment provider is available`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { getAvailablePaymentProviders() } returns setOf(PaymentProvider.PayPal)
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.NoOrganization }
|
||||
coEvery { getCurrentSubscription(testUserId) } returns
|
||||
mockk { every { external } returns SubscriptionManagement.GOOGLE_MANAGED }
|
||||
// WHEN
|
||||
val result = useCase(testUserId)
|
||||
// THEN
|
||||
@@ -114,14 +111,12 @@ class CanUpgradeFromMobileTest {
|
||||
// GIVEN
|
||||
every { optionalGoogleServicesUtils.getOrNull() } returns googleServicesUtils
|
||||
every { googleServicesUtils.isGooglePlayServicesAvailable() } returns GoogleServicesAvailability.Success
|
||||
coEvery { getCurrentSubscription(testUserId) } returns mockk {
|
||||
every { external } returns SubscriptionManagement.GOOGLE_MANAGED
|
||||
}
|
||||
coEvery { getCurrentSubscription(testUserId) } returns
|
||||
mockk { every { external } returns SubscriptionManagement.GOOGLE_MANAGED }
|
||||
coEvery { getAvailablePaymentProviders() } returns setOf(
|
||||
PaymentProvider.CardPayment,
|
||||
PaymentProvider.GoogleInAppPurchase
|
||||
)
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.NoOrganization }
|
||||
// WHEN
|
||||
val result = useCase(testUserId)
|
||||
// THEN
|
||||
@@ -133,13 +128,11 @@ class CanUpgradeFromMobileTest {
|
||||
// GIVEN
|
||||
every { optionalGoogleServicesUtils.getOrNull() } returns googleServicesUtils
|
||||
every { googleServicesUtils.isGooglePlayServicesAvailable() } returns GoogleServicesAvailability.ServiceInvalid
|
||||
coEvery { getCurrentSubscription(testUserId) } returns mockk {
|
||||
every { external } returns SubscriptionManagement.GOOGLE_MANAGED
|
||||
}
|
||||
coEvery { getCurrentSubscription(testUserId) } returns
|
||||
mockk { every { external } returns SubscriptionManagement.GOOGLE_MANAGED }
|
||||
coEvery { getAvailablePaymentProviders() } returns setOf(
|
||||
PaymentProvider.GoogleInAppPurchase
|
||||
)
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.NoOrganization }
|
||||
// WHEN
|
||||
val result = useCase(testUserId)
|
||||
// THEN
|
||||
@@ -149,14 +142,12 @@ class CanUpgradeFromMobileTest {
|
||||
@Test
|
||||
fun `can upgrade returns false for Proton Managed when payment providers available`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { getCurrentSubscription(testUserId) } returns mockk {
|
||||
every { external } returns SubscriptionManagement.PROTON_MANAGED
|
||||
}
|
||||
coEvery { getCurrentSubscription(testUserId) } returns
|
||||
mockk { every { external } returns SubscriptionManagement.PROTON_MANAGED }
|
||||
coEvery { getAvailablePaymentProviders() } returns setOf(
|
||||
PaymentProvider.CardPayment,
|
||||
PaymentProvider.GoogleInAppPurchase
|
||||
)
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.NoOrganization }
|
||||
// WHEN
|
||||
val result = useCase(testUserId)
|
||||
// THEN
|
||||
@@ -164,9 +155,9 @@ class CanUpgradeFromMobileTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can upgrade returns false for members of organizations`() = runTest {
|
||||
fun `can upgrade returns false if subscription is null`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.OrganizationMember }
|
||||
coEvery { getCurrentSubscription(testUserId) } returns null
|
||||
// WHEN
|
||||
val result = useCase(testUserId)
|
||||
// THEN
|
||||
|
||||
+42
-10
@@ -18,7 +18,10 @@
|
||||
|
||||
package me.proton.core.plan.domain.usecase
|
||||
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import me.proton.core.domain.entity.UserId
|
||||
@@ -27,15 +30,23 @@ import me.proton.core.network.domain.ApiResult
|
||||
import me.proton.core.network.domain.ResponseCodes
|
||||
import me.proton.core.plan.domain.entity.dynamicSubscription
|
||||
import me.proton.core.plan.domain.repository.PlansRepository
|
||||
import me.proton.core.user.domain.UserManager
|
||||
import me.proton.core.user.domain.entity.Role
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class GetDynamicSubscriptionTest {
|
||||
// region mocks
|
||||
private val repository = mockk<PlansRepository>(relaxed = true)
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var repository: PlansRepository
|
||||
|
||||
@MockK
|
||||
private lateinit var userManager: UserManager
|
||||
// endregion
|
||||
|
||||
// region test data
|
||||
@@ -48,19 +59,21 @@ class GetDynamicSubscriptionTest {
|
||||
|
||||
@Before
|
||||
fun beforeEveryTest() {
|
||||
useCase = GetDynamicSubscription(repository)
|
||||
MockKAnnotations.init(this)
|
||||
useCase = GetDynamicSubscription(repository, userManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get subscription returns success, enqueue getsubscription observability success`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { repository.getDynamicSubscriptions(testUserId) } returns testSubscriptions
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.NoOrganization }
|
||||
// WHEN
|
||||
val result = useCase.invoke(testUserId)
|
||||
// THEN
|
||||
assertEquals(testSubscription, result)
|
||||
assertNotNull(result)
|
||||
assertEquals(0L, result.amount)
|
||||
assertEquals(testSubscription, result)
|
||||
assertEquals(0L, result?.amount)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -72,13 +85,11 @@ class GetDynamicSubscriptionTest {
|
||||
RuntimeException("Test error")
|
||||
)
|
||||
)
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.OrganizationAdmin }
|
||||
// WHEN
|
||||
val throwable = assertFailsWith(ApiException::class) {
|
||||
useCase.invoke(testUserId)
|
||||
}
|
||||
val result = useCase.invoke(testUserId)
|
||||
// THEN
|
||||
assertNotNull(throwable)
|
||||
assertEquals("Test error", throwable.message)
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,7 +105,28 @@ class GetDynamicSubscriptionTest {
|
||||
)
|
||||
)
|
||||
)
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.NoOrganization }
|
||||
// WHEN
|
||||
assertFailsWith<ApiException> { useCase.invoke(testUserId) }
|
||||
val result = useCase.invoke(testUserId)
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no subscription if user is member of organization`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { userManager.getUser(testUserId) } returns mockk { every { role } returns Role.OrganizationMember }
|
||||
|
||||
// WHEN
|
||||
val result = useCase.invoke(testUserId)
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exception is rethrown for runtime exception`() = runTest {
|
||||
// GIVEN
|
||||
coEvery { userManager.getUser(testUserId) } throws CancellationException("Error")
|
||||
|
||||
// WHEN
|
||||
assertFailsWith<CancellationException> { useCase.invoke(testUserId) }
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import me.proton.core.compose.viewmodel.stopTimeoutMillis
|
||||
@@ -42,6 +43,7 @@ public class UpgradeStorageInfoViewModel @Inject constructor(
|
||||
) : ProtonViewModel() {
|
||||
public val state: StateFlow<AccountStorageState> = shouldUpgradeStorage()
|
||||
.map { it.toAccountStorageState() }
|
||||
.catch { emit(Hidden) }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis), INITIAL_STATE)
|
||||
|
||||
internal companion object {
|
||||
|
||||
+10
@@ -49,4 +49,14 @@ class UpgradeStorageInfoViewModelTest : CoroutinesTest by CoroutinesTest() {
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `state is Hidden when exception is thrown`() = coroutinesTest {
|
||||
every { shouldUpgradeStorage() } throws Exception()
|
||||
|
||||
tested.state.test {
|
||||
assertEquals(AccountStorageState.Hidden, awaitItem())
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ internal class CheckUnredeemedGooglePurchase @Inject constructor(
|
||||
val findUnacknowledgedGooglePurchase = findUnacknowledgedGooglePurchase.getOrNull() ?: return null
|
||||
if (PaymentProvider.GoogleInAppPurchase !in getAvailablePaymentProviders()) return null
|
||||
|
||||
val subscription = getCurrentSubscription(userId)
|
||||
val subscription = getCurrentSubscription(userId) ?: return null
|
||||
val subscriptionCustomerId = subscription.customerId
|
||||
val googlePurchases = if (subscriptionCustomerId != null) {
|
||||
listOfNotNull(findUnacknowledgedGooglePurchase.byCustomer(subscriptionCustomerId))
|
||||
|
||||
+6
-9
@@ -22,11 +22,8 @@ import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import me.proton.core.account.domain.entity.AccountType
|
||||
import me.proton.core.domain.entity.AppStore
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.network.domain.ApiException
|
||||
import me.proton.core.network.domain.ApiResult
|
||||
import me.proton.core.payment.domain.entity.GooglePurchase
|
||||
import me.proton.core.payment.domain.entity.ProductId
|
||||
import me.proton.core.payment.domain.usecase.FindUnacknowledgedGooglePurchase
|
||||
@@ -199,7 +196,7 @@ class CheckUnredeemedGooglePurchaseTest {
|
||||
|
||||
coEvery { findUnacknowledgedGooglePurchase.byCustomer(customerA) } returns googlePurchase
|
||||
coEvery { getAvailablePaymentProviders.invoke() } returns PaymentProvider.entries.toSet()
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk {
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk<DynamicSubscription> {
|
||||
every { customerId } returns customerA
|
||||
every { external } returns SubscriptionManagement.PROTON_MANAGED
|
||||
}
|
||||
@@ -238,7 +235,7 @@ class CheckUnredeemedGooglePurchaseTest {
|
||||
|
||||
coEvery { findUnacknowledgedGooglePurchase.byCustomer(any()) } returns googlePurchase
|
||||
coEvery { getAvailablePaymentProviders.invoke() } returns PaymentProvider.entries.toSet()
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk {
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk<DynamicSubscription> {
|
||||
every { customerId } returns customerB
|
||||
every { external } returns SubscriptionManagement.GOOGLE_MANAGED
|
||||
}
|
||||
@@ -279,7 +276,7 @@ class CheckUnredeemedGooglePurchaseTest {
|
||||
|
||||
coEvery { findUnacknowledgedGooglePurchase.byCustomer(customerA) } returns googlePurchase
|
||||
coEvery { getAvailablePaymentProviders.invoke() } returns PaymentProvider.entries.toSet()
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk {
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk<DynamicSubscription> {
|
||||
every { customerId } returns customerA
|
||||
every { external } returns SubscriptionManagement.GOOGLE_MANAGED
|
||||
every { name } returns planB
|
||||
@@ -318,7 +315,7 @@ class CheckUnredeemedGooglePurchaseTest {
|
||||
|
||||
coEvery { findUnacknowledgedGooglePurchase.byCustomer(customerA) } returns googlePurchase
|
||||
coEvery { getAvailablePaymentProviders.invoke() } returns PaymentProvider.entries.toSet()
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk {
|
||||
coEvery { getCurrentSubscription.invoke(userId) } returns mockk<DynamicSubscription> {
|
||||
every { customerId } returns "customer-A"
|
||||
every { external } returns SubscriptionManagement.GOOGLE_MANAGED
|
||||
every { name } returns "plan-A"
|
||||
@@ -444,9 +441,9 @@ class CheckUnredeemedGooglePurchaseTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns null on network error`() = runTest {
|
||||
fun `returns null if null subscription`() = runTest {
|
||||
coEvery { getAvailablePaymentProviders.invoke() } returns PaymentProvider.entries.toSet()
|
||||
coEvery { getCurrentSubscription.invoke(any()) } throws ApiException(ApiResult.Error.Connection())
|
||||
coEvery { getCurrentSubscription.invoke(any()) } returns null
|
||||
assertNull(tested(mockk()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Proton AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.util.kotlin
|
||||
|
||||
/**
|
||||
* Runs the given block of code and returns the result.
|
||||
* Any [RuntimeException] or [Error] are re-thrown
|
||||
* (e.g. [kotlin.coroutines.cancellation.CancellationException] or [OutOfMemoryError]).
|
||||
*/
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
inline fun <R> runCatchingCheckedExceptions(block: () -> R): Result<R> {
|
||||
return try {
|
||||
Result.success(block())
|
||||
} catch (e: RuntimeException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user