summaryrefslogtreecommitdiffhomepage
path: root/android/app/src/test
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2025-07-22 14:26:22 +0200
committerKalle Lindström <karl.lindstrom@mullvad.net>2025-07-22 14:26:22 +0200
commitb2fc803af349205bc40d7cd00e0a480536c3d09e (patch)
treed603241a7e9ed6284f89704140f02c1a828518cb /android/app/src/test
parent75501a665b1bb7257cacd79f1eca84c839929725 (diff)
parent526ecbf7d85c8abe7af08daf04dc4bc0c6df109c (diff)
downloadmullvadvpn-b2fc803af349205bc40d7cd00e0a480536c3d09e.tar.xz
mullvadvpn-b2fc803af349205bc40d7cd00e0a480536c3d09e.zip
Merge branch 'implement-recents-support-ui'
Diffstat (limited to 'android/app/src/test')
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCaseTest.kt139
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModelTest.kt22
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModelTest.kt5
3 files changed, 158 insertions, 8 deletions
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCaseTest.kt
new file mode 100644
index 0000000000..f7699101d5
--- /dev/null
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCaseTest.kt
@@ -0,0 +1,139 @@
+package net.mullvad.mullvadvpn.usecase
+
+import app.cash.turbine.test
+import io.mockk.every
+import io.mockk.mockk
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import net.mullvad.mullvadvpn.compose.state.RelayListType
+import net.mullvad.mullvadvpn.lib.model.CustomList
+import net.mullvad.mullvadvpn.lib.model.CustomListId
+import net.mullvad.mullvadvpn.lib.model.CustomListName
+import net.mullvad.mullvadvpn.lib.model.GeoLocationId
+import net.mullvad.mullvadvpn.lib.model.Hop
+import net.mullvad.mullvadvpn.lib.model.Recent
+import net.mullvad.mullvadvpn.lib.model.Recents
+import net.mullvad.mullvadvpn.lib.model.RelayItem
+import net.mullvad.mullvadvpn.lib.model.Settings
+import net.mullvad.mullvadvpn.repository.SettingsRepository
+import net.mullvad.mullvadvpn.usecase.customlists.FilterCustomListsRelayItemUseCase
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+
+class RecentsUseCaseTest {
+
+ private val customListsRelayItemUseCase: FilterCustomListsRelayItemUseCase = mockk()
+ private val filteredRelayListUseCase: FilteredRelayListUseCase = mockk()
+ private val settingsRepository: SettingsRepository = mockk()
+
+ private val settingsFlow = MutableStateFlow<Settings?>(null)
+
+ private lateinit var useCase: RecentsUseCase
+
+ @BeforeEach
+ fun setUp() {
+ every { settingsRepository.settingsUpdates } returns settingsFlow
+ useCase =
+ RecentsUseCase(
+ customListsRelayItemUseCase,
+ filteredRelayListUseCase,
+ settingsRepository,
+ )
+ }
+
+ @Test
+ fun `given null settings when invoke then emit null`() = runTest {
+ settingsFlow.value = null
+ every { customListsRelayItemUseCase(any()) } returns flowOf(emptyList())
+ every { filteredRelayListUseCase(any()) } returns flowOf(emptyList())
+
+ useCase().test { assertNull(awaitItem()) }
+ }
+
+ @Test
+ fun `given recents disabled when invoke then emit null`() = runTest {
+ settingsFlow.value = mockk<Settings> { every { recents } returns Recents.Disabled }
+ every { customListsRelayItemUseCase(any()) } returns flowOf(emptyList())
+ every { filteredRelayListUseCase(any()) } returns flowOf(emptyList())
+
+ useCase().test { assertNull(awaitItem()) }
+ }
+
+ @Test
+ fun `given recents enabled but empty when invoke then emit empty list`() = runTest {
+ settingsFlow.value =
+ mockk<Settings> { every { recents } returns Recents.Enabled(emptyList()) }
+ every { customListsRelayItemUseCase(any()) } returns flowOf(emptyList())
+ every { filteredRelayListUseCase(any()) } returns flowOf(emptyList())
+
+ useCase().test { assertEquals(emptyList(), awaitItem()) }
+ }
+
+ @Test
+ fun `given recents enabled when invoke then emit hops based on the relay item filters`() =
+ runTest {
+ val swedenId = GeoLocationId.Country("se")
+ val stockholmId = GeoLocationId.City(swedenId, "sto")
+ val sweden =
+ RelayItem.Location.Country(
+ id = swedenId,
+ name = "Sweden",
+ cities =
+ listOf(
+ RelayItem.Location.City(
+ id = stockholmId,
+ name = "Stockholm",
+ relays = emptyList(),
+ )
+ ),
+ )
+
+ val norwayId = GeoLocationId.Country("no")
+ val norway =
+ RelayItem.Location.Country(id = norwayId, name = "Norway", cities = emptyList())
+
+ val entryCustomListId = CustomListId("custom")
+ val customList =
+ CustomList(
+ id = entryCustomListId,
+ name = CustomListName.fromString("Custom"),
+ locations = listOf(swedenId, norwayId),
+ )
+ val entryCustomList =
+ RelayItem.CustomList(customList = customList, locations = emptyList())
+
+ val singleHopRecent = Recent.Singlehop(stockholmId)
+ val multiHopRecent = Recent.Multihop(entry = entryCustomListId, exit = norwayId)
+ val filteredOutRecent =
+ Recent.Singlehop(
+ GeoLocationId.City(country = GeoLocationId.Country("xx"), code = "xx-xxx-xx")
+ )
+
+ settingsFlow.value =
+ mockk<Settings> {
+ every { recents } returns
+ Recents.Enabled(listOf(singleHopRecent, multiHopRecent, filteredOutRecent))
+ }
+
+ every { customListsRelayItemUseCase(RelayListType.ENTRY) } returns
+ flowOf(listOf(entryCustomList))
+ every { customListsRelayItemUseCase(RelayListType.EXIT) } returns flowOf(emptyList())
+ every { filteredRelayListUseCase(RelayListType.ENTRY) } returns
+ flowOf(listOf(sweden, norway))
+ every { filteredRelayListUseCase(RelayListType.EXIT) } returns
+ flowOf(listOf(sweden, norway))
+
+ useCase().test {
+ val hops = awaitItem()
+
+ val stockholmCity = sweden.cities.first()
+
+ val expectedHops =
+ listOf(Hop.Single(stockholmCity), Hop.Multi(entryCustomList, norway))
+ assertEquals(expectedHops, hops)
+ }
+ }
+}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModelTest.kt
index fb974e52fb..1a54d15f95 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModelTest.kt
@@ -12,6 +12,7 @@ import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.common.test.assertLists
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.GeoLocationId
+import net.mullvad.mullvadvpn.lib.model.Hop
import net.mullvad.mullvadvpn.lib.model.RelayItem
import net.mullvad.mullvadvpn.lib.model.RelayItemSelection
import net.mullvad.mullvadvpn.lib.model.Settings
@@ -20,6 +21,7 @@ import net.mullvad.mullvadvpn.repository.RelayListRepository
import net.mullvad.mullvadvpn.repository.SettingsRepository
import net.mullvad.mullvadvpn.repository.WireguardConstraintsRepository
import net.mullvad.mullvadvpn.usecase.FilteredRelayListUseCase
+import net.mullvad.mullvadvpn.usecase.RecentsUseCase
import net.mullvad.mullvadvpn.usecase.SelectedLocationUseCase
import net.mullvad.mullvadvpn.usecase.customlists.CustomListsRelayItemUseCase
import net.mullvad.mullvadvpn.usecase.customlists.FilterCustomListsRelayItemUseCase
@@ -40,12 +42,14 @@ class SelectLocationListViewModelTest {
private val mockRelayListRepository: RelayListRepository = mockk()
private val mockCustomListRelayItemsUseCase: CustomListsRelayItemUseCase = mockk()
private val mockSettingsRepository: SettingsRepository = mockk()
+ private val recentsUseCase: RecentsUseCase = mockk()
private val filteredRelayList = MutableStateFlow<List<RelayItem.Location.Country>>(emptyList())
private val selectedLocationFlow = MutableStateFlow<RelayItemSelection>(mockk(relaxed = true))
private val filteredCustomListRelayItems =
MutableStateFlow<List<RelayItem.CustomList>>(emptyList())
private val customListRelayItems = MutableStateFlow<List<RelayItem.CustomList>>(emptyList())
+ private val recentsRelayItems = MutableStateFlow<List<Hop>?>(emptyList())
private val settings = MutableStateFlow(mockk<Settings>(relaxed = true))
private lateinit var viewModel: SelectLocationListViewModel
@@ -63,6 +67,7 @@ class SelectLocationListViewModelTest {
filteredCustomListRelayItems
every { mockCustomListRelayItemsUseCase() } returns customListRelayItems
every { mockSettingsRepository.settingsUpdates } returns settings
+ every { recentsUseCase() } returns recentsRelayItems
}
@Test
@@ -132,18 +137,23 @@ class SelectLocationListViewModelTest {
relayListRepository = mockRelayListRepository,
customListsRelayItemUseCase = mockCustomListRelayItemsUseCase,
settingsRepository = mockSettingsRepository,
+ recentsUseCase = recentsUseCase,
)
private fun RelayListItem.relayItemId() =
when (this) {
- is RelayListItem.CustomListFooter -> null
- RelayListItem.CustomListHeader -> null
- RelayListItem.LocationHeader -> null
- is RelayListItem.LocationsEmptyText -> null
- is RelayListItem.EmptyRelayList -> null
is RelayListItem.CustomListEntryItem -> item.id
- is RelayListItem.CustomListItem -> item.id
+ is RelayListItem.CustomListItem -> hop.exit().id
is RelayListItem.GeoLocationItem -> item.id
+ is RelayListItem.RecentListItem -> hop.exit().id
+ is RelayListItem.CustomListFooter,
+ is RelayListItem.LocationsEmptyText,
+ is RelayListItem.EmptyRelayList,
+ is RelayListItem.SectionDivider,
+ RelayListItem.CustomListHeader,
+ RelayListItem.LocationHeader,
+ RelayListItem.RecentsListHeader,
+ RelayListItem.RecentsListFooter -> null
}
companion object {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModelTest.kt
index 7115cd58c0..3f80572e06 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModelTest.kt
@@ -25,6 +25,7 @@ import net.mullvad.mullvadvpn.lib.model.CustomList
import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.CustomListName
import net.mullvad.mullvadvpn.lib.model.GeoLocationId
+import net.mullvad.mullvadvpn.lib.model.Hop
import net.mullvad.mullvadvpn.lib.model.Ownership
import net.mullvad.mullvadvpn.lib.model.Providers
import net.mullvad.mullvadvpn.lib.model.RelayItem
@@ -114,7 +115,7 @@ class SelectLocationViewModelTest {
// Act, Assert
viewModel.uiSideEffect.test {
- viewModel.selectRelay(mockRelayItem)
+ viewModel.selectHop(Hop.Single(mockRelayItem))
// Await an empty item
assertEquals(SelectLocationSideEffect.CloseScreen, awaitItem())
coVerify { mockRelayListRepository.updateSelectedRelayLocation(relayItemId) }
@@ -141,7 +142,7 @@ class SelectLocationViewModelTest {
assertIs<Lc.Content<SelectLocationUiState>>(firstState)
assertEquals(RelayListType.ENTRY, firstState.value.relayListType)
// Select entry
- viewModel.selectRelay(mockRelayItem)
+ viewModel.selectHop(Hop.Single(mockRelayItem))
// Assert relay list type is exit
val secondState = awaitItem()
assertIs<Lc.Content<SelectLocationUiState>>(secondState)