Skip to content

Commit

Permalink
test SubscriptionFeaturesFetcher
Browse files Browse the repository at this point in the history
  • Loading branch information
lmac012 committed Dec 1, 2024
1 parent 19a1aa7 commit d04a296
Showing 1 changed file with 146 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package com.duckduckgo.subscriptions.impl

import androidx.lifecycle.Lifecycle.State.CREATED
import androidx.lifecycle.Lifecycle.State.INITIALIZED
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.ProductDetails.PricingPhase
import com.android.billingclient.api.ProductDetails.PricingPhases
import com.android.billingclient.api.ProductDetails.SubscriptionOfferDetails
import com.duckduckgo.common.test.CoroutineTestRule
import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory
import com.duckduckgo.feature.toggles.api.Toggle.State
import com.duckduckgo.subscriptions.impl.SubscriptionsConstants.ITR
import com.duckduckgo.subscriptions.impl.SubscriptionsConstants.MONTHLY_PLAN_US
import com.duckduckgo.subscriptions.impl.SubscriptionsConstants.NETP
import com.duckduckgo.subscriptions.impl.SubscriptionsConstants.YEARLY_PLAN_US
import com.duckduckgo.subscriptions.impl.billing.PlayBillingManager
import com.duckduckgo.subscriptions.impl.repository.AuthRepository
import com.duckduckgo.subscriptions.impl.services.FeaturesResponse
import com.duckduckgo.subscriptions.impl.services.SubscriptionsService
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
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

@RunWith(AndroidJUnit4::class)
class SubscriptionFeaturesFetcherTest {

@get:Rule
val coroutineRule = CoroutineTestRule()

private val processLifecycleOwner = TestLifecycleOwner(initialState = INITIALIZED)
private val playBillingManager: PlayBillingManager = mock()
private val subscriptionsService: SubscriptionsService = mock()
private val authRepository: AuthRepository = mock()
private val privacyProFeature: PrivacyProFeature = FakeFeatureToggleFactory.create(PrivacyProFeature::class.java)

private val subscriptionFeaturesFetcher = SubscriptionFeaturesFetcher(
appCoroutineScope = coroutineRule.testScope,
playBillingManager = playBillingManager,
subscriptionsService = subscriptionsService,
authRepository = authRepository,
privacyProFeature = privacyProFeature,
dispatcherProvider = coroutineRule.testDispatcherProvider,
)

@Before
fun setUp() {
processLifecycleOwner.lifecycle.addObserver(subscriptionFeaturesFetcher)
}

@Test
fun `when FF disabled then does not do anything`() = runTest {
givenIsFeaturesApiEnabled(false)

processLifecycleOwner.currentState = CREATED

verifyNoInteractions(playBillingManager)
verifyNoInteractions(authRepository)
verifyNoInteractions(subscriptionsService)
}

@Test
fun `when products loaded then fetches and stores features`() = runTest {
givenIsFeaturesApiEnabled(true)
val productDetails = mockProductDetails()
whenever(playBillingManager.productsFlow).thenReturn(flowOf(productDetails))
whenever(authRepository.getFeatures(any())).thenReturn(emptySet())
whenever(subscriptionsService.features(any())).thenReturn(FeaturesResponse(listOf(NETP, ITR)))

processLifecycleOwner.currentState = CREATED

verify(playBillingManager).productsFlow
verify(authRepository).getFeatures(MONTHLY_PLAN_US)
verify(authRepository).getFeatures(YEARLY_PLAN_US)
verify(authRepository).setFeatures(MONTHLY_PLAN_US, setOf(NETP, ITR))
verify(authRepository).setFeatures(YEARLY_PLAN_US, setOf(NETP, ITR))
}

@Test
fun `when there are no products then does not store anything`() = runTest {
givenIsFeaturesApiEnabled(true)
whenever(playBillingManager.productsFlow).thenReturn(flowOf())

processLifecycleOwner.currentState = CREATED

verify(playBillingManager).productsFlow
verifyNoInteractions(authRepository)
verifyNoInteractions(subscriptionsService)
}

@Test
fun `when features already stored then does not fetch again`() = runTest {
givenIsFeaturesApiEnabled(true)
val productDetails = mockProductDetails()
whenever(playBillingManager.productsFlow).thenReturn(flowOf(productDetails))
whenever(authRepository.getFeatures(any())).thenReturn(setOf(NETP, ITR))

processLifecycleOwner.currentState = CREATED

verify(playBillingManager).productsFlow
verify(authRepository).getFeatures(MONTHLY_PLAN_US)
verify(authRepository).getFeatures(YEARLY_PLAN_US)
verify(authRepository, never()).setFeatures(any(), any())
verifyNoInteractions(subscriptionsService)
}

private fun givenIsFeaturesApiEnabled(value: Boolean) {
privacyProFeature.featuresApi().setRawStoredState(State(value))
}

private fun mockProductDetails(): List<ProductDetails> {
val productDetails: ProductDetails = mock { productDetails ->
whenever(productDetails.productId).thenReturn(SubscriptionsConstants.BASIC_SUBSCRIPTION)

val pricingPhaseList: List<PricingPhase> = listOf(
mock { pricingPhase -> whenever(pricingPhase.formattedPrice).thenReturn("1$") },
)

val pricingPhases: PricingPhases = mock { pricingPhases ->
whenever(pricingPhases.pricingPhaseList).thenReturn(pricingPhaseList)
}

val offers = listOf(MONTHLY_PLAN_US, YEARLY_PLAN_US)
.map { basePlanId ->
mock<SubscriptionOfferDetails> { offer ->
whenever(offer.basePlanId).thenReturn(basePlanId)
whenever(offer.pricingPhases).thenReturn(pricingPhases)
}
}

whenever(productDetails.subscriptionOfferDetails).thenReturn(offers)
}

return listOf(productDetails)
}
}

0 comments on commit d04a296

Please sign in to comment.