diff --git a/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetLocaleUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetLocaleUseCaseTest.kt new file mode 100644 index 000000000..b50e54b24 --- /dev/null +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetLocaleUseCaseTest.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026 Meshtastic LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 . + */ +package org.meshtastic.core.domain.usecase.settings + +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.matcher.any +import dev.mokkery.mock +import dev.mokkery.verify +import org.meshtastic.core.common.UiPreferences +import kotlin.test.BeforeTest +import kotlin.test.Test + +class SetLocaleUseCaseTest { + + private val uiPreferences: UiPreferences = mock() + private lateinit var useCase: SetLocaleUseCase + + @BeforeTest + fun setUp() { + useCase = SetLocaleUseCase(uiPreferences) + } + + @Test + fun `invoke calls setLocale on uiPreferences`() { + every { uiPreferences.setLocale(any()) } returns Unit + useCase("en") + verify { uiPreferences.setLocale("en") } + } +} diff --git a/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetNotificationSettingsUseCaseTest.kt b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetNotificationSettingsUseCaseTest.kt new file mode 100644 index 000000000..09b653ced --- /dev/null +++ b/core/domain/src/commonTest/kotlin/org/meshtastic/core/domain/usecase/settings/SetNotificationSettingsUseCaseTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2026 Meshtastic LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. 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 . + */ +package org.meshtastic.core.domain.usecase.settings + +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.matcher.any +import dev.mokkery.mock +import dev.mokkery.verify +import org.meshtastic.core.repository.NotificationPrefs +import kotlin.test.BeforeTest +import kotlin.test.Test + +class SetNotificationSettingsUseCaseTest { + + private val notificationPrefs: NotificationPrefs = mock() + private lateinit var useCase: SetNotificationSettingsUseCase + + @BeforeTest + fun setUp() { + useCase = SetNotificationSettingsUseCase(notificationPrefs) + } + + @Test + fun `setMessagesEnabled calls notificationPrefs`() { + every { notificationPrefs.setMessagesEnabled(any()) } returns Unit + useCase.setMessagesEnabled(true) + verify { notificationPrefs.setMessagesEnabled(true) } + } + + @Test + fun `setNodeEventsEnabled calls notificationPrefs`() { + every { notificationPrefs.setNodeEventsEnabled(any()) } returns Unit + useCase.setNodeEventsEnabled(false) + verify { notificationPrefs.setNodeEventsEnabled(false) } + } + + @Test + fun `setLowBatteryEnabled calls notificationPrefs`() { + every { notificationPrefs.setLowBatteryEnabled(any()) } returns Unit + useCase.setLowBatteryEnabled(true) + verify { notificationPrefs.setLowBatteryEnabled(true) } + } +} diff --git a/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCaseTest.kt b/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCaseTest.kt new file mode 100644 index 000000000..26ce4bd60 --- /dev/null +++ b/feature/node/src/commonTest/kotlin/org/meshtastic/feature/node/domain/usecase/GetFilteredNodesUseCaseTest.kt @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025-2026 Meshtastic LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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 . + */ +package org.meshtastic.feature.node.domain.usecase + +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.matcher.any +import dev.mokkery.mock +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.meshtastic.core.model.Node +import org.meshtastic.core.model.NodeSortOption +import org.meshtastic.core.repository.NodeRepository +import org.meshtastic.feature.node.list.NodeFilterState +import org.meshtastic.proto.Config +import org.meshtastic.proto.User +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class GetFilteredNodesUseCaseTest { + + private lateinit var nodeRepository: NodeRepository + private lateinit var useCase: GetFilteredNodesUseCase + + @BeforeTest + fun setUp() { + nodeRepository = mock() + useCase = GetFilteredNodesUseCase(nodeRepository) + } + + private fun createNode( + num: Int, + role: Config.DeviceConfig.Role = Config.DeviceConfig.Role.CLIENT, + ignored: Boolean = false, + name: String = "Node$num", + viaMqtt: Boolean = false, + ): Node { + val user = User(id = "!$num", long_name = name, short_name = "N$num", role = role) + return Node(num = num, user = user, isIgnored = ignored, viaMqtt = viaMqtt) + } + + @Test + fun `invoke applies repository filters and returns nodes`() = runTest { + // Arrange + val nodes = listOf(createNode(1), createNode(2)) + val filter = NodeFilterState(filterText = "Node", includeUnknown = true) + + every { + nodeRepository.getNodes( + sort = NodeSortOption.LAST_HEARD, + filter = "Node", + includeUnknown = true, + onlyOnline = false, + onlyDirect = false, + ) + } returns flowOf(nodes) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + assertEquals(2, result.size) + assertEquals(1, result[0].num) + } + + @Test + fun `invoke filters out ignored nodes if showIgnored is false`() = runTest { + // Arrange + val normalNode = createNode(1, ignored = false) + val ignoredNode = createNode(2, ignored = true) + val filter = NodeFilterState(showIgnored = false) + + every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns + flowOf(listOf(normalNode, ignoredNode)) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + assertEquals(1, result.size) + assertEquals(1, result.first().num) + } + + @Test + fun `invoke filters out infrastructure nodes if excludeInfrastructure is true`() = runTest { + // Arrange + val clientNode = createNode(1, role = Config.DeviceConfig.Role.CLIENT) + val routerNode = createNode(2, role = Config.DeviceConfig.Role.ROUTER) + + @Suppress("DEPRECATION") + val repeaterNode = createNode(3, role = Config.DeviceConfig.Role.REPEATER) + val clientBaseNode = createNode(4, role = Config.DeviceConfig.Role.CLIENT_BASE) + val filter = NodeFilterState(excludeInfrastructure = true) + + every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns + flowOf(listOf(clientNode, routerNode, repeaterNode, clientBaseNode)) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + // Should only keep the CLIENT node, others are infrastructure + assertEquals(1, result.size) + assertEquals(1, result.first().num) + } + + @Test + fun `invoke filters out MQTT nodes if excludeMqtt is true`() = runTest { + // Arrange + val loraNode = createNode(1, viaMqtt = false) + val mqttNode = createNode(2, viaMqtt = true) + val filter = NodeFilterState(excludeMqtt = true) + + every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns flowOf(listOf(loraNode, mqttNode)) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + assertEquals(1, result.size) + assertEquals(1, result.first().num) + } + + @Test + fun `invoke keeps MQTT nodes if excludeMqtt is false`() = runTest { + // Arrange + val loraNode = createNode(1, viaMqtt = false) + val mqttNode = createNode(2, viaMqtt = true) + val filter = NodeFilterState(excludeMqtt = false) + + every { nodeRepository.getNodes(any(), any(), any(), any(), any()) } returns flowOf(listOf(loraNode, mqttNode)) + + // Act + val result = useCase(filter, NodeSortOption.LAST_HEARD).first() + + // Assert + assertEquals(2, result.size) + } +}