Skip to content

Commit

Permalink
Add payment unit tests for welcome view model
Browse files Browse the repository at this point in the history
  • Loading branch information
Pururun committed Sep 27, 2023
1 parent 028f4ed commit c68c3e3
Showing 1 changed file with 173 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,30 @@ package net.mullvad.mullvadvpn.viewmodel

import androidx.lifecycle.viewModelScope
import app.cash.turbine.test
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.PaymentProvider
import net.mullvad.mullvadvpn.compose.state.PaymentState
import net.mullvad.mullvadvpn.compose.state.WelcomeDialogState
import net.mullvad.mullvadvpn.compose.state.WelcomeUiState
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.common.test.assertLists
import net.mullvad.mullvadvpn.lib.payment.PaymentRepository
import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability
import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
import net.mullvad.mullvadvpn.lib.payment.model.PurchaseResult
import net.mullvad.mullvadvpn.model.AccountAndDevice
import net.mullvad.mullvadvpn.model.AccountExpiry
import net.mullvad.mullvadvpn.model.Device
Expand Down Expand Up @@ -42,6 +54,7 @@ class WelcomeViewModelTest {
MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected)
private val deviceState = MutableStateFlow<DeviceState>(DeviceState.Initial)
private val accountExpiryState = MutableStateFlow<AccountExpiry>(AccountExpiry.Missing)
private val purchaseResult = MutableSharedFlow<PurchaseResult>(extraBufferCapacity = 1)

// Service connections
private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk()
Expand All @@ -52,6 +65,8 @@ class WelcomeViewModelTest {

private val mockAccountRepository: AccountRepository = mockk()
private val mockDeviceRepository: DeviceRepository = mockk()
private val mockPaymentRepository: PaymentRepository = mockk()
private val mockPaymentProvider: PaymentProvider = mockk()
private val mockServiceConnectionManager: ServiceConnectionManager = mockk()

private lateinit var viewModel: WelcomeViewModel
Expand All @@ -70,11 +85,21 @@ class WelcomeViewModelTest {

every { mockAccountRepository.accountExpiryState } returns accountExpiryState

coEvery { mockPaymentRepository.verifyPurchases() } just Runs

coEvery { mockPaymentRepository.purchaseResult } returns purchaseResult

coEvery { mockPaymentRepository.queryPaymentAvailability() } returns
PaymentAvailability.ProductsUnavailable

every { mockPaymentProvider.paymentRepository } returns mockPaymentRepository

viewModel =
WelcomeViewModel(
accountRepository = mockAccountRepository,
deviceRepository = mockDeviceRepository,
serviceConnectionManager = mockServiceConnectionManager,
paymentProvider = mockPaymentProvider,
pollAccountExpiry = false
)
}
Expand Down Expand Up @@ -112,9 +137,9 @@ class WelcomeViewModelTest {
// Act, Assert
viewModel.uiState.test {
assertEquals(WelcomeUiState(), awaitItem())
eventNotifierTunnelUiState.notify(tunnelUiStateTestItem)
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
eventNotifierTunnelUiState.notify(tunnelUiStateTestItem)
val result = awaitItem()
assertEquals(tunnelUiStateTestItem, result.tunnelState)
}
Expand All @@ -131,13 +156,13 @@ class WelcomeViewModelTest {
// Act, Assert
viewModel.uiState.test {
assertEquals(WelcomeUiState(), awaitItem())
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
deviceState.value =
DeviceState.LoggedIn(
accountAndDevice =
AccountAndDevice(account_token = expectedAccountNumber, device = device)
)
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem()
assertEquals(expectedAccountNumber, result.accountNumber)
}
Expand All @@ -158,6 +183,151 @@ class WelcomeViewModelTest {
}
}

@Test
fun testVerifyPurchases() = runTest {
// Act
viewModel.verifyPurchases()

// Assert
coVerify { mockPaymentRepository.verifyPurchases() }
}

@Test
fun testBillingProductsUnavailableState() = runTest {
// Arrange

// Act, Assert
viewModel.uiState.test {
// Default item
awaitItem()
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem().billingPaymentState
assertIs<PaymentState.NoPayment>(result)
}
}

@Test
fun testBillingProductsGenericErrorState() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.Error.Other(mockk())
coEvery { mockPaymentRepository.queryPaymentAvailability() } returns mockPaymentAvailability

// Act, Assert
viewModel.uiState.test {
// Default item
assertIs<PaymentState.Loading>(awaitItem().billingPaymentState)
viewModel.fetchPaymentAvailability()
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem().billingPaymentState
assertIs<PaymentState.GenericError>(result)
}
}

@Test
fun testBillingProductsBillingErrorState() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.Error.BillingUnavailable
coEvery { mockPaymentRepository.queryPaymentAvailability() } returns mockPaymentAvailability

// Act, Assert
viewModel.uiState.test {
// Default item
assertIs<PaymentState.Loading>(awaitItem().billingPaymentState)
viewModel.fetchPaymentAvailability()
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem().billingPaymentState
assertIs<PaymentState.BillingError>(result)
}
}

