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 Oct 18, 2023
1 parent 58558fb commit 6e3610c
Showing 1 changed file with 180 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,27 @@ package net.mullvad.mullvadvpn.viewmodel
import androidx.lifecycle.viewModelScope
import app.cash.turbine.test
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 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.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.lib.payment.model.VerificationResult
import net.mullvad.mullvadvpn.model.AccountAndDevice
import net.mullvad.mullvadvpn.model.AccountExpiry
import net.mullvad.mullvadvpn.model.Device
Expand Down Expand Up @@ -42,6 +52,12 @@ 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, replay = 1)
private val verifyPurchases =
MutableSharedFlow<VerificationResult>(extraBufferCapacity = 1, replay = 1)
private val paymentAvailability =
MutableSharedFlow<PaymentAvailability>(extraBufferCapacity = 1, replay = 1)

// Service connections
private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk()
Expand All @@ -52,6 +68,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 +88,20 @@ class WelcomeViewModelTest {

every { mockAccountRepository.accountExpiryState } returns accountExpiryState

coEvery { mockPaymentRepository.verifyPurchases() } returns verifyPurchases

coEvery { mockPaymentRepository.purchaseBillingProduct(any()) } returns purchaseResult

coEvery { mockPaymentRepository.queryPaymentAvailability() } returns paymentAvailability

every { mockPaymentProvider.paymentRepository } returns mockPaymentRepository

viewModel =
WelcomeViewModel(
accountRepository = mockAccountRepository,
deviceRepository = mockDeviceRepository,
serviceConnectionManager = mockServiceConnectionManager,
paymentProvider = mockPaymentProvider,
pollAccountExpiry = false
)
}
Expand Down Expand Up @@ -112,9 +139,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 +158,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 +185,156 @@ class WelcomeViewModelTest {
}
}

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

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

@Test
fun testBillingProductsUnavailableState() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.ProductsUnavailable

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

@Test
fun testBillingProductsGenericErrorState() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.Error.Other(mockk())

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

@Test
fun testBillingProductsBillingErrorState() = runTest {
// Arrange
val mockPaymentAvailability = PaymentAvailability.Error.BillingUnavailable

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

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

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

@Test
fun testBillingVerificationError() = runTest {
// Arrange
val mockPurchaseResult = PurchaseResult.Error.VerificationError(null)

// Act, Assert
viewModel.uiState.test {
// Default item
awaitItem()
purchaseResult.tryEmit(mockPurchaseResult)
viewModel.startBillingPayment(productId = "mockId")
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
val result = awaitItem().purchaseResult
assertIs<PurchaseResult.Error.VerificationError>(result)
}
}

@Test
fun testBillingUserCancelled() = runTest {
// Arrange
val mockPurchaseResult = PurchaseResult.PurchaseCancelled

// Act, Assert
viewModel.uiState.test {
// Default item
awaitItem()
purchaseResult.tryEmit(mockPurchaseResult)
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
viewModel.startBillingPayment(productId = "mockId")
assertIs<PurchaseResult.PurchaseCancelled>(awaitItem().purchaseResult)
ensureAllEventsConsumed()
}
}

@Test
fun testBillingPurchaseCompleted() = runTest {
// Arrange
val mockPurchaseResult = PurchaseResult.PurchaseCompleted

// Act, Assert
viewModel.uiState.test {
// Default item
awaitItem()
purchaseResult.tryEmit(mockPurchaseResult)
serviceConnectionState.value =
ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer)
viewModel.startBillingPayment(productId = "mockId")
val result = awaitItem().purchaseResult
assertIs<PurchaseResult.PurchaseCompleted>(result)
}
}

@Test
fun testStartBillingPayment() {
// Arrange
val mockProductId = "MOCK"
coEvery { mockPaymentRepository.purchaseBillingProduct(mockProductId) } returns
mockk(relaxed = true)

// 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 6e3610c

Please sign in to comment.