Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,11 @@ interface NodeInfoReadDataSource {
suspend fun getNodesOlderThan(lastHeard: Int): List<NodeEntity>

suspend fun getUnknownNodes(): List<NodeEntity>

/**
* One-shot snapshot of every node row in the currently selected database. Bypasses the process-wide
* [nodeDBbyNumFlow] cache so callers see the live DB state at invocation time rather than a `stateIn` value that
* may belong to a previous transport.
*/
suspend fun getNodeDbSnapshot(): Map<Int, NodeWithRelations>
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,7 @@ class SwitchingNodeInfoReadDataSource(private val dbManager: DatabaseProvider) :

override suspend fun getUnknownNodes(): List<NodeEntity> =
dbManager.withReadDb { it.nodeInfoDao().getUnknownNodes() }

override suspend fun getNodeDbSnapshot(): Map<Int, NodeWithRelations> =
dbManager.withReadDb { it.nodeInfoDao().nodeDBbyNumSnapshot() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ class MeshConfigFlowManagerImpl(
if (!isActiveSession(session)) return
if (removedNums.isNotEmpty()) {
Logger.i { "Config install migrated ${removedNums.size} stale node identit(y/ies)" }
removedNums.forEach(nodeManager::removeByNodenum)
nodeManager.applyTrustedIdentityMigrations(removedNums)
}

val published =
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ class NodeRepositoryImpl(
override suspend fun getUnknownNodes(): List<Node> =
withContext(dispatchers.io) { nodeInfoReadDataSource.getUnknownNodes().map { it.toModel() } }

override suspend fun getNodeDbSnapshot(): Map<Int, Node> =
withContext(dispatchers.io) { nodeInfoReadDataSource.getNodeDbSnapshot().mapValues { (_, it) -> it.toModel() } }

/** Persists hardware metadata for a node. */
override suspend fun insertMetadata(nodeNum: Int, metadata: DeviceMetadata) =
withContext(dispatchers.io) { nodeInfoWriteDataSource.upsert(MetadataEntity(nodeNum, metadata)) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,37 @@ class MeshConfigFlowManagerImplTest {
verifySuspend { connectionManager.onNodeDbReady() }
}

@Test
fun `Stage 2 applies trusted migrations before readiness and replay`() = testScope.runTest {
val retiredNum = 456
val callOrder = mutableListOf<String>()
everySuspend { nodeRepository.installConfig(any(), any()) } calls
{
callOrder.add("installConfig")
listOf(retiredNum)
}
every { nodeManager.applyTrustedIdentityMigrations(any()) } calls { callOrder.add("applyMigrations") }
every { nodeManager.setNodeDbReady(true) } calls { callOrder.add("nodeDbReady") }
every { nodeManager.setAllowNodeDbWrites(true) } calls { callOrder.add("writesReady") }
everySuspend { connectionManager.onNodeDbReady() } calls { callOrder.add("replayReady") }

handleMyInfo(protoMyNodeInfo)
advanceUntilIdle()
manager.handleLocalMetadata(metadata)
advanceUntilIdle()
manager.handleConfigComplete(HandshakeConstants.CONFIG_NONCE)
advanceTimeBy(STAGE_TRANSITION_ADVANCE_MS)
runCurrent()
manager.handleConfigComplete(HandshakeConstants.NODE_INFO_NONCE)
advanceUntilIdle()

assertEquals(
listOf("installConfig", "applyMigrations", "nodeDbReady", "writesReady", "replayReady"),
callOrder,
)
verify { nodeManager.applyTrustedIdentityMigrations(listOf(retiredNum)) }
}

@Test
fun `Stage 2 complete id ignored when not in ReceivingNodeInfo state`() = testScope.runTest {
manager.handleConfigComplete(HandshakeConstants.NODE_INFO_NONCE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.meshtastic.core.data.manager
import dev.mokkery.MockMode
import dev.mokkery.answering.returns
import dev.mokkery.every
import dev.mokkery.everySuspend
import dev.mokkery.mock
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -55,6 +56,7 @@ class NodeManagerConnectionIdentityTest {

@BeforeTest
fun setUp() {
everySuspend { nodeRepository.getNodeDbSnapshot() } returns emptyMap()
nodeManager = NodeManagerImpl(nodeRepository, notificationManager, radioInterfaceService, testScope)
}

Expand Down
Loading