mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
test(feature): Expand ViewModel coverage with Turbine and Mokkery
This commit is contained in:
parent
727af8a3be
commit
c813be8228
3 changed files with 286 additions and 150 deletions
|
|
@ -16,174 +16,113 @@
|
||||||
*/
|
*/
|
||||||
package org.meshtastic.feature.connections
|
package org.meshtastic.feature.connections
|
||||||
|
|
||||||
import io.kotest.matchers.shouldBe
|
import app.cash.turbine.test
|
||||||
|
import dev.mokkery.MockMode
|
||||||
|
import dev.mokkery.answering.returns
|
||||||
|
import dev.mokkery.every
|
||||||
|
import dev.mokkery.mock
|
||||||
|
import dev.mokkery.matcher.any
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
import org.meshtastic.core.model.ConnectionState
|
||||||
import org.meshtastic.core.model.DeviceType
|
|
||||||
import org.meshtastic.core.model.RadioController
|
import org.meshtastic.core.model.RadioController
|
||||||
import org.meshtastic.core.repository.RadioInterfaceService
|
import org.meshtastic.core.repository.RadioInterfaceService
|
||||||
import org.meshtastic.core.repository.ServiceRepository
|
import org.meshtastic.core.repository.ServiceRepository
|
||||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||||
import org.meshtastic.feature.connections.model.DiscoveredDevices
|
import org.meshtastic.feature.connections.model.DiscoveredDevices
|
||||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||||
|
import kotlin.test.BeforeTest
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertNull
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for [ScannerViewModel] covering core device selection, connection, and state management.
|
|
||||||
*
|
|
||||||
* Uses `core:testing` fakes where available and mockk for remaining dependencies.
|
|
||||||
*/
|
|
||||||
class ScannerViewModelTest {
|
class ScannerViewModelTest {
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
private lateinit var viewModel: ScannerViewModel
|
private lateinit var viewModel: ScannerViewModel
|
||||||
private lateinit var radioController: RadioController
|
private val serviceRepository: ServiceRepository = mock(MockMode.autofill)
|
||||||
private lateinit var serviceRepository: ServiceRepository
|
private val radioController: RadioController = mock(MockMode.autofill)
|
||||||
private lateinit var radioInterfaceService: RadioInterfaceService
|
private val radioInterfaceService: RadioInterfaceService = mock(MockMode.autofill)
|
||||||
private lateinit var recentAddressesDataSource: RecentAddressesDataSource
|
private val recentAddressesDataSource: RecentAddressesDataSource = mock(MockMode.autofill)
|
||||||
private lateinit var getDiscoveredDevicesUseCase: GetDiscoveredDevicesUseCase
|
private val getDiscoveredDevicesUseCase: GetDiscoveredDevicesUseCase = mock(MockMode.autofill)
|
||||||
|
private val bleScanner: org.meshtastic.core.ble.BleScanner = mock(MockMode.autofill)
|
||||||
|
|
||||||
private fun setUp() {
|
private val connectionProgressFlow = MutableStateFlow<String?>(null)
|
||||||
radioInterfaceService =
|
private val discoveredDevicesFlow = MutableStateFlow(DiscoveredDevices())
|
||||||
every { isMockInterface() } returns false
|
|
||||||
every { currentDeviceAddressFlow } returns MutableStateFlow(null)
|
|
||||||
every { supportedDeviceTypes } returns listOf(DeviceType.BLE, DeviceType.TCP, DeviceType.USB)
|
|
||||||
}
|
|
||||||
getDiscoveredDevicesUseCase =
|
|
||||||
object : GetDiscoveredDevicesUseCase {
|
|
||||||
override fun invoke(showMock: Boolean) = flowOf(DiscoveredDevices())
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel =
|
@BeforeTest
|
||||||
ScannerViewModel(
|
fun setUp() {
|
||||||
serviceRepository = serviceRepository,
|
every { radioInterfaceService.isMockInterface() } returns false
|
||||||
radioController = radioController,
|
every { radioInterfaceService.currentDeviceAddressFlow } returns MutableStateFlow(null)
|
||||||
radioInterfaceService = radioInterfaceService,
|
every { radioInterfaceService.supportedDeviceTypes } returns emptyList()
|
||||||
recentAddressesDataSource = recentAddressesDataSource,
|
|
||||||
getDiscoveredDevicesUseCase = getDiscoveredDevicesUseCase,
|
every { serviceRepository.connectionProgress } returns connectionProgressFlow
|
||||||
)
|
every { getDiscoveredDevicesUseCase.invoke(any()) } returns discoveredDevicesFlow
|
||||||
}
|
every { recentAddressesDataSource.recentAddresses } returns MutableStateFlow(emptyList())
|
||||||
|
|
||||||
@Test
|
connectionProgressFlow.value = null
|
||||||
fun testInitialization() = runTest {
|
discoveredDevicesFlow.value = DiscoveredDevices()
|
||||||
setUp()
|
|
||||||
assertNull(viewModel.errorText.value, "Error text starts as null before connectionProgress emits")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
viewModel = ScannerViewModel(
|
||||||
fun testSetErrorText() = runTest {
|
serviceRepository = serviceRepository,
|
||||||
setUp()
|
radioController = radioController,
|
||||||
viewModel.setErrorText("Test error")
|
radioInterfaceService = radioInterfaceService,
|
||||||
viewModel.errorText.value shouldBe "Test error"
|
recentAddressesDataSource = recentAddressesDataSource,
|
||||||
}
|
getDiscoveredDevicesUseCase = getDiscoveredDevicesUseCase,
|
||||||
|
bleScanner = bleScanner
|
||||||
@Test
|
|
||||||
fun testDisconnect() = runTest {
|
|
||||||
setUp()
|
|
||||||
viewModel.disconnect()
|
|
||||||
verify { radioController.setDeviceAddress(NO_DEVICE_SELECTED) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testChangeDeviceAddress() = runTest {
|
|
||||||
setUp()
|
|
||||||
viewModel.changeDeviceAddress("x12:34:56:78:90:AB")
|
|
||||||
verify { radioController.setDeviceAddress("x12:34:56:78:90:AB") }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOnSelectedBleDeviceBonded() = runTest {
|
|
||||||
setUp()
|
|
||||||
val bleDevice =
|
|
||||||
every { bonded } returns true
|
|
||||||
every { fullAddress } returns "xAA:BB:CC:DD:EE:FF"
|
|
||||||
}
|
|
||||||
val result = viewModel.onSelected(bleDevice)
|
|
||||||
assertTrue(result, "Should return true for bonded BLE device")
|
|
||||||
verify { radioController.setDeviceAddress("xAA:BB:CC:DD:EE:FF") }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOnSelectedBleDeviceNotBonded() = runTest {
|
|
||||||
setUp()
|
|
||||||
val result = viewModel.onSelected(bleDevice)
|
|
||||||
assertFalse(result, "Should return false for unbonded BLE device (triggers bonding)")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOnSelectedTcpDevice() = runTest {
|
|
||||||
setUp()
|
|
||||||
val tcpDevice = DeviceListEntry.Tcp("Meshtastic_1234", "t192.168.1.100")
|
|
||||||
val result = viewModel.onSelected(tcpDevice)
|
|
||||||
assertTrue(result, "Should return true for TCP device")
|
|
||||||
verify { radioController.setDeviceAddress("t192.168.1.100") }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOnSelectedMockDevice() = runTest {
|
|
||||||
setUp()
|
|
||||||
val mockDevice = DeviceListEntry.Mock("Demo Mode")
|
|
||||||
val result = viewModel.onSelected(mockDevice)
|
|
||||||
assertTrue(result, "Should return true for mock device")
|
|
||||||
verify { radioController.setDeviceAddress("m") }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOnSelectedUsbDeviceBonded() = runTest {
|
|
||||||
setUp()
|
|
||||||
val usbDevice =
|
|
||||||
every { bonded } returns true
|
|
||||||
every { fullAddress } returns "s/dev/ttyACM0"
|
|
||||||
}
|
|
||||||
val result = viewModel.onSelected(usbDevice)
|
|
||||||
assertTrue(result, "Should return true for bonded USB device")
|
|
||||||
verify { radioController.setDeviceAddress("s/dev/ttyACM0") }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testOnSelectedUsbDeviceNotBonded() = runTest {
|
|
||||||
setUp()
|
|
||||||
val result = viewModel.onSelected(usbDevice)
|
|
||||||
assertFalse(result, "Should return false for unbonded USB device (triggers permission request)")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAddRecentAddressIgnoresNonTcpAddresses() = runTest {
|
|
||||||
setUp()
|
|
||||||
viewModel.addRecentAddress("xBLE_ADDRESS", "BLE Device")
|
|
||||||
// Should not add — address doesn't start with "t"
|
|
||||||
verify(exactly = 0) { recentAddressesDataSource.toString() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSelectedNotNullFlowDefaultsToNoDeviceSelected() = runTest {
|
|
||||||
setUp()
|
|
||||||
assertEquals(
|
|
||||||
NO_DEVICE_SELECTED,
|
|
||||||
viewModel.selectedNotNullFlow.value,
|
|
||||||
"selectedNotNullFlow defaults to NO_DEVICE_SELECTED when no device is selected",
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSupportedDeviceTypes() = runTest {
|
fun testInitialization() {
|
||||||
setUp()
|
assertNotNull(viewModel)
|
||||||
viewModel.supportedDeviceTypes shouldBe listOf(DeviceType.BLE, DeviceType.TCP, DeviceType.USB)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testShowMockInterfaceFalseByDefault() = runTest {
|
fun `errorText reflects connectionProgress`() = runTest {
|
||||||
setUp()
|
viewModel.errorText.test {
|
||||||
assertFalse(viewModel.showMockInterface.value, "showMockInterface defaults to false")
|
assertEquals(null, awaitItem())
|
||||||
|
connectionProgressFlow.value = "Connecting..."
|
||||||
|
assertEquals("Connecting...", awaitItem())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
@Test
|
||||||
|
fun `startBleScan updates isBleScanning`() = runTest {
|
||||||
|
every { bleScanner.scan(any(), any()) } returns kotlinx.coroutines.flow.emptyFlow()
|
||||||
|
|
||||||
|
viewModel.isBleScanning.test {
|
||||||
|
assertEquals(false, awaitItem())
|
||||||
|
viewModel.startBleScan()
|
||||||
|
assertEquals(true, awaitItem())
|
||||||
|
|
||||||
|
viewModel.stopBleScan()
|
||||||
|
assertEquals(false, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `changeDeviceAddress calls radioController`() {
|
||||||
|
every { radioController.setDeviceAddress(any()) } returns Unit
|
||||||
|
|
||||||
|
viewModel.changeDeviceAddress("test_address")
|
||||||
|
|
||||||
|
dev.mokkery.verify { radioController.setDeviceAddress("test_address") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `usbDevicesForUi emits updates`() = runTest {
|
||||||
|
viewModel.usbDevicesForUi.test {
|
||||||
|
assertEquals(emptyList(), awaitItem())
|
||||||
|
|
||||||
|
val device = org.meshtastic.feature.connections.model.DeviceListEntry.Usb(
|
||||||
|
usbData = object : org.meshtastic.feature.connections.model.UsbDeviceData {},
|
||||||
|
name = "USB Device",
|
||||||
|
fullAddress = "usb_address",
|
||||||
|
bonded = true
|
||||||
|
)
|
||||||
|
discoveredDevicesFlow.value = DiscoveredDevices(usbDevices = listOf(device))
|
||||||
|
|
||||||
|
assertEquals(listOf(device), awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,14 @@ import app.cash.turbine.test
|
||||||
import dev.mokkery.MockMode
|
import dev.mokkery.MockMode
|
||||||
import dev.mokkery.answering.returns
|
import dev.mokkery.answering.returns
|
||||||
import dev.mokkery.every
|
import dev.mokkery.every
|
||||||
|
import dev.mokkery.everySuspend
|
||||||
import dev.mokkery.mock
|
import dev.mokkery.mock
|
||||||
import dev.mokkery.matcher.any
|
import dev.mokkery.matcher.any
|
||||||
|
import dev.mokkery.verify
|
||||||
|
import dev.mokkery.verifySuspend
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.meshtastic.core.repository.QuickChatActionRepository
|
import org.meshtastic.core.repository.QuickChatActionRepository
|
||||||
import org.meshtastic.core.model.service.ServiceAction
|
import org.meshtastic.core.model.service.ServiceAction
|
||||||
|
|
@ -59,12 +63,23 @@ class MessageViewModelTest {
|
||||||
private val customEmojiPrefs: CustomEmojiPrefs = mock(MockMode.autofill)
|
private val customEmojiPrefs: CustomEmojiPrefs = mock(MockMode.autofill)
|
||||||
private val homoglyphPrefs: HomoglyphPrefs = mock(MockMode.autofill)
|
private val homoglyphPrefs: HomoglyphPrefs = mock(MockMode.autofill)
|
||||||
private val uiPrefs: UiPrefs = mock(MockMode.autofill)
|
private val uiPrefs: UiPrefs = mock(MockMode.autofill)
|
||||||
|
private val notificationManager: org.meshtastic.core.repository.NotificationManager = mock(MockMode.autofill)
|
||||||
|
|
||||||
|
private val connectionStateFlow = MutableStateFlow<org.meshtastic.core.model.ConnectionState>(org.meshtastic.core.model.ConnectionState.Disconnected)
|
||||||
|
private val showQuickChatFlow = MutableStateFlow(false)
|
||||||
|
private val customEmojiFrequencyFlow = MutableStateFlow<String?>(null)
|
||||||
|
private val contactSettingsFlow = MutableStateFlow<Map<String, org.meshtastic.core.model.ContactSettings>>(emptyMap())
|
||||||
|
|
||||||
@BeforeTest
|
@BeforeTest
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
savedStateHandle = SavedStateHandle(mapOf("contactKey" to "0!12345678"))
|
savedStateHandle = SavedStateHandle(mapOf("contactKey" to "0!12345678"))
|
||||||
nodeRepository = FakeNodeRepository()
|
nodeRepository = FakeNodeRepository()
|
||||||
|
|
||||||
|
connectionStateFlow.value = org.meshtastic.core.model.ConnectionState.Disconnected
|
||||||
|
showQuickChatFlow.value = false
|
||||||
|
customEmojiFrequencyFlow.value = null
|
||||||
|
contactSettingsFlow.value = emptyMap()
|
||||||
|
|
||||||
// Core flows - MUST be separate every blocks
|
// Core flows - MUST be separate every blocks
|
||||||
every { radioConfigRepository.channelSetFlow } returns MutableStateFlow(ChannelSet())
|
every { radioConfigRepository.channelSetFlow } returns MutableStateFlow(ChannelSet())
|
||||||
every { radioConfigRepository.localConfigFlow } returns MutableStateFlow(LocalConfig())
|
every { radioConfigRepository.localConfigFlow } returns MutableStateFlow(LocalConfig())
|
||||||
|
|
@ -72,13 +87,14 @@ class MessageViewModelTest {
|
||||||
every { radioConfigRepository.deviceProfileFlow } returns MutableStateFlow(DeviceProfile())
|
every { radioConfigRepository.deviceProfileFlow } returns MutableStateFlow(DeviceProfile())
|
||||||
|
|
||||||
every { serviceRepository.serviceAction } returns emptyFlow<ServiceAction>()
|
every { serviceRepository.serviceAction } returns emptyFlow<ServiceAction>()
|
||||||
every { serviceRepository.connectionState } returns MutableStateFlow(org.meshtastic.core.model.ConnectionState.Disconnected)
|
every { serviceRepository.connectionState } returns connectionStateFlow
|
||||||
|
|
||||||
every { customEmojiPrefs.customEmojiFrequency } returns MutableStateFlow<String?>(null)
|
every { customEmojiPrefs.customEmojiFrequency } returns customEmojiFrequencyFlow
|
||||||
every { homoglyphPrefs.homoglyphEncodingEnabled } returns MutableStateFlow(false)
|
every { homoglyphPrefs.homoglyphEncodingEnabled } returns MutableStateFlow(false)
|
||||||
every { uiPrefs.showQuickChat } returns MutableStateFlow(false)
|
every { uiPrefs.showQuickChat } returns showQuickChatFlow
|
||||||
|
every { uiPrefs.setShowQuickChat(any()) } returns Unit
|
||||||
|
|
||||||
every { packetRepository.getContactSettings() } returns MutableStateFlow(emptyMap())
|
every { packetRepository.getContactSettings() } returns contactSettingsFlow
|
||||||
every { packetRepository.getFirstUnreadMessageUuid(any<String>()) } returns MutableStateFlow(null)
|
every { packetRepository.getFirstUnreadMessageUuid(any<String>()) } returns MutableStateFlow(null)
|
||||||
every { packetRepository.hasUnreadMessages(any<String>()) } returns MutableStateFlow(false)
|
every { packetRepository.hasUnreadMessages(any<String>()) } returns MutableStateFlow(false)
|
||||||
every { packetRepository.getUnreadCountFlow(any<String>()) } returns MutableStateFlow(0)
|
every { packetRepository.getUnreadCountFlow(any<String>()) } returns MutableStateFlow(0)
|
||||||
|
|
@ -97,7 +113,7 @@ class MessageViewModelTest {
|
||||||
customEmojiPrefs = customEmojiPrefs,
|
customEmojiPrefs = customEmojiPrefs,
|
||||||
homoglyphEncodingPrefs = homoglyphPrefs,
|
homoglyphEncodingPrefs = homoglyphPrefs,
|
||||||
uiPrefs = uiPrefs,
|
uiPrefs = uiPrefs,
|
||||||
notificationManager = mock(MockMode.autofill),
|
notificationManager = notificationManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,6 +122,117 @@ class MessageViewModelTest {
|
||||||
assertNotNull(viewModel)
|
assertNotNull(viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetTitle() = runTest {
|
||||||
|
viewModel.title.test {
|
||||||
|
assertEquals("", awaitItem())
|
||||||
|
viewModel.setTitle("New Title")
|
||||||
|
assertEquals("New Title", awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testConnectionState() = runTest {
|
||||||
|
viewModel.connectionState.test {
|
||||||
|
assertEquals(org.meshtastic.core.model.ConnectionState.Disconnected, awaitItem())
|
||||||
|
connectionStateFlow.value = org.meshtastic.core.model.ConnectionState.Connected
|
||||||
|
assertEquals(org.meshtastic.core.model.ConnectionState.Connected, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testToggleShowQuickChat() = runTest {
|
||||||
|
// The VM init collects from uiPrefs.showQuickChat
|
||||||
|
viewModel.showQuickChat.test {
|
||||||
|
assertEquals(false, awaitItem())
|
||||||
|
|
||||||
|
viewModel.toggleShowQuickChat()
|
||||||
|
// toggleShowQuickChat updates _showQuickChat AND calls uiPrefs.setShowQuickChat
|
||||||
|
// Since we are collecting, we should see the update.
|
||||||
|
assertEquals(true, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFrequentEmojis() = runTest {
|
||||||
|
customEmojiFrequencyFlow.value = "👍=10,👎=5,😂=20"
|
||||||
|
|
||||||
|
// frequentEmojis is a property, not a flow.
|
||||||
|
val emojis = viewModel.frequentEmojis
|
||||||
|
assertEquals(listOf("😂", "👍", "👎"), emojis)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSendMessage() = runTest {
|
||||||
|
everySuspend { sendMessageUseCase.invoke(any(), any(), any()) } returns Unit
|
||||||
|
|
||||||
|
viewModel.sendMessage("Hello", "0!12345678", null)
|
||||||
|
|
||||||
|
// Wait for coroutine to finish
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Verify via mokkery
|
||||||
|
verifySuspend { sendMessageUseCase.invoke("Hello", "0!12345678", null) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSendReaction() = runTest {
|
||||||
|
everySuspend { serviceRepository.onServiceAction(any()) } returns Unit
|
||||||
|
|
||||||
|
viewModel.sendReaction("❤️", 123, "0!12345678")
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
verifySuspend {
|
||||||
|
serviceRepository.onServiceAction(ServiceAction.Reaction("❤️", 123, "0!12345678"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDeleteMessages() = runTest {
|
||||||
|
everySuspend { packetRepository.deleteMessages(any()) } returns Unit
|
||||||
|
|
||||||
|
viewModel.deleteMessages(listOf(1L, 2L))
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
verifySuspend { packetRepository.deleteMessages(listOf(1L, 2L)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUnreadCount() = runTest {
|
||||||
|
val countFlow = MutableStateFlow(5)
|
||||||
|
every { packetRepository.getUnreadCountFlow("new_contact") } returns countFlow
|
||||||
|
|
||||||
|
viewModel.setContactKey("new_contact")
|
||||||
|
|
||||||
|
viewModel.unreadCount.test {
|
||||||
|
// Initial 0 from stateIn
|
||||||
|
assertEquals(0, awaitItem())
|
||||||
|
// Value from countFlow
|
||||||
|
assertEquals(5, awaitItem())
|
||||||
|
countFlow.value = 10
|
||||||
|
assertEquals(10, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClearUnreadCount() = runTest {
|
||||||
|
val contact = "0!12345678"
|
||||||
|
everySuspend { packetRepository.clearUnreadCount(contact, 1000L) } returns Unit
|
||||||
|
everySuspend { packetRepository.updateLastReadMessage(contact, 1L, 1000L) } returns Unit
|
||||||
|
everySuspend { packetRepository.getUnreadCount(contact) } returns 0
|
||||||
|
every { notificationManager.cancel(contact.hashCode()) } returns Unit
|
||||||
|
|
||||||
|
viewModel.clearUnreadCount(contact, 1L, 1000L)
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
verifySuspend { packetRepository.clearUnreadCount(contact, 1000L) }
|
||||||
|
verifySuspend { packetRepository.updateLastReadMessage(contact, 1L, 1000L) }
|
||||||
|
verifySuspend { notificationManager.cancel(contact.hashCode()) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNodeRepositoryIntegration() = runTest {
|
fun testNodeRepositoryIntegration() = runTest {
|
||||||
val testNodes = TestDataFactory.createTestNodes(3)
|
val testNodes = TestDataFactory.createTestNodes(3)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import app.cash.turbine.test
|
||||||
import dev.mokkery.MockMode
|
import dev.mokkery.MockMode
|
||||||
import dev.mokkery.answering.returns
|
import dev.mokkery.answering.returns
|
||||||
import dev.mokkery.every
|
import dev.mokkery.every
|
||||||
|
import dev.mokkery.everySuspend
|
||||||
import dev.mokkery.mock
|
import dev.mokkery.mock
|
||||||
import dev.mokkery.matcher.any
|
import dev.mokkery.matcher.any
|
||||||
import dev.mokkery.verifySuspend
|
import dev.mokkery.verifySuspend
|
||||||
|
|
@ -125,7 +126,7 @@ class RadioConfigViewModelTest {
|
||||||
viewModel = createViewModel()
|
viewModel = createViewModel()
|
||||||
|
|
||||||
val config = Config(device = Config.DeviceConfig(role = Config.DeviceConfig.Role.ROUTER))
|
val config = Config(device = Config.DeviceConfig(role = Config.DeviceConfig.Role.ROUTER))
|
||||||
dev.mokkery.everySuspend { radioConfigUseCase.setConfig(any(), any()) } returns 42
|
everySuspend { radioConfigUseCase.setConfig(any(), any()) } returns 42
|
||||||
|
|
||||||
viewModel.setConfig(config)
|
viewModel.setConfig(config)
|
||||||
|
|
||||||
|
|
@ -136,4 +137,73 @@ class RadioConfigViewModelTest {
|
||||||
|
|
||||||
verifySuspend { radioConfigUseCase.setConfig(123, config) }
|
verifySuspend { radioConfigUseCase.setConfig(123, config) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toggleAnalyticsAllowed calls useCase`() {
|
||||||
|
every { toggleAnalyticsUseCase() } returns Unit
|
||||||
|
|
||||||
|
viewModel.toggleAnalyticsAllowed()
|
||||||
|
|
||||||
|
dev.mokkery.verify { toggleAnalyticsUseCase() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toggleHomoglyphCharactersEncodingEnabled calls useCase`() {
|
||||||
|
every { toggleHomoglyphEncodingUseCase() } returns Unit
|
||||||
|
|
||||||
|
viewModel.toggleHomoglyphCharactersEncodingEnabled()
|
||||||
|
|
||||||
|
dev.mokkery.verify { toggleHomoglyphEncodingUseCase() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `setPreserveFavorites updates state`() = runTest {
|
||||||
|
viewModel.radioConfigState.test {
|
||||||
|
assertEquals(false, awaitItem().nodeDbResetPreserveFavorites)
|
||||||
|
viewModel.setPreserveFavorites(true)
|
||||||
|
assertEquals(true, awaitItem().nodeDbResetPreserveFavorites)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `setOwner calls useCase`() = runTest {
|
||||||
|
val node = Node(num = 123, user = User(id = "!123"))
|
||||||
|
every { nodeRepository.nodeDBbyNum } returns MutableStateFlow(mapOf(123 to node))
|
||||||
|
viewModel = createViewModel()
|
||||||
|
|
||||||
|
val user = User(long_name = "Test User")
|
||||||
|
everySuspend { radioConfigUseCase.setOwner(any(), any()) } returns 42
|
||||||
|
|
||||||
|
viewModel.setOwner(user)
|
||||||
|
|
||||||
|
verifySuspend { radioConfigUseCase.setOwner(123, user) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `setRingtone calls useCase`() = runTest {
|
||||||
|
val node = Node(num = 123, user = User(id = "!123"))
|
||||||
|
every { nodeRepository.nodeDBbyNum } returns MutableStateFlow(mapOf(123 to node))
|
||||||
|
viewModel = createViewModel()
|
||||||
|
|
||||||
|
everySuspend { radioConfigUseCase.setRingtone(any(), any()) } returns Unit
|
||||||
|
|
||||||
|
viewModel.setRingtone("ringtone.mp3")
|
||||||
|
|
||||||
|
assertEquals("ringtone.mp3", viewModel.radioConfigState.value.ringtone)
|
||||||
|
verifySuspend { radioConfigUseCase.setRingtone(123, "ringtone.mp3") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `setCannedMessages calls useCase`() = runTest {
|
||||||
|
val node = Node(num = 123, user = User(id = "!123"))
|
||||||
|
every { nodeRepository.nodeDBbyNum } returns MutableStateFlow(mapOf(123 to node))
|
||||||
|
viewModel = createViewModel()
|
||||||
|
|
||||||
|
everySuspend { radioConfigUseCase.setCannedMessages(any(), any()) } returns Unit
|
||||||
|
|
||||||
|
viewModel.setCannedMessages("Hello|World")
|
||||||
|
|
||||||
|
assertEquals("Hello|World", viewModel.radioConfigState.value.cannedMessageMessages)
|
||||||
|
verifySuspend { radioConfigUseCase.setCannedMessages(123, "Hello|World") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue