test(map, intro): Refactor IntroViewModelTest and BaseMapViewModelTest to use Turbine and Mokkery

This commit is contained in:
James Rich 2026-03-18 20:08:44 -05:00
parent 835a86085f
commit 33e10fc6c1
2 changed files with 87 additions and 48 deletions

View file

@ -16,39 +16,47 @@
*/ */
package org.meshtastic.feature.intro package org.meshtastic.feature.intro
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
/** /**
* Bootstrap tests for IntroViewModel. * Bootstrap tests for IntroViewModel.
* *
* Tests the intro navigation flow logic. * Tests the intro navigation flow logic.
*/ */
class IntroViewModelTest { class IntroViewModelTest {
/*
private lateinit var viewModel: IntroViewModel
private val viewModel = IntroViewModel() @BeforeTest
fun setUp() {
viewModel = IntroViewModel()
}
@Test @Test
fun testWelcomeNavigatesNextToBluetooth() { fun testWelcomeNavigatesNextToBluetooth() {
val next = viewModel.getNextKey(Welcome, allPermissionsGranted = false) val next = viewModel.getNextKey(Welcome, allPermissionsGranted = false)
"Welcome should navigate to Bluetooth" shouldBe Bluetooth, next assertEquals(Bluetooth, next)
} }
@Test @Test
fun testBluetoothNavigatesToLocation() { fun testBluetoothNavigatesToLocation() {
val next = viewModel.getNextKey(Bluetooth, allPermissionsGranted = false) val next = viewModel.getNextKey(Bluetooth, allPermissionsGranted = false)
"Bluetooth should navigate to Location" shouldBe Location, next assertEquals(Location, next)
} }
@Test @Test
fun testLocationNavigatesToNotifications() { fun testLocationNavigatesToNotifications() {
val next = viewModel.getNextKey(Location, allPermissionsGranted = false) val next = viewModel.getNextKey(Location, allPermissionsGranted = false)
"Location should navigate to Notifications" shouldBe Notifications, next assertEquals(Notifications, next)
} }
@Test @Test
fun testNotificationsWithPermissionNavigatesToCriticalAlerts() { fun testNotificationsWithPermissionNavigatesToCriticalAlerts() {
val next = viewModel.getNextKey(Notifications, allPermissionsGranted = true) val next = viewModel.getNextKey(Notifications, allPermissionsGranted = true)
"Notifications should navigate to CriticalAlerts when permissions granted" shouldBe CriticalAlerts, next assertEquals(CriticalAlerts, next)
} }
@Test @Test
@ -62,6 +70,4 @@ class IntroViewModelTest {
val next = viewModel.getNextKey(CriticalAlerts, allPermissionsGranted = true) val next = viewModel.getNextKey(CriticalAlerts, allPermissionsGranted = true)
assertNull(next, "CriticalAlerts should not navigate further") assertNull(next, "CriticalAlerts should not navigate further")
} }
*/
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2025-2026 Meshtastic LLC * Copyright (c) 2026 Meshtastic LLC
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -12,40 +12,62 @@
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. See
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.meshtastic.feature.map package org.meshtastic.feature.map
/** import app.cash.turbine.test
* Bootstrap tests for BaseMapViewModel. import dev.mokkery.answering.returns
* import dev.mokkery.every
* Tests map functionality using FakeNodeRepository and test data. import dev.mokkery.mock
*/ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.meshtastic.core.model.ConnectionState
import org.meshtastic.core.repository.MapPrefs
import org.meshtastic.core.repository.PacketRepository
import org.meshtastic.core.testing.FakeNodeRepository
import org.meshtastic.core.testing.FakeRadioController
import org.meshtastic.core.testing.TestDataFactory
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@OptIn(ExperimentalCoroutinesApi::class)
class BaseMapViewModelTest { class BaseMapViewModelTest {
/*
private val testDispatcher = StandardTestDispatcher()
private lateinit var viewModel: BaseMapViewModel private lateinit var viewModel: BaseMapViewModel
private lateinit var nodeRepository: FakeNodeRepository private lateinit var nodeRepository: FakeNodeRepository
private lateinit var radioController: FakeRadioController private lateinit var radioController: FakeRadioController
private lateinit var mapPrefs: MapPrefs private val mapPrefs: MapPrefs = mock()
private lateinit var packetRepository: PacketRepository private val packetRepository: PacketRepository = mock()
@BeforeTest @BeforeTest
fun setUp() { fun setUp() {
Dispatchers.setMain(testDispatcher)
nodeRepository = FakeNodeRepository() nodeRepository = FakeNodeRepository()
radioController = FakeRadioController() radioController = FakeRadioController()
mapPrefs = every { mapPrefs.showOnlyFavorites } returns MutableStateFlow(false)
every { showOnlyFavorites } returns MutableStateFlow(false) every { mapPrefs.showWaypointsOnMap } returns MutableStateFlow(false)
every { showWaypointsOnMap } returns MutableStateFlow(false) every { mapPrefs.showPrecisionCircleOnMap } returns MutableStateFlow(false)
every { showPrecisionCircleOnMap } returns MutableStateFlow(false) every { mapPrefs.lastHeardFilter } returns MutableStateFlow(0L)
every { lastHeardFilter } returns MutableStateFlow(0L) every { mapPrefs.lastHeardTrackFilter } returns MutableStateFlow(0L)
every { lastHeardTrackFilter } returns MutableStateFlow(0L)
}
viewModel = every { packetRepository.getWaypoints() } returns MutableStateFlow(emptyList())
BaseMapViewModel(
viewModel = BaseMapViewModel(
mapPrefs = mapPrefs, mapPrefs = mapPrefs,
nodeRepository = nodeRepository, nodeRepository = nodeRepository,
packetRepository = packetRepository, packetRepository = packetRepository,
@ -53,41 +75,52 @@ class BaseMapViewModelTest {
) )
} }
@AfterTest
fun tearDown() {
Dispatchers.resetMain()
}
@Test @Test
fun testInitialization() = runTest { fun testInitialization() {
setUp() assertNotNull(viewModel)
assertTrue(true, "BaseMapViewModel initialized successfully")
} }
@Test @Test
fun testMyNodeInfoFlow() = runTest { fun testMyNodeInfoFlow() = runTest {
setUp() viewModel.myNodeInfo.test {
val myNodeInfo = viewModel.myNodeInfo.value assertEquals(null, awaitItem())
assertTrue(myNodeInfo == null, "myNodeInfo starts as null") cancelAndIgnoreRemainingEvents()
}
} }
@Test @Test
fun testNodesWithPositionStartsEmpty() = runTest { fun testNodesWithPositionStartsEmpty() = runTest {
setUp() viewModel.nodesWithPosition.test {
"nodesWithPosition should start empty" shouldBe emptyList<Any>(), viewModel.nodesWithPosition.value assertEquals(emptyList(), awaitItem())
cancelAndIgnoreRemainingEvents()
}
} }
@Test @Test
fun testConnectionStateFlow() = runTest { fun testConnectionStateFlow() = runTest {
setUp() viewModel.isConnected.test {
radioController.setConnectionState(org.meshtastic.core.model.ConnectionState.Disconnected) // Initially reflects radioController state (which is Disconnected in FakeRadioController default)
// isConnected should reflect radioController state assertEquals(false, awaitItem())
assertTrue(true, "Connection state flow is reactive")
radioController.setConnectionState(ConnectionState.Connected)
assertEquals(true, awaitItem())
radioController.setConnectionState(ConnectionState.Disconnected)
assertEquals(false, awaitItem())
cancelAndIgnoreRemainingEvents()
}
} }
@Test @Test
fun testNodeRepositoryIntegration() = runTest { fun testNodeRepositoryIntegration() = runTest {
setUp()
val testNodes = TestDataFactory.createTestNodes(3) val testNodes = TestDataFactory.createTestNodes(3)
nodeRepository.setNodes(testNodes) nodeRepository.setNodes(testNodes)
"Nodes added to repository" shouldBe 3, nodeRepository.nodeDBbyNum.value.size assertEquals(3, nodeRepository.nodeDBbyNum.value.size)
} }
*/
} }