@Test
fun testBillingProductsPaymentAvailableState() = runTest {
// Arrange
val mockProduct: PaymentProduct = mockk()
val expectedProductList = listOf(mockProduct)
val mockPaymentAvailability = PaymentAvailability.ProductsAvailable(listOf(mockProduct))
coEvery { mockPaymentRepository.queryPaymentAvailability() } returns mockPaymentAvailability

// Act, Assert
viewModel.uiState.test {
// Default item
assertIs<PaymentState.Loading>(awaitItem().billingPaymentState)
viewModel.fetchPaymentAvailability()
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem().billingPaymentState
assertIs<PaymentState.PaymentAvailable>(result)
assertLists(expectedProductList, result.products)
}
}

@Test
fun testBillingVerificationError() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.ProductsUnavailable
coEvery { mockPaymentRepository.queryPaymentAvailability() } returns mockPaymentAvailability

// Act, Assert
viewModel.uiState.test {
// Default item
assertIs<WelcomeDialogState.NoDialog>(awaitItem().dialogState)
purchaseResult.tryEmit(PurchaseResult.VerificationError)
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem().dialogState
assertIs<WelcomeDialogState.VerificationError>(result)
}
}

@Test
fun testBillingUserCancelled() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.ProductsUnavailable
coEvery { mockPaymentRepository.queryPaymentAvailability() } returns mockPaymentAvailability

// Act, Assert
viewModel.uiState.test {
// Default item
assertIs<WelcomeDialogState.NoDialog>(awaitItem().dialogState)
purchaseResult.tryEmit(PurchaseResult.PurchaseCancelled)
ensureAllEventsConsumed()
}
}

@Test
fun testBillingPurchaseCompleted() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.ProductsUnavailable
coEvery { mockPaymentRepository.queryPaymentAvailability() } returns mockPaymentAvailability

// Act, Assert
viewModel.uiState.test {
// Default item
assertIs<WelcomeDialogState.NoDialog>(awaitItem().dialogState)
purchaseResult.tryEmit(PurchaseResult.PurchaseCompleted)
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem().dialogState
assertIs<WelcomeDialogState.PurchaseComplete>(result)
}
}

@Test
fun testStartBillingPayment() {
// Arrange
val mockProductId = "MOCK"
coEvery { mockPaymentRepository.purchaseBillingProduct(mockProductId) } just Runs

// Act
viewModel.startBillingPayment(mockProductId)

// Assert
coVerify { mockPaymentRepository.purchaseBillingProduct(mockProductId) }
}

companion object {
private const val SERVICE_CONNECTION_MANAGER_EXTENSIONS =
"net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManagerExtensionsKt"
Expand Down

0 comments on commit c68c3e3

Please sign in to comment.