chore: rename stale files and update migration doc

- NodeInfo.kt → MeshModels.kt (no longer contains NodeInfo class)
- NodeManagerImplTest.kt → SdkNodeRepositoryImplTest.kt (tests SdkNodeRepositoryImpl)
- Update MIGRATION-REMAINING.md with dead code removal, error handling,
  and NodeManager merge status

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
James Rich
2026-05-05 12:00:09 -05:00
parent 2db6db5ed9
commit 43ecd2eb73
3 changed files with 66 additions and 52 deletions

View File

@@ -77,7 +77,7 @@ NodeManager merged into SdkNodeRepositoryImpl, MeshActivity restored.
### Data Layer ✅
- Room migration 38→39: NodeMetadata persistence
- Room migration 39→40: DROP legacy `nodes`, `my_node`, `metadata` tables
- `SdkNodeRepositoryImpl` implements NodeRepository + NodeManager + NodeIdLookup
- `SdkNodeRepositoryImpl` implements unified NodeRepository + NodeIdLookup
- SDK storage (SqlDelight) is source of truth for node data
- `AppMetadataRepository` provides firmware/hardware/model info
- NodeManagerImpl deleted — logic merged into SdkNodeRepositoryImpl
@@ -88,10 +88,12 @@ NodeManager merged into SdkNodeRepositoryImpl, MeshActivity restored.
- No transport stubs needed — SDK handles everything
### NodeManager Merge ✅
- `SdkNodeRepositoryImpl` now binds NodeRepository, NodeManager, NodeIdLookup
- NodeManager interface eliminated — all methods merged into NodeRepository
- `SdkNodeRepositoryImpl` now binds [NodeRepository, NodeIdLookup]
- Single in-memory StateFlow — no duplicate maps
- Metadata enrichment on every write (favorites, notes, ignore, mute)
- `NodeManagerImpl.kt` deleted (377 LOC)
- `NodeManager.kt` interface deleted (82 LOC)
### MeshActivity Restoration ✅
- `meshActivityFlow` added to ServiceRepository interface
@@ -100,6 +102,18 @@ NodeManager merged into SdkNodeRepositoryImpl, MeshActivity restored.
- UIViewModel.meshActivity wired to serviceRepository.meshActivityFlow
- Connection icon animation fully functional
### Dead Code Removal ✅
- Removed 7 dead methods from NodeManager/NodeRepository interfaces (~220 LOC)
- Deleted `NodeInfo` data class (kept MeshUser, Position, DeviceMetrics, EnvironmentMetrics)
- Renamed `NodeInfo.kt``MeshModels.kt`
- Removed dead `nodeManager` parameter from MeshServiceOrchestrator
### Error Handling ✅
- SdkStateBridge: ServiceAction dispatch wrapped in try/catch (prevents bridge death)
- Favorite/Ignore/Mute: local state update only applied on admin call success
- SdkRadioController: sendMessage + sendRemoteAdmin log errors before re-throwing
- ImportContact: guarded with runCatching
### UseCases Deleted ✅
- ProcessRadioResponse (tests only — impl kept, has real packet parsing logic)
- AdminActions (tests only — impl kept, has real reboot/reset logic)

View File

