diff options
| author | Kalle Lindström <karl.lindstrom@mullvad.net> | 2025-07-22 14:26:22 +0200 |
|---|---|---|
| committer | Kalle Lindström <karl.lindstrom@mullvad.net> | 2025-07-22 14:26:22 +0200 |
| commit | b2fc803af349205bc40d7cd00e0a480536c3d09e (patch) | |
| tree | d603241a7e9ed6284f89704140f02c1a828518cb /android/app/src/test | |
| parent | 75501a665b1bb7257cacd79f1eca84c839929725 (diff) | |
| parent | 526ecbf7d85c8abe7af08daf04dc4bc0c6df109c (diff) | |
| download | mullvadvpn-b2fc803af349205bc40d7cd00e0a480536c3d09e.tar.xz mullvadvpn-b2fc803af349205bc40d7cd00e0a480536c3d09e.zip | |
Merge branch 'implement-recents-support-ui'
Diffstat (limited to 'android/app/src/test')
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) |
