summaryrefslogtreecommitdiffhomepage
path: root/android/app/src/test
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2024-09-30 18:04:57 +0200
committerDavid Göransson <david.goransson@mullvad.net>2024-10-07 08:34:52 +0200
commit1fe033aabeb67925955906dabb044c2622d2ccd6 (patch)
treef51c3686beb5bc87d8184d812a0dbdd321e44dd4 /android/app/src/test
parent6462dd1d00ea924d99deb27b1741e76e88045ec2 (diff)
downloadmullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.tar.xz
mullvadvpn-1fe033aabeb67925955906dabb044c2622d2ccd6.zip
Update in app expiry notifications over time
Diffstat (limited to 'android/app/src/test')
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt12
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt163
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt73
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt8
4 files changed, 172 insertions, 84 deletions
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
index 84ec047f06..07c6c2ed13 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
@@ -16,11 +16,11 @@ import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.ErrorState
import net.mullvad.mullvadvpn.repository.InAppNotification
import net.mullvad.mullvadvpn.repository.InAppNotificationController
-import net.mullvad.mullvadvpn.usecase.AccountExpiryNotificationUseCase
+import net.mullvad.mullvadvpn.usecase.AccountExpiryInAppNotificationUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import net.mullvad.mullvadvpn.usecase.TunnelStateNotificationUseCase
import net.mullvad.mullvadvpn.usecase.VersionNotificationUseCase
-import org.joda.time.DateTime
+import org.joda.time.Period
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -42,11 +42,11 @@ class InAppNotificationControllerTest {
fun setup() {
MockKAnnotations.init(this)
- val accountExpiryNotificationUseCase: AccountExpiryNotificationUseCase = mockk()
+ val accountExpiryInAppNotificationUseCase: AccountExpiryInAppNotificationUseCase = mockk()
val newDeviceNotificationUseCase: NewDeviceNotificationUseCase = mockk()
val versionNotificationUseCase: VersionNotificationUseCase = mockk()
val tunnelStateNotificationUseCase: TunnelStateNotificationUseCase = mockk()
- every { accountExpiryNotificationUseCase.invoke() } returns accountExpiryNotifications
+ every { accountExpiryInAppNotificationUseCase.invoke() } returns accountExpiryNotifications
every { newDeviceNotificationUseCase.invoke() } returns newDeviceNotifications
every { versionNotificationUseCase.invoke() } returns versionNotifications
every { tunnelStateNotificationUseCase.invoke() } returns tunnelStateNotifications
@@ -54,7 +54,7 @@ class InAppNotificationControllerTest {
inAppNotificationController =
InAppNotificationController(
- accountExpiryNotificationUseCase,
+ accountExpiryInAppNotificationUseCase,
newDeviceNotificationUseCase,
versionNotificationUseCase,
tunnelStateNotificationUseCase,
@@ -81,7 +81,7 @@ class InAppNotificationControllerTest {
val unsupportedVersion = InAppNotification.UnsupportedVersion(mockk())
versionNotifications.value = listOf(unsupportedVersion)
- val accountExpiry = InAppNotification.AccountExpiry(DateTime.now())
+ val accountExpiry = InAppNotification.AccountExpiry(Period.ZERO)
accountExpiryNotifications.value = listOf(accountExpiry)
inAppNotificationController.notifications.test {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt
new file mode 100644
index 0000000000..826c8c4962
--- /dev/null
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryInAppNotificationUseCaseTest.kt
@@ -0,0 +1,163 @@
+package net.mullvad.mullvadvpn.usecase
+
+import app.cash.turbine.test
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.unmockkAll
+import kotlin.math.roundToInt
+import kotlin.test.assertTrue
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
+import net.mullvad.mullvadvpn.lib.model.AccountData
+import net.mullvad.mullvadvpn.lib.shared.AccountRepository
+import net.mullvad.mullvadvpn.repository.InAppNotification
+import net.mullvad.mullvadvpn.service.notifications.accountexpiry.ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD
+import net.mullvad.mullvadvpn.service.notifications.accountexpiry.ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL
+import org.joda.time.DateTime
+import org.joda.time.Duration
+import org.joda.time.Period
+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 AccountExpiryInAppNotificationUseCaseTest {
+
+ private val accountExpiry = MutableStateFlow<AccountData?>(null)
+ private lateinit var accountExpiryInAppNotificationUseCase:
+ AccountExpiryInAppNotificationUseCase
+
+ private lateinit var notificationThreshold: DateTime
+
+ @BeforeEach
+ fun setup() {
+ MockKAnnotations.init(this)
+
+ val accountRepository = mockk<AccountRepository>()
+ every { accountRepository.accountData } returns accountExpiry
+
+ accountExpiryInAppNotificationUseCase =
+ AccountExpiryInAppNotificationUseCase(accountRepository)
+
+ notificationThreshold = DateTime.now().plus(ACCOUNT_EXPIRY_CLOSE_TO_EXPIRY_THRESHOLD)
+ }
+
+ @AfterEach
+ fun teardown() {
+ unmockkAll()
+ }
+
+ @Test
+ fun `initial state should be empty`() = runTest {
+ // Arrange, Act, Assert
+ accountExpiryInAppNotificationUseCase().test { assertTrue { awaitItem().isEmpty() } }
+ }
+
+ @Test
+ fun `account that expires within the threshold should emit a notification`() = runTest {
+ // Arrange, Act, Assert
+ accountExpiryInAppNotificationUseCase().test {
+ assertTrue { awaitItem().isEmpty() }
+ val expiry = setExpiry(notificationThreshold.minusHours(1))
+ assertExpiryNotificationAndPeriod(expiry, expectMostRecentItem())
+ expectNoEvents()
+ }
+ }
+
+ @Test
+ fun `account that expires after the threshold should not emit a notification`() = runTest {
+ // Arrange, Act, Assert
+ accountExpiryInAppNotificationUseCase().test {
+ assertTrue { awaitItem().isEmpty() }
+ setExpiry(notificationThreshold.plusDays(1))
+ expectNoEvents()
+ }
+ }
+
+ @Test
+ fun `should emit when the threshold is passed`() = runTest {
+ // Arrange, Act, Assert
+ accountExpiryInAppNotificationUseCase().test {
+ assertTrue { awaitItem().isEmpty() }
+ val expiry = setExpiry(notificationThreshold.plusMinutes(1))
+ expectNoEvents()
+
+ // Advance to before threshold
+ advanceTimeBy(59.seconds)
+ expectNoEvents()
+
+ // Advance to after threshold
+ advanceTimeBy(2.seconds)
+ assertExpiryNotificationAndPeriod(expiry, expectMostRecentItem())
+ expectNoEvents()
+ }
+ }
+
+ @Test
+ fun `should emit remaining time divided by update interval time times`() = runTest {
+ // Arrange, Act, Assert
+ accountExpiryInAppNotificationUseCase().test {
+ assertTrue { awaitItem().isEmpty() }
+
+ // Set expiry to to be 1 second before the end of the first update interval
+ val beforeUpdate =
+ notificationThreshold
+ .minus(ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL)
+ .plusSeconds(1)
+ val expiry = setExpiry(beforeUpdate)
+
+ // The expiry time is within the notification threshold so we should have an item
+ // immediately.
+ assertExpiryNotificationAndPeriod(expiry, expectMostRecentItem())
+ expectNoEvents()
+
+ val expectedEmits =
+ (Duration(DateTime.now(), beforeUpdate).millis.toDouble() /
+ ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL.millis)
+ .roundToInt()
+
+ advanceTimeBy(2.seconds)
+ repeat(expectedEmits) {
+ expectMostRecentItem()
+ advanceTimeBy(
+ ACCOUNT_EXPIRY_IN_APP_NOTIFICATION_UPDATE_INTERVAL.plus(
+ Duration.standardSeconds(1)
+ )
+ .millis
+ )
+ }
+
+ expectNoEvents()
+ }
+ }
+
+ private fun setExpiry(expiryDateTime: DateTime): DateTime {
+ val expiry = AccountData(mockk(relaxed = true), expiryDateTime)
+ accountExpiry.value = expiry
+ return expiryDateTime
+ }
+
+ // Assert that we go a single AccountExpiry notification and that the period is within
+ // the expected range (checking exact period values is not possible since we use DateTime.now)
+ private fun assertExpiryNotificationAndPeriod(
+ expiry: DateTime,
+ notifications: List<InAppNotification>,
+ ) {
+ assertTrue(notifications.size == 1, "Expected a single notification")
+ val n = notifications[0]
+ if (n !is InAppNotification.AccountExpiry) {
+ error("Expected an AccountExpiry notification")
+ }
+ val periodNow = Period(DateTime.now(), expiry)
+ assertTrue(periodNow.toStandardDuration() <= n.expiry.toStandardDuration())
+ assertTrue(
+ periodNow.toStandardDuration().plus(Duration.standardSeconds(5)) >
+ n.expiry.toStandardDuration()
+ )
+ }
+}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt
deleted file mode 100644
index f8a0e52a3e..0000000000
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/AccountExpiryNotificationUseCaseTest.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package net.mullvad.mullvadvpn.usecase
-
-import app.cash.turbine.test
-import io.mockk.MockKAnnotations
-import io.mockk.every
-import io.mockk.mockk
-import io.mockk.unmockkAll
-import kotlin.test.assertEquals
-import kotlin.test.assertTrue
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.runTest
-import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
-import net.mullvad.mullvadvpn.lib.model.AccountData
-import net.mullvad.mullvadvpn.lib.shared.AccountRepository
-import net.mullvad.mullvadvpn.repository.InAppNotification
-import org.joda.time.DateTime
-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 AccountExpiryNotificationUseCaseTest {
-
- private val accountExpiry = MutableStateFlow<AccountData?>(null)
- private lateinit var accountExpiryNotificationUseCase: AccountExpiryNotificationUseCase
-
- @BeforeEach
- fun setup() {
- MockKAnnotations.init(this)
-
- val accountRepository = mockk<AccountRepository>()
- every { accountRepository.accountData } returns accountExpiry
-
- accountExpiryNotificationUseCase = AccountExpiryNotificationUseCase(accountRepository)
- }
-
- @AfterEach
- fun teardown() {
- unmockkAll()
- }
-
- @Test
- fun `initial state should be empty`() = runTest {
- // Arrange, Act, Assert
- accountExpiryNotificationUseCase().test { assertTrue { awaitItem().isEmpty() } }
- }
-
- @Test
- fun `account that expires within 3 days should emit a notification`() = runTest {
- // Arrange, Act, Assert
- accountExpiryNotificationUseCase().test {
- assertTrue { awaitItem().isEmpty() }
- val closeToExpiry = AccountData(mockk(relaxed = true), DateTime.now().plusDays(2))
- accountExpiry.value = closeToExpiry
-
- assertEquals(
- listOf(InAppNotification.AccountExpiry(closeToExpiry.expiryDate)),
- awaitItem(),
- )
- }
- }
-
- @Test
- fun `account that expires in 4 days should not emit a notification`() = runTest {
- // Arrange, Act, Assert
- accountExpiryNotificationUseCase().test {
- assertTrue { awaitItem().isEmpty() }
- accountExpiry.value = AccountData(mockk(relaxed = true), DateTime.now().plusDays(4))
- expectNoEvents()
- }
- }
-}
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 d8438cafe0..e12c2a1d88 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
@@ -185,14 +185,12 @@ class OutOfTimeUseCaseTest {
advanceTimeBy(90.seconds)
expectNoEvents()
- // User fills up with more time 10 seconds before expiry
+ // User fills up with more time 10 seconds before expiry.
expiry.emit(updatedExpiry)
- advanceTimeBy(1.days)
+ advanceTimeBy(29.days)
expectNoEvents()
- // Expect no more emissions while user has time.
- advanceTimeBy(29.days + 2.minutes)
- println(testScheduler.currentTime)
+ advanceTimeBy(2.days)
assertEquals(true, expectMostRecentItem())
expectNoEvents()
}