@@ -41,25 +41,25 @@ import kotlin.test.assertTrue
import org.meshtastic.proto.NodeInfo as ProtoNodeInfo
import org.meshtastic.proto.Position as ProtoPosition
class NodeManagerImplTest {
class SdkNodeRepositoryImplTest {
private val notificationManager: NotificationManager = mock(MockMode.autofill)
private val testScope = TestScope()
private lateinit var nodeManager: SdkNodeRepositoryImpl
private lateinit var nodeRepository: SdkNodeRepositoryImpl
@BeforeTest
fun setUp() {
val dbProvider: DatabaseProvider = mock(MockMode.autofill)
val localStatsDataSource: LocalStatsDataSource = mock(MockMode.autofill)
nodeManager = SdkNodeRepositoryImpl(localStatsDataSource, dbProvider, notificationManager, testScope)
nodeRepository = SdkNodeRepositoryImpl(localStatsDataSource, dbProvider, notificationManager, testScope)
}
@Test
fun `getOrCreateNode creates default user for unknown node`() {
val nodeNum = 1234
nodeManager.updateNode(nodeNum) { it }
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
nodeRepository.updateNode(nodeNum) { it }
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertNotNull(result)
assertEquals(nodeNum, result.num)
@@ -73,14 +73,14 @@ class NodeManagerImplTest {
val existingUser =
User(id = "!12345678", long_name = "My Custom Name", short_name = "MCN", hw_model = HardwareModel.TLORA_V2)
nodeManager.updateNode(nodeNum) { it.copy(user = existingUser) }
nodeRepository.updateNode(nodeNum) { it.copy(user = existingUser) }
val incomingDefaultUser =
User(id = "!12345678", long_name = "Meshtastic 5678", short_name = "5678", hw_model = HardwareModel.UNSET)
nodeManager.handleReceivedUser(nodeNum, incomingDefaultUser)
nodeRepository.handleReceivedUser(nodeNum, incomingDefaultUser)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertEquals("My Custom Name", result!!.user.long_name)
assertEquals(HardwareModel.TLORA_V2, result.user.hw_model)
}
@@ -91,14 +91,14 @@ class NodeManagerImplTest {
val existingUser =
User(id = "!12345678", long_name = "Old Name", short_name = "ON", hw_model = HardwareModel.TLORA_V2)
nodeManager.updateNode(nodeNum) { it.copy(user = existingUser) }
nodeRepository.updateNode(nodeNum) { it.copy(user = existingUser) }
val incomingDetailedUser =
User(id = "!12345678", long_name = "Real User", short_name = "RU", hw_model = HardwareModel.TLORA_V1)
nodeManager.handleReceivedUser(nodeNum, incomingDetailedUser)
nodeRepository.handleReceivedUser(nodeNum, incomingDetailedUser)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertEquals("Real User", result!!.user.long_name)
assertEquals(HardwareModel.TLORA_V1, result.user.hw_model)
}
@@ -108,9 +108,9 @@ class NodeManagerImplTest {
val nodeNum = 1234
val position = ProtoPosition(latitude_i = 450000000, longitude_i = 900000000)
nodeManager.handleReceivedPosition(nodeNum, 9999, position, 0)
nodeRepository.handleReceivedPosition(nodeNum, 9999, position, 0)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertNotNull(result)
assertNotNull(result.position)
assertEquals(450000000, result.position.latitude_i)
@@ -121,13 +121,13 @@ class NodeManagerImplTest {
fun `handleReceivedPosition with zero coordinates preserves last known location but updates satellites`() {
val nodeNum = 1234
val initialPosition = ProtoPosition(latitude_i = 450000000, longitude_i = 900000000, sats_in_view = 10)
nodeManager.handleReceivedPosition(nodeNum, 9999, initialPosition, 1000000L)
nodeRepository.handleReceivedPosition(nodeNum, 9999, initialPosition, 1000000L)
// Receive "zero" position with new satellite count
val zeroPosition = ProtoPosition(latitude_i = 0, longitude_i = 0, sats_in_view = 5, time = 1001)
nodeManager.handleReceivedPosition(nodeNum, 9999, zeroPosition, 1001000L)
nodeRepository.handleReceivedPosition(nodeNum, 9999, zeroPosition, 1001000L)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertEquals(450000000, result!!.position.latitude_i)
assertEquals(900000000, result.position.longitude_i)
assertEquals(5, result.position.sats_in_view)
@@ -139,9 +139,9 @@ class NodeManagerImplTest {
val myNum = 1111
val emptyPos = ProtoPosition(latitude_i = 0, longitude_i = 0, sats_in_view = 0, time = 0)
nodeManager.handleReceivedPosition(myNum, myNum, emptyPos, 0)
nodeRepository.handleReceivedPosition(myNum, myNum, emptyPos, 0)
val result = nodeManager.nodeDBbyNodeNum[myNum]
val result = nodeRepository.nodeDBbyNodeNum[myNum]
// Should still be null since the empty position for local node is ignored
assertNull(result)
}
@@ -149,13 +149,13 @@ class NodeManagerImplTest {
@Test
fun `handleReceivedTelemetry updates lastHeard`() {
val nodeNum = 1234
nodeManager.updateNode(nodeNum) { it.copy(lastHeard = 1000) }
nodeRepository.updateNode(nodeNum) { it.copy(lastHeard = 1000) }
val telemetry = Telemetry(time = 2000, device_metrics = DeviceMetrics(battery_level = 50))
nodeManager.handleReceivedTelemetry(nodeNum, telemetry)
nodeRepository.handleReceivedTelemetry(nodeNum, telemetry)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertEquals(2000, result!!.lastHeard)
}
@@ -164,9 +164,9 @@ class NodeManagerImplTest {
val nodeNum = 1234
val telemetry = Telemetry(device_metrics = DeviceMetrics(battery_level = 75, voltage = 3.8f))
nodeManager.handleReceivedTelemetry(nodeNum, telemetry)
nodeRepository.handleReceivedTelemetry(nodeNum, telemetry)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertNotNull(result!!.deviceMetrics)
assertEquals(75, result.deviceMetrics.battery_level)
assertEquals(3.8f, result.deviceMetrics.voltage)
@@ -178,9 +178,9 @@ class NodeManagerImplTest {
val telemetry =
Telemetry(environment_metrics = EnvironmentMetrics(temperature = 22.5f, relative_humidity = 45.0f))
nodeManager.handleReceivedTelemetry(nodeNum, telemetry)
nodeRepository.handleReceivedTelemetry(nodeNum, telemetry)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]
assertNotNull(result!!.environmentMetrics)
assertEquals(22.5f, result.environmentMetrics.temperature)
assertEquals(45.0f, result.environmentMetrics.relative_humidity)
@@ -188,23 +188,23 @@ class NodeManagerImplTest {
@Test
fun `clear resets internal state`() {
nodeManager.updateNode(1234) { it.copy(user = it.user.copy(long_name = "Test")) }
nodeManager.clear()
nodeRepository.updateNode(1234) { it.copy(user = it.user.copy(long_name = "Test")) }
nodeRepository.clear()
assertTrue(nodeManager.nodeDBbyNodeNum.isEmpty())
assertTrue(nodeManager.nodeDBbyID.isEmpty())
assertNull(nodeManager.myNodeNum.value)
assertTrue(nodeRepository.nodeDBbyNodeNum.isEmpty())
assertTrue(nodeRepository.nodeDBbyID.isEmpty())
assertNull(nodeRepository.myNodeNum.value)
}
@Test
fun `toNodeID returns broadcast ID for broadcast nodeNum`() {
val result = nodeManager.toNodeID(DataPacket.NODENUM_BROADCAST)
val result = nodeRepository.toNodeID(DataPacket.NODENUM_BROADCAST)
assertEquals(DataPacket.ID_BROADCAST, result)
}
@Test
fun `toNodeID returns default hex ID for unknown node`() {
val result = nodeManager.toNodeID(0x1234)
val result = nodeRepository.toNodeID(0x1234)
assertEquals(DataPacket.nodeNumToDefaultId(0x1234), result)
}
@@ -212,24 +212,24 @@ class NodeManagerImplTest {
fun `toNodeID returns user ID for known node`() {
val nodeNum = 5678
val userId = "!customid"
nodeManager.updateNode(nodeNum) { it.copy(user = it.user.copy(id = userId)) }
val result = nodeManager.toNodeID(nodeNum)
nodeRepository.updateNode(nodeNum) { it.copy(user = it.user.copy(id = userId)) }
val result = nodeRepository.toNodeID(nodeNum)
assertEquals(userId, result)
}
@Test
fun `removeByNodenum removes node from both maps`() {
val nodeNum = 1234
nodeManager.updateNode(nodeNum) {
nodeRepository.updateNode(nodeNum) {
Node(num = nodeNum, user = User(id = "!testnode", long_name = "Test", short_name = "T"))
}
assertTrue(nodeManager.nodeDBbyNodeNum.containsKey(nodeNum))
assertTrue(nodeManager.nodeDBbyID.containsKey("!testnode"))
assertTrue(nodeRepository.nodeDBbyNodeNum.containsKey(nodeNum))
assertTrue(nodeRepository.nodeDBbyID.containsKey("!testnode"))
nodeManager.removeByNodenum(nodeNum)
nodeRepository.removeByNodenum(nodeNum)
assertTrue(!nodeManager.nodeDBbyNodeNum.containsKey(nodeNum))
assertTrue(!nodeManager.nodeDBbyID.containsKey("!testnode"))
assertTrue(!nodeRepository.nodeDBbyNodeNum.containsKey(nodeNum))
assertTrue(!nodeRepository.nodeDBbyID.containsKey("!testnode"))
}
@Test
@@ -238,7 +238,7 @@ class NodeManagerImplTest {
val pk = ByteArray(32) { (it + 1).toByte() }.toByteString()
val existingUser =
User(id = "!12345678", long_name = "Existing", short_name = "EX", hw_model = HardwareModel.TLORA_V2)
nodeManager.updateNode(nodeNum) { it.copy(user = existingUser) }
nodeRepository.updateNode(nodeNum) { it.copy(user = existingUser) }
val incomingUser =
User(
@@ -248,9 +248,9 @@ class NodeManagerImplTest {
hw_model = HardwareModel.TLORA_V2,
public_key = pk,
)
nodeManager.handleReceivedUser(nodeNum, incomingUser)
nodeRepository.handleReceivedUser(nodeNum, incomingUser)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]!!
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]!!
assertEquals(pk, result.publicKey)
assertEquals(pk, result.user.public_key)
assertTrue(result.hasPKC)
@@ -268,7 +268,7 @@ class NodeManagerImplTest {
hw_model = HardwareModel.TLORA_V2,
public_key = existingPk,
)
nodeManager.updateNode(nodeNum) { it.copy(user = existingUser, publicKey = existingPk) }
nodeRepository.updateNode(nodeNum) { it.copy(user = existingUser, publicKey = existingPk) }
val differentPk = ByteArray(32) { (it + 10).toByte() }.toByteString()
val incomingUser =
@@ -279,9 +279,9 @@ class NodeManagerImplTest {
hw_model = HardwareModel.TLORA_V2,
public_key = differentPk,
)
nodeManager.handleReceivedUser(nodeNum, incomingUser)
nodeRepository.handleReceivedUser(nodeNum, incomingUser)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]!!
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]!!
// Key mismatch: newUser gets public_key cleared to EMPTY, and publicKey should match
assertEquals(ByteString.EMPTY, result.publicKey)
assertEquals(ByteString.EMPTY, result.user.public_key)
@@ -301,9 +301,9 @@ class NodeManagerImplTest {
)
val info = ProtoNodeInfo(num = nodeNum, user = user, last_heard = 1000, channel = 0)
nodeManager.installNodeInfo(info)
nodeRepository.installNodeInfo(info)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]!!
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]!!
assertEquals(pk, result.publicKey)
assertEquals(pk, result.user.public_key)
assertTrue(result.hasPKC)
@@ -324,9 +324,9 @@ class NodeManagerImplTest {
)
val info = ProtoNodeInfo(num = nodeNum, user = user, last_heard = 1000, channel = 0)
nodeManager.installNodeInfo(info)
nodeRepository.installNodeInfo(info)
val result = nodeManager.nodeDBbyNodeNum[nodeNum]!!
val result = nodeRepository.nodeDBbyNodeNum[nodeNum]!!
assertEquals(ByteString.EMPTY, result.publicKey)
assertEquals(ByteString.EMPTY, result.user.public_key)
}