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)
+ }
+}