summaryrefslogtreecommitdiffhomepage
path: root/android/app/src/test
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2024-03-11 15:57:19 +0100
committerDavid Göransson <david.goransson@mullvad.net>2024-03-11 15:57:19 +0100
commitbed06a1c14eaa312c8ed76a6310754261addaee5 (patch)
tree92c296ac643d3e5061434138279bfa1ddd208c31 /android/app/src/test
parent4869427603ffe33aa64c7b8bb553338562367e99 (diff)
parentc6b1744b673644eaec26c93da9beadec4344a575 (diff)
downloadmullvadvpn-bed06a1c14eaa312c8ed76a6310754261addaee5.tar.xz
mullvadvpn-bed06a1c14eaa312c8ed76a6310754261addaee5.zip
Merge branch 'outoftimescreen-is-navigated-to-despite-the-connectscreen-droid-658'
Diffstat (limited to 'android/app/src/test')
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt222
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt26
4 files changed, 170 insertions, 82 deletions
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
index d40e8d8e18..326e183445 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
@@ -3,10 +3,19 @@ package net.mullvad.mullvadvpn.usecase
import app.cash.turbine.test
import io.mockk.every
import io.mockk.mockk
+import io.mockk.unmockkAll
import kotlin.test.assertEquals
-import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlin.time.Duration.Companion.days
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
import net.mullvad.mullvadvpn.lib.ipc.Event
import net.mullvad.mullvadvpn.lib.ipc.MessageHandler
import net.mullvad.mullvadvpn.lib.ipc.events
@@ -16,6 +25,7 @@ import net.mullvad.mullvadvpn.repository.AccountRepository
import net.mullvad.talpid.tunnel.ErrorState
import net.mullvad.talpid.tunnel.ErrorStateCause
import org.joda.time.DateTime
+import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -23,106 +33,194 @@ class OutOfTimeUseCaseTest {
private val mockAccountRepository: AccountRepository = mockk()
private val mockMessageHandler: MessageHandler = mockk()
- private val events = MutableSharedFlow<Event.TunnelStateChange>()
- private val expiry = MutableStateFlow<AccountExpiry>(AccountExpiry.Missing)
+ private lateinit var events: Channel<Event.TunnelStateChange>
+ private lateinit var expiry: MutableStateFlow<AccountExpiry>
- lateinit var outOfTimeUseCase: OutOfTimeUseCase
+ private val dispatcher = StandardTestDispatcher()
+ private val scope = TestScope(dispatcher)
+
+ private lateinit var outOfTimeUseCase: OutOfTimeUseCase
@BeforeEach
fun setup() {
+ events = Channel()
+ expiry = MutableStateFlow(AccountExpiry.Missing)
every { mockAccountRepository.accountExpiryState } returns expiry
- every { mockMessageHandler.events<Event.TunnelStateChange>() } returns events
- outOfTimeUseCase = OutOfTimeUseCase(mockAccountRepository, mockMessageHandler)
+ every { mockMessageHandler.events<Event.TunnelStateChange>() } returns
+ events.receiveAsFlow()
+
+ Dispatchers.setMain(dispatcher)
+
+ outOfTimeUseCase =
+ OutOfTimeUseCase(mockAccountRepository, mockMessageHandler, scope.backgroundScope)
}
- @Test
- fun `no events should result in no expiry`() = runTest {
- // Arrange
- // Act, Assert
- outOfTimeUseCase.isOutOfTime().test { assertEquals(null, awaitItem()) }
+ @AfterEach
+ fun teardown() {
+ Dispatchers.resetMain()
+ unmockkAll()
}
@Test
- fun `tunnel is blocking because out of time should emit true`() = runTest {
- // Arrange
- // Act, Assert
- val errorStateCause = ErrorStateCause.AuthFailed("[EXPIRED_ACCOUNT]")
- val tunnelStateError = TunnelState.Error(ErrorState(errorStateCause, true))
- val errorChange = Event.TunnelStateChange(tunnelStateError)
+ fun `no events should result in no expiry`() =
+ scope.runTest {
+ // Arrange
+ // Act, Assert
+ outOfTimeUseCase.isOutOfTime.test { assertEquals(null, awaitItem()) }
+ }
- outOfTimeUseCase.isOutOfTime().test {
- assertEquals(null, awaitItem())
- events.emit(errorChange)
- assertEquals(true, awaitItem())
+ @Test
+ fun `tunnel is blocking because out of time should emit true`() =
+ scope.runTest {
+ // Arrange
+ // Act, Assert
+ val errorStateCause = ErrorStateCause.AuthFailed("[EXPIRED_ACCOUNT]")
+ val tunnelStateError = TunnelState.Error(ErrorState(errorStateCause, true))
+ val errorChange = Event.TunnelStateChange(tunnelStateError)
+
+ outOfTimeUseCase.isOutOfTime.test {
+ assertEquals(null, awaitItem())
+ events.send(errorChange)
+ assertEquals(true, awaitItem())
+ }
}
- }
@Test
- fun `tunnel is connected should emit false`() = runTest {
- // Arrange
- val expiredAccountExpiry = AccountExpiry.Available(DateTime.now().plusDays(1))
- val tunnelStateChanges =
- listOf(
- TunnelState.Disconnected(),
- TunnelState.Connected(mockk(), null),
- TunnelState.Connecting(null, null),
- TunnelState.Disconnecting(mockk()),
- TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, false)),
- )
- .map(Event::TunnelStateChange)
+ fun `tunnel is connected should emit false`() =
+ scope.runTest {
+ // Arrange
+ val expiredAccountExpiry = AccountExpiry.Available(DateTime.now().plusDays(1))
+ val tunnelStateChanges =
+ listOf(
+ TunnelState.Disconnected(),
+ TunnelState.Connected(mockk(), null),
+ TunnelState.Connecting(null, null),
+ TunnelState.Disconnecting(mockk()),
+ TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, false)),
+ )
+ .map(Event::TunnelStateChange)
- // Act, Assert
- outOfTimeUseCase.isOutOfTime().test {
- assertEquals(null, awaitItem())
- events.emit(tunnelStateChanges.first())
- expiry.emit(expiredAccountExpiry)
- assertEquals(false, awaitItem())
+ // Act, Assert
+ outOfTimeUseCase.isOutOfTime.test {
+ assertEquals(null, awaitItem())
+ events.send(tunnelStateChanges.first())
+ expiry.emit(expiredAccountExpiry)
+ assertEquals(false, awaitItem())
- tunnelStateChanges.forEach { events.emit(it) }
+ tunnelStateChanges.forEach { events.send(it) }
- // Should not emit again
- expectNoEvents()
+ // Should not emit again
+ expectNoEvents()
+ }
}
- }
@Test
- fun `account expiry that has expired should emit true`() = runTest {
- // Arrange
- val expiredAccountExpiry = AccountExpiry.Available(DateTime.now().minusDays(1))
- // Act, Assert
- outOfTimeUseCase.isOutOfTime().test {
- assertEquals(null, awaitItem())
- expiry.emit(expiredAccountExpiry)
- assertEquals(true, awaitItem())
+ fun `account expiry that has expired should emit true`() =
+ scope.runTest {
+ // Arrange
+ val expiredAccountExpiry = AccountExpiry.Available(DateTime.now().minusDays(1))
+ // Act, Assert
+ outOfTimeUseCase.isOutOfTime.test {
+ assertEquals(null, awaitItem())
+ expiry.emit(expiredAccountExpiry)
+ assertEquals(true, awaitItem())
+ }
+ }
+
+ @Test
+ fun `account expiry that has not expired should emit false`() =
+ scope.runTest {
+ // Arrange
+ val notExpiredAccountExpiry = AccountExpiry.Available(DateTime.now().plusDays(1))
+
+ // Act, Assert
+ outOfTimeUseCase.isOutOfTime.test {
+ assertEquals(null, awaitItem())
+ expiry.emit(notExpiredAccountExpiry)
+ assertEquals(false, awaitItem())
+ }
}
- }
@Test
- fun `account expiry that has not expired should emit false`() = runTest {
+ fun `account that expires without new expiry event should emit true`() =
+ runTest(dispatcher) {
+ // Arrange
+ val expiredAccountExpiry = AccountExpiry.Available(DateTime.now().plusSeconds(100))
+ // Act, Assert
+ outOfTimeUseCase.isOutOfTime.test {
+ // Initial event
+ assertEquals(null, awaitItem())
+
+ expiry.emit(expiredAccountExpiry)
+ assertEquals(false, awaitItem())
+
+ // After 50 seconds we should still not emitted out of time
+ advanceTimeBy(50_000)
+ expectNoEvents()
+
+ // After additional 50 seconds we should be out of time since account is now expired
+ advanceTimeBy(50_000)
+ assertEquals(true, awaitItem())
+ }
+ }
+
+ @Test
+ fun `account that is about to expire but is refilled should emit false`() = runTest {
// Arrange
- val expiredAccountExpiry = AccountExpiry.Available(DateTime.now().plusDays(1))
+ val initialAccountExpiry = AccountExpiry.Available(DateTime.now().plusSeconds(100))
+ val updatedExpiry =
+ AccountExpiry.Available(initialAccountExpiry.expiryDateTime.plusDays(30))
// Act, Assert
- outOfTimeUseCase.isOutOfTime().test {
+ outOfTimeUseCase.isOutOfTime.test {
+ // Initial event
assertEquals(null, awaitItem())
- expiry.emit(expiredAccountExpiry)
+
+ expiry.emit(initialAccountExpiry)
assertEquals(false, awaitItem())
+ advanceTimeBy(90_000)
+ expectNoEvents()
+
+ // User fills up with more time 30 seconds before expiry
+ expiry.emit(updatedExpiry)
+ advanceTimeBy(1.days)
+ expectNoEvents()
+
+ // Expect no more emissions while user has time.
+ advanceTimeBy(29.days)
+ assertEquals(true, awaitItem())
+ expectNoEvents()
}
}
@Test
- fun `account that expires without new expiry event should emit true`() = runTest {
+ fun `expired account that is refilled should emit false`() = runTest {
// Arrange
- val expiredAccountExpiry = AccountExpiry.Available(DateTime.now().plusSeconds(62))
-
+ val initialAccountExpiry = AccountExpiry.Available(DateTime.now().plusSeconds(100))
+ val updatedExpiry =
+ AccountExpiry.Available(initialAccountExpiry.expiryDateTime.plusDays(30))
// Act, Assert
- outOfTimeUseCase.isOutOfTime().test {
+ outOfTimeUseCase.isOutOfTime.test {
// Initial event
assertEquals(null, awaitItem())
- expiry.emit(expiredAccountExpiry)
+ expiry.emit(initialAccountExpiry)
assertEquals(false, awaitItem())
+
+ // After 100 seconds we expire
+ advanceTimeBy(100_000)
assertEquals(true, awaitItem())
+ expectNoEvents()
+
+ // We then fill up our account and should no longer be out of time
+ expiry.emit(updatedExpiry)
+ assertEquals(false, awaitItem())
+ expectNoEvents()
+
+ // Advance the time to the updated expiry
+ advanceTimeBy(30.days)
+ assertEquals(true, awaitItem())
+ expectNoEvents()
}
}
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt
index 545422b6f2..7e207a15a4 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt
@@ -132,7 +132,7 @@ class ConnectViewModelTest {
// Flows
every { mockRelayListUseCase.selectedRelayItem() } returns selectedRelayItemFlow
- every { outOfTimeUseCase.isOutOfTime() } returns outOfTimeViewFlow
+ every { outOfTimeUseCase.isOutOfTime } returns outOfTimeViewFlow
viewModel =
ConnectViewModel(
serviceConnectionManager = mockServiceConnectionManager,
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt
index a5171b2ea6..e489c01d41 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt
@@ -89,7 +89,7 @@ class OutOfTimeViewModelTest {
coEvery { mockPaymentUseCase.paymentAvailability } returns paymentAvailabilityFlow
- coEvery { mockOutOfTimeUseCase.isOutOfTime() } returns outOfTimeFlow
+ coEvery { mockOutOfTimeUseCase.isOutOfTime } returns outOfTimeFlow
viewModel =
OutOfTimeViewModel(
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt
index 74ea210a60..91554193bc 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/WelcomeViewModelTest.kt
@@ -32,11 +32,9 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
import net.mullvad.mullvadvpn.ui.serviceconnection.authTokenCache
-import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
import net.mullvad.talpid.util.EventNotifier
import org.joda.time.DateTime
-import org.joda.time.ReadableInstant
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -51,7 +49,6 @@ class WelcomeViewModelTest {
private val accountExpiryStateFlow = MutableStateFlow<AccountExpiry>(AccountExpiry.Missing)
private val purchaseResultFlow = MutableStateFlow<PurchaseResult?>(null)
private val paymentAvailabilityFlow = MutableStateFlow<PaymentAvailability?>(null)
- private val outOfTimeFlow = MutableStateFlow(true)
// Service connections
private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk()
@@ -64,7 +61,6 @@ class WelcomeViewModelTest {
private val mockDeviceRepository: DeviceRepository = mockk()
private val mockServiceConnectionManager: ServiceConnectionManager = mockk()
private val mockPaymentUseCase: PaymentUseCase = mockk(relaxed = true)
- private val mockOutOfTimeUseCase: OutOfTimeUseCase = mockk(relaxed = true)
private lateinit var viewModel: WelcomeViewModel
@@ -87,15 +83,12 @@ class WelcomeViewModelTest {
coEvery { mockPaymentUseCase.paymentAvailability } returns paymentAvailabilityFlow
- coEvery { mockOutOfTimeUseCase.isOutOfTime() } returns outOfTimeFlow
-
viewModel =
WelcomeViewModel(
accountRepository = mockAccountRepository,
deviceRepository = mockDeviceRepository,
serviceConnectionManager = mockServiceConnectionManager,
paymentUseCase = mockPaymentUseCase,
- outOfTimeUseCase = mockOutOfTimeUseCase,
pollAccountExpiry = false,
isPlayBuild = false
)
@@ -164,19 +157,16 @@ class WelcomeViewModelTest {
}
@Test
- fun `when OutOfTimeUseCase return false uiSideEffect should emit OpenConnectScreen`() =
- runTest {
- // Arrange
- val mockExpiryDate: DateTime = mockk()
- every { mockExpiryDate.isAfter(any<ReadableInstant>()) } returns true
+ fun `when user has added time then uiSideEffect should emit OpenConnectScreen`() = runTest {
+ // Arrange
+ accountExpiryStateFlow.emit(AccountExpiry.Available(DateTime().plusDays(1)))
- // Act, Assert
- viewModel.uiSideEffect.test {
- outOfTimeFlow.value = false
- val action = awaitItem()
- assertIs<WelcomeViewModel.UiSideEffect.OpenConnectScreen>(action)
- }
+ // Act, Assert
+ viewModel.uiSideEffect.test {
+ val action = awaitItem()
+ assertIs<WelcomeViewModel.UiSideEffect.OpenConnectScreen>(action)
}
+ }
@Test
fun `when paymentAvailability emits ProductsUnavailable uiState should include state NoPayment`() =