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 5839e575c19e..ea239b7cbd04 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 @@ -34,12 +34,12 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoCache import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy import net.mullvad.mullvadvpn.ui.serviceconnection.LocationInfoCache -import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener 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.ui.serviceconnection.connectionProxy +import net.mullvad.mullvadvpn.usecase.RelayListUseCase import net.mullvad.mullvadvpn.util.appVersionCallbackFlow import net.mullvad.talpid.tunnel.ErrorState import net.mullvad.talpid.tunnel.ErrorStateCause @@ -73,7 +73,6 @@ class ConnectViewModelTest { // Service connections private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk() private val mockLocationInfoCache: LocationInfoCache = mockk(relaxUnitFun = true) - private val mockRelayListListener: RelayListListener = mockk(relaxUnitFun = true) private lateinit var mockAppVersionInfoCache: AppVersionInfoCache private val mockConnectionProxy: ConnectionProxy = mockk() private val mockLocation: GeoIpLocation = mockk(relaxed = true) @@ -87,14 +86,20 @@ class ConnectViewModelTest { // In App Notifications private val mockInAppNotificationController: InAppNotificationController = mockk() + // Relay list use case + private val mockRelayListUseCase: RelayListUseCase = mockk() + // Captures private val locationSlot = slot<((GeoIpLocation?) -> Unit)>() - private val relaySlot = slot<(List, RelayItem?) -> Unit>() // Event notifiers private val eventNotifierTunnelUiState = EventNotifier(TunnelState.Disconnected) private val eventNotifierTunnelRealState = EventNotifier(TunnelState.Disconnected) + // Flows + private val relayListWithSelectionFlow = + MutableStateFlow, RelayItem?>>(Pair(emptyList(), null)) + @Before fun setup() { mockkStatic(CACHE_EXTENSION_CLASS) @@ -107,7 +112,6 @@ class ConnectViewModelTest { every { mockServiceConnectionManager.connectionState } returns serviceConnectionState every { mockServiceConnectionContainer.locationInfoCache } returns mockLocationInfoCache - every { mockServiceConnectionContainer.relayListListener } returns mockRelayListListener every { mockServiceConnectionContainer.appVersionInfoCache } returns mockAppVersionInfoCache every { mockServiceConnectionContainer.connectionProxy } returns mockConnectionProxy @@ -124,15 +128,18 @@ class ConnectViewModelTest { // Listeners every { mockLocationInfoCache.onNewLocation = capture(locationSlot) } answers {} - every { mockRelayListListener.onRelayCountriesChange = capture(relaySlot) } answers {} every { mockAppVersionInfoCache.onUpdate = any() } answers {} + // Flows + every { mockRelayListUseCase.relayListWithSelection() } returns relayListWithSelectionFlow + viewModel = ConnectViewModel( serviceConnectionManager = mockServiceConnectionManager, accountRepository = mockAccountRepository, deviceRepository = mockDeviceRepository, inAppNotificationController = mockInAppNotificationController, + relayListUseCase = mockRelayListUseCase, newDeviceNotificationUseCase = mockk() ) } @@ -156,7 +163,6 @@ class ConnectViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(mockLocation) - relaySlot.captured.invoke(mockk(), mockk()) viewModel.toggleTunnelInfoExpansion() val result = awaitItem() assertTrue(result.isTunnelInfoExpanded) @@ -173,7 +179,6 @@ class ConnectViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(mockLocation) - relaySlot.captured.invoke(mockk(), mockk()) eventNotifierTunnelRealState.notify(tunnelRealStateTestItem) val result = awaitItem() assertEquals(tunnelRealStateTestItem, result.tunnelRealState) @@ -190,7 +195,6 @@ class ConnectViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(mockLocation) - relaySlot.captured.invoke(mockk(), mockk()) eventNotifierTunnelUiState.notify(tunnelUiStateTestItem) val result = awaitItem() assertEquals(tunnelUiStateTestItem, result.tunnelUiState) @@ -202,13 +206,13 @@ class ConnectViewModelTest { runTest(testCoroutineRule.testDispatcher) { val relayTestItem = RelayCountry(name = "Name", code = "Code", expanded = false, cities = emptyList()) + relayListWithSelectionFlow.value = emptyList() to relayTestItem viewModel.uiState.test { assertEquals(ConnectUiState.INITIAL, awaitItem()) serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(mockLocation) - relaySlot.captured.invoke(mockk(), relayTestItem) val result = awaitItem() assertEquals(relayTestItem, result.relayLocation) } @@ -231,7 +235,6 @@ class ConnectViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(locationTestItem) - relaySlot.captured.invoke(mockk(), mockk()) val result = awaitItem() assertEquals(locationTestItem, result.location) } @@ -249,7 +252,6 @@ class ConnectViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(locationTestItem) - relaySlot.captured.invoke(mockk(), mockk()) expectNoEvents() val result = awaitItem() assertEquals(locationTestItem, result.location) @@ -308,7 +310,6 @@ class ConnectViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(mockLocation) - relaySlot.captured.invoke(mockk(), mockk()) eventNotifierTunnelUiState.notify(tunnelUiState) val result = awaitItem() assertEquals(expectedConnectNotificationState, result.inAppNotification) @@ -347,7 +348,6 @@ class ConnectViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) locationSlot.captured.invoke(mockLocation) - relaySlot.captured.invoke(mockk(), mockk()) eventNotifierTunnelRealState.notify(tunnelRealStateTestItem) awaitItem() } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt index 3cadfe575fb1..e72635d0f497 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt @@ -5,7 +5,6 @@ import app.cash.turbine.test import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic -import io.mockk.slot import io.mockk.unmockkAll import io.mockk.verify import kotlin.test.assertEquals @@ -21,12 +20,9 @@ import net.mullvad.mullvadvpn.relaylist.RelayCountry import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.filterOnSearchTerm import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy -import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener -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.connectionProxy -import net.mullvad.mullvadvpn.ui.serviceconnection.relayListListener +import net.mullvad.mullvadvpn.usecase.RelayListUseCase import org.junit.After import org.junit.Before import org.junit.Rule @@ -38,28 +34,19 @@ class SelectLocationViewModelTest { private val mockServiceConnectionManager: ServiceConnectionManager = mockk() private lateinit var viewModel: SelectLocationViewModel - // Service connections - private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk() - private val mockRelayListListener: RelayListListener = mockk(relaxUnitFun = true) + private val relayListWithSelectionFlow = + MutableStateFlow, RelayItem?>>(Pair(emptyList(), null)) - // Captures - private val relaySlot = slot<(List, RelayItem?) -> Unit>() - - private val serviceConnectionState = - MutableStateFlow(ServiceConnectionState.Disconnected) + private val mockRelayListUseCase: RelayListUseCase = mockk() @Before fun setup() { - every { mockServiceConnectionManager.connectionState } returns serviceConnectionState - every { mockServiceConnectionContainer.relayListListener } returns mockRelayListListener - - every { mockRelayListListener.onRelayCountriesChange = capture(relaySlot) } answers {} - every { mockRelayListListener.onRelayCountriesChange = null } answers {} + every { mockRelayListUseCase.relayListWithSelection() } returns relayListWithSelectionFlow mockkStatic(SERVICE_CONNECTION_MANAGER_EXTENSIONS) mockkStatic(RELAY_LIST_EXTENSIONS) - viewModel = SelectLocationViewModel(mockServiceConnectionManager) + viewModel = SelectLocationViewModel(mockServiceConnectionManager, mockRelayListUseCase) } @After @@ -70,7 +57,7 @@ class SelectLocationViewModelTest { @Test fun testInitialState() = runTest { - viewModel.uiState.test { assertEquals(SelectLocationUiState.Loading, awaitItem()) } + assertEquals(SelectLocationUiState.Loading, viewModel.uiState.value) } @Test @@ -79,14 +66,10 @@ class SelectLocationViewModelTest { val mockCountries = listOf(mockk(), mockk()) val selectedRelay: RelayItem = mockk() every { mockCountries.filterOnSearchTerm(any(), selectedRelay) } returns mockCountries + relayListWithSelectionFlow.value = mockCountries to selectedRelay // Act, Assert viewModel.uiState.test { - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - relaySlot.captured.invoke(mockCountries, selectedRelay) - - assertEquals(SelectLocationUiState.Loading, awaitItem()) val actualState = awaitItem() assertIs(actualState) assertLists(mockCountries, actualState.countries) @@ -100,14 +83,10 @@ class SelectLocationViewModelTest { val mockCountries = listOf(mockk(), mockk()) val selectedRelay: RelayItem? = null every { mockCountries.filterOnSearchTerm(any(), selectedRelay) } returns mockCountries + relayListWithSelectionFlow.value = mockCountries to selectedRelay // Act, Assert viewModel.uiState.test { - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - relaySlot.captured.invoke(mockCountries, selectedRelay) - - assertEquals(SelectLocationUiState.Loading, awaitItem()) val actualState = awaitItem() assertIs(actualState) assertLists(mockCountries, actualState.countries) @@ -122,8 +101,8 @@ class SelectLocationViewModelTest { val mockLocation: GeographicLocationConstraint.Country = mockk(relaxed = true) val connectionProxyMock: ConnectionProxy = mockk(relaxUnitFun = true) every { mockRelayItem.location } returns mockLocation - every { mockServiceConnectionManager.relayListListener() } returns mockRelayListListener every { mockServiceConnectionManager.connectionProxy() } returns connectionProxyMock + every { mockRelayListUseCase.updateSelectedRelayLocation(mockLocation) } returns Unit // Act, Assert viewModel.uiCloseAction.test { @@ -132,7 +111,7 @@ class SelectLocationViewModelTest { assertEquals(Unit, awaitItem()) verify { connectionProxyMock.connect() - mockRelayListListener.updateSelectedRelayLocation(mockLocation) + mockRelayListUseCase.updateSelectedRelayLocation(mockLocation) } } } @@ -146,15 +125,10 @@ class SelectLocationViewModelTest { val mockSearchString = "SEARCH" every { mockRelayList.filterOnSearchTerm(mockSearchString, selectedRelay) } returns mockCountries + relayListWithSelectionFlow.value = mockRelayList to selectedRelay // Act, Assert viewModel.uiState.test { - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - relaySlot.captured.invoke(mockRelayList, selectedRelay) - - // Wait for loading - assertEquals(SelectLocationUiState.Loading, awaitItem()) // Wait for first data assertIs(awaitItem()) @@ -178,15 +152,10 @@ class SelectLocationViewModelTest { val mockSearchString = "SEARCH" every { mockRelayList.filterOnSearchTerm(mockSearchString, selectedRelay) } returns mockCountries + relayListWithSelectionFlow.value = mockRelayList to selectedRelay // Act, Assert viewModel.uiState.test { - serviceConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - relaySlot.captured.invoke(mockRelayList, selectedRelay) - - // Wait for loading - assertEquals(SelectLocationUiState.Loading, awaitItem()) // Wait for first data assertIs(awaitItem()) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt index 13561737c830..f8736eb823ea 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.viewModelScope import app.cash.turbine.test import io.mockk.every import io.mockk.mockk -import io.mockk.slot import io.mockk.unmockkAll import io.mockk.verify import kotlin.test.assertEquals @@ -30,10 +29,8 @@ import net.mullvad.mullvadvpn.model.TunnelOptions import net.mullvad.mullvadvpn.model.WireguardConstraints import net.mullvad.mullvadvpn.model.WireguardTunnelOptions import net.mullvad.mullvadvpn.repository.SettingsRepository -import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener -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.usecase.PortRangeUseCase +import net.mullvad.mullvadvpn.usecase.RelayListUseCase import org.apache.commons.validator.routines.InetAddressValidator import org.junit.After import org.junit.Before @@ -46,34 +43,26 @@ class VpnSettingsViewModelTest { private val mockSettingsRepository: SettingsRepository = mockk() private val mockInetAddressValidator: InetAddressValidator = mockk() private val mockResources: Resources = mockk() - private val mockServiceConnectionManager: ServiceConnectionManager = mockk() - - private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk() - private val mockRelayListListener: RelayListListener = mockk() - private val portRangeSlot = slot<(List) -> Unit>() + private val mockPortRangeUseCase: PortRangeUseCase = mockk() + private val mockRelayListUseCase: RelayListUseCase = mockk() private val mockSettingsUpdate = MutableStateFlow(null) - private val mockConnectionState = - MutableStateFlow(ServiceConnectionState.Disconnected) + private val portRangeFlow = MutableStateFlow(emptyList()) private lateinit var viewModel: VpnSettingsViewModel @Before fun setUp() { every { mockSettingsRepository.settingsUpdates } returns mockSettingsUpdate - every { mockServiceConnectionManager.connectionState } returns mockConnectionState - - every { mockServiceConnectionContainer.relayListListener } returns mockRelayListListener - - every { mockRelayListListener.onPortRangesChange = capture(portRangeSlot) } answers {} - every { mockRelayListListener.onPortRangesChange = null } answers {} + every { mockPortRangeUseCase.portRanges() } returns portRangeFlow viewModel = VpnSettingsViewModel( repository = mockSettingsRepository, inetAddressValidator = mockInetAddressValidator, resources = mockResources, - serviceConnectionManager = mockServiceConnectionManager, + portRangeUseCase = mockPortRangeUseCase, + relayListUseCase = mockRelayListUseCase, dispatcher = UnconfinedTestDispatcher() ) } @@ -122,9 +111,6 @@ class VpnSettingsViewModelTest { viewModel.uiState.test { assertEquals(defaultResistantState, awaitItem().quantumResistant) mockSettingsUpdate.value = mockSettings - mockConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - portRangeSlot.captured.invoke(emptyList()) assertEquals(expectedResistantState, awaitItem().quantumResistant) } } @@ -147,9 +133,6 @@ class VpnSettingsViewModelTest { viewModel.uiState.test { assertIs>(awaitItem().selectedWireguardPort) mockSettingsUpdate.value = mockSettings - mockConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - portRangeSlot.captured.invoke(emptyList()) assertEquals(expectedPort, awaitItem().selectedWireguardPort) } } @@ -159,18 +142,14 @@ class VpnSettingsViewModelTest { // Arrange val wireguardPort: Constraint = Constraint.Only(Port(99)) val wireguardConstraints = WireguardConstraints(port = wireguardPort) - every { - mockRelayListListener.updateSelectedWireguardConstraints(wireguardConstraints) - } returns Unit + every { mockRelayListUseCase.updateSelectedWireguardConstraints(any()) } returns Unit // Act - mockConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) viewModel.onWireguardPortSelected(wireguardPort) // Assert verify(exactly = 1) { - mockRelayListListener.updateSelectedWireguardConstraints(wireguardConstraints) + mockRelayListUseCase.updateSelectedWireguardConstraints(wireguardConstraints) } } @@ -181,15 +160,12 @@ class VpnSettingsViewModelTest { val mockSettings: Settings = mockk(relaxed = true) every { mockSettings.relaySettings } returns mockk(relaxed = true) + portRangeFlow.value = expectedPortRange // Act, Assert viewModel.uiState.test { assertIs(awaitItem()) - mockSettingsUpdate.value = mockSettings viewModel.onWireguardPortInfoClicked() - mockConnectionState.value = - ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) - portRangeSlot.captured.invoke(expectedPortRange) val state = awaitItem() assertTrue { state.dialog is VpnSettingsDialog.WireguardPortInfo } assertLists(expectedPortRange, state.availablePortRanges)