summaryrefslogtreecommitdiffhomepage
path: root/android/lib/billing/src/androidTest
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2024-10-07 11:15:34 +0200
committerDavid Göransson <david.goransson@mullvad.net>2024-10-09 16:10:26 +0200
commitdcaccd122433c7b005193ea0a8e025ef2d4cf5ee (patch)
tree00dc0307f298b2c06154c0cc1c902b0581751fa9 /android/lib/billing/src/androidTest
parent6c1f16c386c2d4cd3560c3a4cce7900f267058ad (diff)
downloadmullvadvpn-dcaccd122433c7b005193ea0a8e025ef2d4cf5ee.tar.xz
mullvadvpn-dcaccd122433c7b005193ea0a8e025ef2d4cf5ee.zip
Migrate billing repository to unit test
Diffstat (limited to 'android/lib/billing/src/androidTest')
-rw-r--r--android/lib/billing/src/androidTest/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt383
1 files changed, 0 insertions, 383 deletions
diff --git a/android/lib/billing/src/androidTest/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt b/android/lib/billing/src/androidTest/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt
deleted file mode 100644
index 083dfabff0..0000000000
--- a/android/lib/billing/src/androidTest/kotlin/net/mullvad/mullvadvpn/lib/billing/BillingRepositoryTest.kt
+++ /dev/null
@@ -1,383 +0,0 @@
-package net.mullvad.mullvadvpn.lib.billing
-
-import android.app.Activity
-import android.content.Context
-import app.cash.turbine.test
-import com.android.billingclient.api.BillingClient
-import com.android.billingclient.api.BillingClient.BillingResponseCode
-import com.android.billingclient.api.BillingClientStateListener
-import com.android.billingclient.api.BillingFlowParams
-import com.android.billingclient.api.BillingResult
-import com.android.billingclient.api.ProductDetails
-import com.android.billingclient.api.ProductDetailsResult
-import com.android.billingclient.api.Purchase
-import com.android.billingclient.api.PurchasesResult
-import com.android.billingclient.api.PurchasesUpdatedListener
-import com.android.billingclient.api.QueryPurchasesParams
-import com.android.billingclient.api.queryProductDetails
-import com.android.billingclient.api.queryPurchasesAsync
-import io.mockk.CapturingSlot
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.mockkStatic
-import io.mockk.unmockkAll
-import io.mockk.verify
-import kotlin.test.assertEquals
-import kotlin.test.assertIs
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import net.mullvad.mullvadvpn.lib.billing.model.BillingException
-import net.mullvad.mullvadvpn.lib.billing.model.PurchaseEvent
-import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
-import net.mullvad.mullvadvpn.lib.common.test.assertLists
-import org.junit.jupiter.api.AfterEach
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.extension.ExtendWith
-
-@ExtendWith(TestCoroutineRule::class)
-class BillingRepositoryTest {
-
- private val mockContext: Context = mockk()
- private lateinit var billingRepository: BillingRepository
-
- private val mockBillingClientBuilder: BillingClient.Builder = mockk(relaxed = true)
- private val mockBillingClient: BillingClient = mockk()
-
- private val purchaseUpdatedListenerSlot: CapturingSlot<PurchasesUpdatedListener> =
- CapturingSlot()
-
- @BeforeEach
- fun setup() {
- mockkStatic(BILLING_CLIENT_CLASS)
- mockkStatic(BILLING_CLIENT_KOTLIN_CLASS)
- mockkStatic(BILLING_FLOW_PARAMS)
-
- every { BillingClient.newBuilder(any()) } returns mockBillingClientBuilder
- every { mockBillingClientBuilder.enablePendingPurchases(any()) } returns
- mockBillingClientBuilder
- every { mockBillingClientBuilder.setListener(capture(purchaseUpdatedListenerSlot)) } returns
- mockBillingClientBuilder
- every { mockBillingClientBuilder.build() } returns mockBillingClient
-
- billingRepository = BillingRepository(mockContext)
- }
-
- @AfterEach
- fun tearDown() {
- unmockkAll()
- }
-
- @Test
- fun testQueryProductsOk() = runTest {
- // Arrange
- val mockBillingResult: BillingResult = mockk()
- val mockProductDetails: ProductDetails = mockk()
- val expectedProductDetailsResult: ProductDetailsResult = mockk()
- val productId = "TEST"
- val price = "44.4"
-
- every { mockBillingResult.responseCode } returns BillingResponseCode.OK
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- coEvery { mockBillingClient.queryProductDetails(any()) } returns
- expectedProductDetailsResult
- every { expectedProductDetailsResult.billingResult } returns mockBillingResult
- every { expectedProductDetailsResult.productDetailsList } returns listOf(mockProductDetails)
- every { mockProductDetails.productId } returns productId
- every { mockProductDetails.oneTimePurchaseOfferDetails?.formattedPrice } returns price
-
- // Act
- val result = billingRepository.queryProducts(listOf(productId))
-
- // Assert
- assertEquals(expectedProductDetailsResult, result)
- }
-
- @Test
- fun testQueryProductsItemUnavailable() = runTest {
- // Arrange
- val mockBillingResult: BillingResult = mockk()
- val mockProductDetailsResult: ProductDetailsResult = mockk()
-
- every { mockBillingResult.responseCode } returns BillingResponseCode.ITEM_UNAVAILABLE
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- coEvery { mockBillingClient.queryProductDetails(any()) } returns mockProductDetailsResult
- every { mockProductDetailsResult.billingResult } returns mockBillingResult
- every { mockProductDetailsResult.productDetailsList } returns emptyList()
-
- // Act
- val result = billingRepository.queryProducts(listOf("TEST"))
-
- // Assert
- assertEquals(mockProductDetailsResult, result)
- }
-
- @Test
- fun testQueryProductsBillingUnavailable() = runTest {
- // Arrange
- val mockBillingResult: BillingResult = mockk()
- val mockProductDetailsResult: ProductDetailsResult = mockk()
-
- every { mockBillingResult.responseCode } returns BillingResponseCode.BILLING_UNAVAILABLE
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- coEvery { mockBillingClient.queryProductDetails(any()) } returns mockProductDetailsResult
- every { mockProductDetailsResult.billingResult } returns mockBillingResult
- every { mockProductDetailsResult.productDetailsList } returns emptyList()
-
- // Act
- val result = billingRepository.queryProducts(listOf("TEST"))
-
- // Assert
- assertEquals(mockProductDetailsResult, result)
- }
-
- @Test
- fun testStartPurchaseFlowOk() = runTest {
- // Arrange
- val mockProductBillingResult: BillingResult = mockk()
- val mockBillingResult: BillingResult = mockk()
- val transactionId = "MOCK22"
- val mockProductDetails: ProductDetails = mockk(relaxed = true)
- val mockActivityProvider: () -> Activity = mockk()
- every { mockBillingResult.responseCode } returns BillingResponseCode.OK
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- every { mockBillingClient.launchBillingFlow(any(), any()) } returns mockBillingResult
- every { BillingFlowParams.newBuilder() } returns mockk(relaxed = true)
- every { mockProductBillingResult.responseCode } returns BillingResponseCode.OK
- every { mockActivityProvider() } returns mockk()
-
- // Act
- val result =
- billingRepository.startPurchaseFlow(
- mockProductDetails,
- transactionId,
- mockActivityProvider,
- )
-
- // Assert
- assertEquals(mockBillingResult, result)
- }
-
- @Test
- fun testStartPurchaseFlowBillingUnavailable() = runTest {
- // Arrange
- val mockBillingResult: BillingResult = mockk()
- val transactionId = "MOCK22"
- val mockProductDetails: ProductDetails = mockk(relaxed = true)
- val mockActivityProvider: () -> Activity = mockk()
- every { mockBillingResult.responseCode } returns BillingResponseCode.BILLING_UNAVAILABLE
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- every { mockBillingClient.launchBillingFlow(any(), any()) } returns mockBillingResult
- every { BillingFlowParams.newBuilder() } returns mockk(relaxed = true)
- every { mockActivityProvider() } returns mockk()
-
- // Act
- val result =
- billingRepository.startPurchaseFlow(
- mockProductDetails,
- transactionId,
- mockActivityProvider,
- )
-
- // Assert
- assertEquals(mockBillingResult, result)
- }
-
- @Test
- fun testQueryPurchasesFoundPurchases() = runTest {
- // Arrange
- val mockResult: PurchasesResult = mockk()
- val mockPurchase: Purchase = mockk()
- every { mockResult.billingResult.responseCode } returns BillingResponseCode.OK
- every { mockResult.purchasesList } returns listOf(mockPurchase)
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- coEvery { mockBillingClient.queryPurchasesAsync(any<QueryPurchasesParams>()) } returns
- mockResult
- every { BillingFlowParams.newBuilder() } returns mockk(relaxed = true)
-
- // Act
- val result = billingRepository.queryPurchases()
-
- // Assert
- assertEquals(mockResult, result)
- }
-
- @Test
- fun testQueryPurchasesNoPurchaseFound() = runTest {
- // Arrange
- val mockResult: PurchasesResult = mockk()
- every { mockResult.billingResult.responseCode } returns BillingResponseCode.OK
- every { mockResult.purchasesList } returns emptyList()
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- coEvery { mockBillingClient.queryPurchasesAsync(any<QueryPurchasesParams>()) } returns
- mockResult
- every { BillingFlowParams.newBuilder() } returns mockk(relaxed = true)
-
- // Act
- val result = billingRepository.queryPurchases()
-
- // Assert
- assertEquals(mockResult, result)
- }
-
- @Test
- fun testQueryPurchasesError() = runTest {
- // Arrange
- val responseCode = BillingResponseCode.ITEM_UNAVAILABLE
- val message = "ERROR"
- val expectedError = BillingException(responseCode, message)
- val mockResult: PurchasesResult = mockk()
- every { mockResult.billingResult.responseCode } returns responseCode
- every { mockResult.billingResult.debugMessage } returns message
- every { mockResult.purchasesList } returns emptyList()
- every { mockBillingClient.isReady } returns true
- every { mockBillingClient.connectionState } returns BillingClient.ConnectionState.CONNECTED
- coEvery { mockBillingClient.queryPurchasesAsync(any<QueryPurchasesParams>()) } returns
- mockResult
- every { BillingFlowParams.newBuilder() } returns mockk(relaxed = true)
-
- // Act
- val result = billingRepository.queryPurchases()
-
- // Assert
- assertEquals(
- expectedError.toBillingResult().responseCode,
- result.billingResult.responseCode,
- )
- assertEquals(expectedError.message, result.billingResult.debugMessage)
- }
-
- @Test
- fun testPurchaseEventPurchaseComplete() = runTest {
- // Arrange
- val mockPurchase: Purchase = mockk()
- val mockPurchaseList = listOf(mockPurchase)
- val mockBillingResult: BillingResult = mockk()
- every { mockBillingResult.responseCode } returns BillingResponseCode.OK
-
- // Act, Assert
- billingRepository.purchaseEvents.test {
- purchaseUpdatedListenerSlot.captured.onPurchasesUpdated(
- mockBillingResult,
- mockPurchaseList,
- )
- val result = awaitItem()
- assertIs<PurchaseEvent.Completed>(result)
- assertLists(mockPurchaseList, result.purchases)
- }
- }
-
- @Test
- fun testPurchaseEventUserCanceled() = runTest {
- // Arrange
- val mockBillingResult: BillingResult = mockk()
- val mockResponseCode: Int = BillingResponseCode.USER_CANCELED
- every { mockBillingResult.responseCode } returns mockResponseCode
-
- // Act, Assert
- billingRepository.purchaseEvents.test {
- purchaseUpdatedListenerSlot.captured.onPurchasesUpdated(mockBillingResult, null)
- val result = awaitItem()
- assertIs<PurchaseEvent.UserCanceled>(result)
- }
- }
-
- @Test
- fun testPurchaseEventError() = runTest {
- // Arrange
- val mockDebugMessage = "ERROR"
- val mockBillingResult: BillingResult = mockk()
- val mockResponseCode: Int = BillingResponseCode.ERROR
- val expectedError =
- BillingException(responseCode = mockResponseCode, message = mockDebugMessage)
- every { mockBillingResult.responseCode } returns mockResponseCode
- every { mockBillingResult.debugMessage } returns mockDebugMessage
-
- // Act, Assert
- billingRepository.purchaseEvents.test {
- purchaseUpdatedListenerSlot.captured.onPurchasesUpdated(mockBillingResult, null)
- val result = awaitItem()
- assertIs<PurchaseEvent.Error>(result)
- assertEquals(expectedError.message, result.exception.message)
- }
- }
-
- @Test
- fun testEnsureConnectedStartConnection() = runTest {
- // Arrange
- val mockStartConnectionResult: BillingResult = mockk()
- every { mockBillingClient.isReady } returns false
- every { mockBillingClient.connectionState } returns
- BillingClient.ConnectionState.DISCONNECTED
- every { mockBillingClient.startConnection(any()) } answers
- {
- firstArg<BillingClientStateListener>()
- .onBillingSetupFinished(mockStartConnectionResult)
- }
- every { mockStartConnectionResult.responseCode } returns BillingResponseCode.OK
- coEvery { mockBillingClient.queryPurchasesAsync(any<QueryPurchasesParams>()) } returns
- mockk(relaxed = true)
-
- // Act
- billingRepository.queryPurchases()
-
- // Assert
- verify { mockBillingClient.startConnection(any()) }
- coVerify { mockBillingClient.queryPurchasesAsync(any<QueryPurchasesParams>()) }
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @Test
- fun testEnsureConnectedOnlyOneSuccessfulConnection() =
- runTest(UnconfinedTestDispatcher()) {
- // Arrange
- var hasConnected = false
- val mockStartConnectionResult: BillingResult = mockk()
- every { mockBillingClient.isReady } answers { hasConnected }
- every { mockBillingClient.connectionState } answers
- {
- if (hasConnected) {
- BillingClient.ConnectionState.CONNECTED
- } else {
- BillingClient.ConnectionState.DISCONNECTED
- }
- }
- every { mockBillingClient.startConnection(any()) } answers
- {
- hasConnected = true
- firstArg<BillingClientStateListener>()
- .onBillingSetupFinished(mockStartConnectionResult)
- }
- every { mockStartConnectionResult.responseCode } returns BillingResponseCode.OK
- coEvery { mockBillingClient.queryPurchasesAsync(any<QueryPurchasesParams>()) } returns
- mockk(relaxed = true)
- coEvery { mockBillingClient.queryProductDetails(any()) } returns mockk(relaxed = true)
-
- // Act
- launch { billingRepository.queryPurchases() }
- launch { billingRepository.queryProducts(listOf("MOCK")) }
-
- // Assert
- verify(exactly = 1) { mockBillingClient.startConnection(any()) }
- coVerify { mockBillingClient.queryPurchasesAsync(any<QueryPurchasesParams>()) }
- coVerify { mockBillingClient.queryProductDetails(any()) }
- }
-
- companion object {
- private const val BILLING_CLIENT_CLASS = "com.android.billingclient.api.BillingClient"
- private const val BILLING_CLIENT_KOTLIN_CLASS =
- "com.android.billingclient.api.BillingClientKotlinKt"
- private const val BILLING_FLOW_PARAMS = "com.android.billingclient.api.BillingFlowParams"
- }
-}