mirror of
https://github.com/torlando-tech/columba.git
synced 2025-12-22 05:37:07 +00:00
fix: resolve unit test failures from location sharing integration
- Add enablePeriodicRefresh companion property to MapViewModel to disable infinite loop during tests - Add missing getEnrichedContacts() mock to MessagingViewModelTest - Add missing locationSharingState and contacts mocks to MessagingScreenTest - Add mock locationSharingManager.activeSessions to all affected tests - Update MapViewModelTest to use getLatestLocationsPerSenderUnfiltered - Add mock AnnounceStreamViewModel to ContactsScreenTest with proper StateFlow mocks for isAnnouncing, announceSuccess, announceError All 2416 unit tests now pass in ~1m20s (was timing out at 25+ mins). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComplexCondition:MainActivity.kt$!isOnWelcomeScreen && !isOnMessagingScreen && !isOnAnnounceDetailScreen && !isOnInterfaceManagementScreen && !isOnBleConnectionStatusScreen && !isOnThemeManagementScreen && !isOnThemeEditorScreen && !isOnRNodeWizardScreen</ID>
|
||||
<ID>CyclomaticComplexMethod:ColumbaApplication.kt$ColumbaApplication$override fun onCreate()</ID>
|
||||
<ID>CyclomaticComplexMethod:ConfigFileParser.kt$ConfigFileParser$private fun parseConfigLines(lines: List<String>): List<InterfaceConfig></ID>
|
||||
<ID>CyclomaticComplexMethod:DebugViewModel.kt$DebugViewModel$private fun fetchDebugInfo()</ID>
|
||||
@@ -16,20 +15,20 @@
|
||||
<ID>ImplicitDefaultLocale:ThemeColorGenerator.kt$ThemeColorGenerator$String.format("#%06X", argb and 0xFFFFFF)</ID>
|
||||
<ID>ImplicitDefaultLocale:ThemeColorGenerator.kt$ThemeColorGenerator$String.format("#%08X", argb)</ID>
|
||||
<ID>InstanceOfCheckForException:ServiceNotificationManager.kt$ServiceNotificationManager$e is android.app.ForegroundServiceStartNotAllowedException</ID>
|
||||
<ID>LargeClass:MessagingViewModel.kt$MessagingViewModel : ViewModel</ID>
|
||||
<ID>LargeClass:ReticulumServiceBinder.kt$ReticulumServiceBinder : Stub</ID>
|
||||
<ID>LargeClass:ServiceReticulumProtocol.kt$ServiceReticulumProtocol : ReticulumProtocol</ID>
|
||||
<ID>LongMethod:ColumbaApplication.kt$ColumbaApplication$override fun onCreate()</ID>
|
||||
<ID>LongMethod:ConfigFileParser.kt$ConfigFileParser$private fun parseConfigLines(lines: List<String>): List<InterfaceConfig></ID>
|
||||
<ID>LongMethod:InterfaceRepository.kt$InterfaceRepository$fun entityToConfig(entity: InterfaceEntity): InterfaceConfig</ID>
|
||||
<ID>LongMethod:MessageCollector.kt$MessageCollector$fun startCollecting()</ID>
|
||||
<ID>LongMethod:MessagingViewModel.kt$MessagingViewModel$fun sendMessage( destinationHash: String, content: String, )</ID>
|
||||
<ID>LongParameterList:BleTestFixtures.kt$BleTestFixtures$( identityHash: String = "abcd1234efgh5678ijkl9012mnop3456", peerName: String = "RNS-TestPeer", currentMac: String = "AA:BB:CC:DD:EE:FF", connectionType: ConnectionType = ConnectionType.BOTH, rssi: Int = -70, mtu: Int = 512, connectedAt: Long = System.currentTimeMillis() - 60000, // 1 minute ago firstSeen: Long = System.currentTimeMillis() - 300000, // 5 minutes ago lastSeen: Long = System.currentTimeMillis(), bytesReceived: Long = 1024, bytesSent: Long = 2048, packetsReceived: Long = 10, packetsSent: Long = 20, successRate: Double = 1.0, )</ID>
|
||||
<ID>LongParameterList:MarkerBitmapFactory.kt$MarkerBitmapFactory$( sizeDp: Float = 28f, fillColor: Int, strokeColor: Int, fillOpacity: Float = 0.6f, strokeWidthDp: Float = 3f, dashLengthDp: Float = 4f, gapLengthDp: Float = 4f, density: Float, )</ID>
|
||||
<ID>LongParameterList:ReticulumServiceBinder.kt$ReticulumServiceBinder$( private val context: Context, private val state: ServiceState, private val wrapperManager: PythonWrapperManager, private val identityManager: IdentityManager, private val routingManager: RoutingManager, private val messagingManager: MessagingManager, private val pollingManager: PollingManager, private val broadcaster: CallbackBroadcaster, private val lockManager: LockManager, private val maintenanceManager: MaintenanceManager, private val notificationManager: ServiceNotificationManager, private val bleCoordinator: BleCoordinator, private val scope: CoroutineScope, private val onInitialized: () -> Unit, private val onShutdown: () -> Unit, private val onForceExit: () -> Unit, )</ID>
|
||||
<ID>LoopWithTooManyJumpStatements:ConfigFileParser.kt$ConfigFileParser$for</ID>
|
||||
<ID>LoopWithTooManyJumpStatements:RNodeWizardViewModel.kt$RNodeWizardViewModel$while</ID>
|
||||
<ID>LoopWithTooManyJumpStatements:SettingsViewModel.kt$SettingsViewModel$while</ID>
|
||||
<ID>MaxLineLength:BleTestFixtures.kt$BleTestFixtures$"""{"identityHash":"${connection.identityHash}","peerName":"${connection.peerName}","currentMac":"${connection.currentMac}","hasCentralConnection":$hasCentral,"hasPeripheralConnection":$hasPeripheral,"mtu":${connection.mtu},"connectedAt":${connection.connectedAt},"firstSeen":${connection.firstSeen},"lastSeen":${connection.lastSeen},"rssi":${connection.rssi}}"""</ID>
|
||||
<ID>MaxLineLength:MainActivity.kt$// Only show NavigationBar when NOT on messaging screen, announce detail screen, interface management screen, BLE connection status screen, theme screens, welcome screen, or RNode wizard</ID>
|
||||
<ID>MaxLineLength:MainActivity.kt$if</ID>
|
||||
<ID>MaxLineLength:SettingsViewModel.kt$SettingsViewModel$"Settings updated: displayName=${newState.displayName}, autoAnnounce=${newState.autoAnnounceEnabled}, interval=${newState.autoAnnounceIntervalMinutes}min, theme=${newState.selectedTheme}, customThemes=${newState.customThemes.size}"</ID>
|
||||
<ID>NestedBlockDepth:ConfigFileParser.kt$ConfigFileParser$private fun parseConfigLines(lines: List<String>): List<InterfaceConfig></ID>
|
||||
<ID>NestedBlockDepth:SettingsViewModel.kt$SettingsViewModel$private suspend fun loadIdentityInfo(): Pair<String?, String?></ID>
|
||||
@@ -45,6 +44,7 @@
|
||||
<ID>SwallowedException:IdentityManagerViewModel.kt$IdentityManagerViewModel$e: Exception</ID>
|
||||
<ID>SwallowedException:IdentityQrCodeUtils.kt$IdentityQrCodeUtils$e: Exception</ID>
|
||||
<ID>SwallowedException:InterfaceConfigManager.kt$InterfaceConfigManager$e: Exception</ID>
|
||||
<ID>SwallowedException:LocationSharingCard.kt$e: IllegalArgumentException</ID>
|
||||
<ID>SwallowedException:NodeTypeDetector.kt$NodeTypeDetector$e: Exception</ID>
|
||||
<ID>SwallowedException:NotificationHelper.kt$NotificationHelper$e: SecurityException</ID>
|
||||
<ID>SwallowedException:QrCodeComposables.kt$e: Exception</ID>
|
||||
@@ -68,7 +68,6 @@
|
||||
<ID>TooGenericExceptionThrown:InterfaceConfigManager.kt$InterfaceConfigManager$throw Exception("Failed to initialize Reticulum: ${error.message}", error)</ID>
|
||||
<ID>TooGenericExceptionThrown:ReticulumServiceErrorHandlingTest.kt$ReticulumServiceErrorHandlingTest$throw RuntimeException("Real error")</ID>
|
||||
<ID>TooGenericExceptionThrown:ServiceReticulumProtocol.kt$ServiceReticulumProtocol$throw RuntimeException( "Missing identity fields in response. hash=$hashStr, publicKey=$publicKeyStr, privateKey=$privateKeyStr", )</ID>
|
||||
<ID>TooGenericExceptionThrown:ServiceReticulumProtocol.kt$ServiceReticulumProtocol$throw RuntimeException("Failed to restore peer identities: $error")</ID>
|
||||
<ID>TooGenericExceptionThrown:ServiceReticulumProtocol.kt$ServiceReticulumProtocol$throw RuntimeException("Service entered ERROR state: $status")</ID>
|
||||
<ID>TooGenericExceptionThrown:ServiceReticulumProtocol.kt$ServiceReticulumProtocol$throw RuntimeException("Timeout waiting for service to become READY (status: ${networkStatus.value})")</ID>
|
||||
<ID>TooGenericExceptionThrown:ServiceReticulumProtocol.kt$ServiceReticulumProtocol$throw RuntimeException(error)</ID>
|
||||
@@ -76,6 +75,7 @@
|
||||
<ID>TooGenericExceptionThrown:ServiceReticulumProtocol.kt$ServiceReticulumProtocol$throw RuntimeException(result.optString("error", "Unknown error"))</ID>
|
||||
<ID>TooManyFunctions:InputValidator.kt$InputValidator</ID>
|
||||
<ID>TooManyFunctions:InterfaceManagementViewModel.kt$InterfaceManagementViewModel : ViewModel</ID>
|
||||
<ID>TooManyFunctions:LocationSharingManager.kt$LocationSharingManager</ID>
|
||||
<ID>TooManyFunctions:RNodeWizardViewModel.kt$RNodeWizardViewModel : ViewModel</ID>
|
||||
<ID>TooManyFunctions:ReticulumServiceBinder.kt$ReticulumServiceBinder : Stub</ID>
|
||||
<ID>TooManyFunctions:ServiceReticulumProtocol.kt$ServiceReticulumProtocol : ReticulumProtocol</ID>
|
||||
@@ -94,8 +94,6 @@
|
||||
<ID>UnusedPrivateMember:MessagingViewModel.kt$MessagingViewModel$private fun DataMessage.toReticulumMessage()</ID>
|
||||
<ID>UnusedPrivateProperty:DebugViewModel.kt$DebugViewModel$private val settingsRepository: SettingsRepository</ID>
|
||||
<ID>UnusedPrivateProperty:DebugViewModel.kt$DebugViewModel$val destHash = _publicKey.value ?: return null</ID>
|
||||
<ID>UnusedPrivateProperty:DebugViewModel.kt$DebugViewModel$val isReady = status is com.lxmf.messenger.reticulum.model.NetworkStatus.READY</ID>
|
||||
<ID>UnusedPrivateProperty:DebugViewModel.kt$DebugViewModel$val statusString = when (status) { is com.lxmf.messenger.reticulum.model.NetworkStatus.READY -> "READY" is com.lxmf.messenger.reticulum.model.NetworkStatus.INITIALIZING -> "INITIALIZING" is com.lxmf.messenger.reticulum.model.NetworkStatus.SHUTDOWN -> "SHUTDOWN" is com.lxmf.messenger.reticulum.model.NetworkStatus.ERROR -> "ERROR: ${status.message}" else -> status.toString() }</ID>
|
||||
<ID>UnusedPrivateProperty:DebugViewModel.kt$DebugViewModel.Companion$private const val DEFAULT_IDENTITY_FILE = "default_identity"</ID>
|
||||
<ID>UnusedPrivateProperty:IdentityScreen.kt$val context = LocalContext.current</ID>
|
||||
<ID>UnusedPrivateProperty:InterfaceConfigManager.kt$InterfaceConfigManager$val serviceConnection = (reticulumProtocol as ServiceReticulumProtocol)</ID>
|
||||
|
||||
@@ -95,6 +95,13 @@ class MapViewModel
|
||||
private const val STALE_THRESHOLD_MS = 5 * 60 * 1000L // 5 minutes
|
||||
private const val GRACE_PERIOD_MS = 60 * 60 * 1000L // 1 hour
|
||||
private const val REFRESH_INTERVAL_MS = 30_000L // 30 seconds
|
||||
|
||||
/**
|
||||
* Controls whether periodic refresh is enabled.
|
||||
* Set to false in tests to prevent infinite coroutine loops.
|
||||
* @suppress VisibleForTesting
|
||||
*/
|
||||
internal var enablePeriodicRefresh = true
|
||||
}
|
||||
|
||||
private val _state = MutableStateFlow(MapState())
|
||||
@@ -196,10 +203,13 @@ class MapViewModel
|
||||
|
||||
// Periodic refresh to update stale states (every 30 seconds)
|
||||
// This ensures markers transition from FRESH -> STALE as time passes
|
||||
viewModelScope.launch {
|
||||
while (isActive) {
|
||||
delay(REFRESH_INTERVAL_MS)
|
||||
_refreshTrigger.value = System.currentTimeMillis()
|
||||
// Can be disabled for tests via enablePeriodicRefresh companion property
|
||||
if (enablePeriodicRefresh) {
|
||||
viewModelScope.launch {
|
||||
while (isActive) {
|
||||
delay(REFRESH_INTERVAL_MS)
|
||||
_refreshTrigger.value = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.lxmf.messenger.service.RelayInfo
|
||||
import com.lxmf.messenger.test.RegisterComponentActivityRule
|
||||
import com.lxmf.messenger.test.TestFactories
|
||||
import com.lxmf.messenger.viewmodel.AddContactResult
|
||||
import com.lxmf.messenger.viewmodel.AnnounceStreamViewModel
|
||||
import com.lxmf.messenger.viewmodel.ContactGroups
|
||||
import com.lxmf.messenger.viewmodel.ContactsViewModel
|
||||
import io.mockk.coEvery
|
||||
@@ -85,7 +86,7 @@ class ContactsScreenTest {
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("No contacts yet").assertIsDisplayed()
|
||||
@@ -98,7 +99,7 @@ class ContactsScreenTest {
|
||||
val mockViewModel = createMockContactsViewModel()
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("Contacts").assertIsDisplayed()
|
||||
@@ -117,7 +118,7 @@ class ContactsScreenTest {
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("1 contact").assertIsDisplayed()
|
||||
@@ -138,7 +139,7 @@ class ContactsScreenTest {
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("3 contacts").assertIsDisplayed()
|
||||
@@ -149,7 +150,7 @@ class ContactsScreenTest {
|
||||
val mockViewModel = createMockContactsViewModel()
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithContentDescription("Search").assertIsDisplayed()
|
||||
@@ -161,7 +162,7 @@ class ContactsScreenTest {
|
||||
// val mockViewModel = createMockContactsViewModel()
|
||||
//
|
||||
// composeTestRule.setContent {
|
||||
// ContactsScreen(viewModel = mockViewModel)
|
||||
// ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
// }
|
||||
//
|
||||
// composeTestRule.onNodeWithContentDescription("Add contact").assertIsDisplayed()
|
||||
@@ -174,7 +175,7 @@ class ContactsScreenTest {
|
||||
val mockViewModel = createMockContactsViewModel()
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
// Initially search bar is hidden
|
||||
@@ -192,7 +193,7 @@ class ContactsScreenTest {
|
||||
val mockViewModel = createMockContactsViewModel()
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
// Open search bar
|
||||
@@ -211,7 +212,7 @@ class ContactsScreenTest {
|
||||
val mockViewModel = createMockContactsViewModel()
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
// Open search bar
|
||||
@@ -229,7 +230,7 @@ class ContactsScreenTest {
|
||||
val mockViewModel = createMockContactsViewModel(searchQuery = "Test")
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
// Open search bar
|
||||
@@ -261,7 +262,7 @@ class ContactsScreenTest {
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("MY RELAY").assertIsDisplayed()
|
||||
@@ -284,7 +285,7 @@ class ContactsScreenTest {
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("(auto)").assertIsDisplayed()
|
||||
@@ -306,7 +307,7 @@ class ContactsScreenTest {
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(viewModel = mockViewModel)
|
||||
ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText("PINNED").assertIsDisplayed()
|
||||
@@ -339,7 +340,7 @@ class ContactsScreenTest {
|
||||
// )
|
||||
//
|
||||
// composeTestRule.setContent {
|
||||
// ContactsScreen(viewModel = mockViewModel)
|
||||
// ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
// }
|
||||
//
|
||||
// // When there are pinned contacts, "ALL CONTACTS" header is shown
|
||||
@@ -366,7 +367,7 @@ class ContactsScreenTest {
|
||||
// )
|
||||
//
|
||||
// composeTestRule.setContent {
|
||||
// ContactsScreen(viewModel = mockViewModel)
|
||||
// ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
// }
|
||||
//
|
||||
// composeTestRule.onNodeWithText("MY RELAY").assertIsDisplayed()
|
||||
@@ -1122,7 +1123,7 @@ class ContactsScreenTest {
|
||||
// val mockViewModel = createMockContactsViewModel()
|
||||
//
|
||||
// composeTestRule.setContent {
|
||||
// ContactsScreen(viewModel = mockViewModel)
|
||||
// ContactsScreen(viewModel = mockViewModel, announceViewModel = createMockAnnounceStreamViewModel())
|
||||
// }
|
||||
//
|
||||
// // Click add button
|
||||
@@ -1152,6 +1153,7 @@ class ContactsScreenTest {
|
||||
composeTestRule.setContent {
|
||||
ContactsScreen(
|
||||
viewModel = mockViewModel,
|
||||
announceViewModel = createMockAnnounceStreamViewModel(),
|
||||
onContactClick = { hash, name ->
|
||||
clickedHash = hash
|
||||
clickedName = name
|
||||
@@ -1191,4 +1193,12 @@ class ContactsScreenTest {
|
||||
|
||||
return mockViewModel
|
||||
}
|
||||
|
||||
private fun createMockAnnounceStreamViewModel(): AnnounceStreamViewModel {
|
||||
val mock = mockk<AnnounceStreamViewModel>(relaxed = true)
|
||||
every { mock.isAnnouncing } returns MutableStateFlow(false)
|
||||
every { mock.announceSuccess } returns MutableStateFlow(false)
|
||||
every { mock.announceError } returns MutableStateFlow(null)
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.compose.ui.test.performTextInput
|
||||
import androidx.paging.PagingData
|
||||
import com.lxmf.messenger.test.MessagingTestFixtures
|
||||
import com.lxmf.messenger.test.RegisterComponentActivityRule
|
||||
import com.lxmf.messenger.ui.model.LocationSharingState
|
||||
import com.lxmf.messenger.viewmodel.ContactToggleResult
|
||||
import com.lxmf.messenger.viewmodel.MessagingViewModel
|
||||
import io.mockk.every
|
||||
@@ -66,6 +67,9 @@ class MessagingScreenTest {
|
||||
every { mockViewModel.totalAttachmentSize } returns MutableStateFlow(0)
|
||||
every { mockViewModel.fileAttachmentError } returns MutableSharedFlow()
|
||||
every { mockViewModel.isProcessingFile } returns MutableStateFlow(false)
|
||||
// Location sharing mocks
|
||||
every { mockViewModel.contacts } returns MutableStateFlow(emptyList())
|
||||
every { mockViewModel.locationSharingState } returns MutableStateFlow(LocationSharingState.NONE)
|
||||
}
|
||||
|
||||
// ========== Empty State Tests ==========
|
||||
|
||||
@@ -55,13 +55,16 @@ class MapViewModelTest {
|
||||
@Before
|
||||
fun setup() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
// Disable periodic refresh to prevent infinite loops in tests
|
||||
MapViewModel.enablePeriodicRefresh = false
|
||||
|
||||
contactRepository = mockk(relaxed = true)
|
||||
receivedLocationDao = mockk(relaxed = true)
|
||||
locationSharingManager = mockk(relaxed = true)
|
||||
announceDao = mockk(relaxed = true)
|
||||
|
||||
every { contactRepository.getEnrichedContacts() } returns flowOf(emptyList())
|
||||
every { receivedLocationDao.getLatestLocationsPerSender(any()) } returns flowOf(emptyList())
|
||||
every { receivedLocationDao.getLatestLocationsPerSenderUnfiltered() } returns flowOf(emptyList())
|
||||
every { receivedLocationDao.getLatestLocationsPerSenderUnfiltered() } returns flowOf(emptyList())
|
||||
every { announceDao.getAllAnnounces() } returns flowOf(emptyList())
|
||||
every { locationSharingManager.isSharing } returns MutableStateFlow(false)
|
||||
@@ -71,6 +74,8 @@ class MapViewModelTest {
|
||||
@After
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain()
|
||||
// Re-enable periodic refresh for other tests
|
||||
MapViewModel.enablePeriodicRefresh = true
|
||||
clearAllMocks()
|
||||
}
|
||||
|
||||
@@ -204,7 +209,7 @@ class MapViewModelTest {
|
||||
),
|
||||
)
|
||||
every { contactRepository.getEnrichedContacts() } returns flowOf(contacts)
|
||||
every { receivedLocationDao.getLatestLocationsPerSender(any()) } returns flowOf(receivedLocations)
|
||||
every { receivedLocationDao.getLatestLocationsPerSenderUnfiltered() } returns flowOf(receivedLocations)
|
||||
|
||||
viewModel = MapViewModel(contactRepository, receivedLocationDao, locationSharingManager, announceDao)
|
||||
|
||||
@@ -238,7 +243,7 @@ class MapViewModelTest {
|
||||
),
|
||||
)
|
||||
every { contactRepository.getEnrichedContacts() } returns flowOf(contacts)
|
||||
every { receivedLocationDao.getLatestLocationsPerSender(any()) } returns flowOf(receivedLocations)
|
||||
every { receivedLocationDao.getLatestLocationsPerSenderUnfiltered() } returns flowOf(receivedLocations)
|
||||
|
||||
viewModel = MapViewModel(contactRepository, receivedLocationDao, locationSharingManager, announceDao)
|
||||
|
||||
@@ -314,7 +319,7 @@ class MapViewModelTest {
|
||||
),
|
||||
)
|
||||
every { contactRepository.getEnrichedContacts() } returns flowOf(contacts)
|
||||
every { receivedLocationDao.getLatestLocationsPerSender(any()) } returns flowOf(receivedLocations)
|
||||
every { receivedLocationDao.getLatestLocationsPerSenderUnfiltered() } returns flowOf(receivedLocations)
|
||||
|
||||
viewModel = MapViewModel(contactRepository, receivedLocationDao, locationSharingManager, announceDao)
|
||||
val newLocation = createMockLocation(40.7128, -74.0060) // New York
|
||||
@@ -353,7 +358,7 @@ class MapViewModelTest {
|
||||
)
|
||||
}
|
||||
every { contactRepository.getEnrichedContacts() } returns flowOf(contacts)
|
||||
every { receivedLocationDao.getLatestLocationsPerSender(any()) } returns flowOf(receivedLocations)
|
||||
every { receivedLocationDao.getLatestLocationsPerSenderUnfiltered() } returns flowOf(receivedLocations)
|
||||
|
||||
viewModel = MapViewModel(contactRepository, receivedLocationDao, locationSharingManager, announceDao)
|
||||
|
||||
@@ -384,7 +389,7 @@ class MapViewModelTest {
|
||||
)
|
||||
}
|
||||
every { contactRepository.getEnrichedContacts() } returns flowOf(contacts)
|
||||
every { receivedLocationDao.getLatestLocationsPerSender(any()) } returns flowOf(receivedLocations)
|
||||
every { receivedLocationDao.getLatestLocationsPerSenderUnfiltered() } returns flowOf(receivedLocations)
|
||||
|
||||
viewModel = MapViewModel(contactRepository, receivedLocationDao, locationSharingManager, announceDao)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.lxmf.messenger.repository.SettingsRepository
|
||||
import com.lxmf.messenger.reticulum.model.Identity
|
||||
import com.lxmf.messenger.reticulum.protocol.ServiceReticulumProtocol
|
||||
import com.lxmf.messenger.service.ActiveConversationManager
|
||||
import com.lxmf.messenger.service.LocationSharingManager
|
||||
import com.lxmf.messenger.service.PropagationNodeManager
|
||||
import com.lxmf.messenger.ui.model.ImageCache
|
||||
import io.mockk.Runs
|
||||
@@ -70,6 +71,7 @@ class MessagingViewModelImageLoadingTest {
|
||||
private lateinit var activeConversationManager: ActiveConversationManager
|
||||
private lateinit var settingsRepository: SettingsRepository
|
||||
private lateinit var propagationNodeManager: PropagationNodeManager
|
||||
private lateinit var locationSharingManager: LocationSharingManager
|
||||
private lateinit var viewModel: MessagingViewModel
|
||||
|
||||
@Before
|
||||
@@ -86,6 +88,10 @@ class MessagingViewModelImageLoadingTest {
|
||||
activeConversationManager = mockk(relaxed = true)
|
||||
settingsRepository = mockk(relaxed = true)
|
||||
propagationNodeManager = mockk(relaxed = true)
|
||||
locationSharingManager = mockk(relaxed = true)
|
||||
|
||||
// Mock locationSharingManager flows
|
||||
every { locationSharingManager.activeSessions } returns MutableStateFlow(emptyList())
|
||||
|
||||
// Setup default mock behaviors
|
||||
every { conversationRepository.getMessages(any()) } returns flowOf(emptyList())
|
||||
@@ -110,6 +116,7 @@ class MessagingViewModelImageLoadingTest {
|
||||
activeConversationManager = activeConversationManager,
|
||||
settingsRepository = settingsRepository,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
locationSharingManager = locationSharingManager,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.lxmf.messenger.reticulum.protocol.DeliveryStatusUpdate
|
||||
import com.lxmf.messenger.reticulum.protocol.MessageReceipt
|
||||
import com.lxmf.messenger.reticulum.protocol.ServiceReticulumProtocol
|
||||
import com.lxmf.messenger.service.ActiveConversationManager
|
||||
import com.lxmf.messenger.service.LocationSharingManager
|
||||
import com.lxmf.messenger.service.PropagationNodeManager
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -61,6 +62,7 @@ class MessagingViewModelTest {
|
||||
private lateinit var activeConversationManager: ActiveConversationManager
|
||||
private lateinit var settingsRepository: SettingsRepository
|
||||
private lateinit var propagationNodeManager: PropagationNodeManager
|
||||
private lateinit var locationSharingManager: LocationSharingManager
|
||||
|
||||
private val testPeerHash = "abcdef0123456789abcdef0123456789" // Valid 32-char hex hash
|
||||
private val testPeerName = "Test Peer"
|
||||
@@ -82,9 +84,14 @@ class MessagingViewModelTest {
|
||||
activeConversationManager = mockk(relaxed = true)
|
||||
settingsRepository = mockk(relaxed = true)
|
||||
propagationNodeManager = mockk(relaxed = true)
|
||||
locationSharingManager = mockk(relaxed = true)
|
||||
|
||||
// Mock locationSharingManager flows
|
||||
every { locationSharingManager.activeSessions } returns MutableStateFlow(emptyList())
|
||||
|
||||
// Mock default contact repository behavior
|
||||
every { contactRepository.hasContactFlow(any()) } returns flowOf(false)
|
||||
every { contactRepository.getEnrichedContacts() } returns flowOf(emptyList())
|
||||
coEvery { contactRepository.hasContact(any()) } returns false
|
||||
coEvery { contactRepository.addContactFromConversation(any(), any()) } returns Result.success(Unit)
|
||||
coEvery { contactRepository.deleteContact(any()) } just Runs
|
||||
@@ -133,6 +140,7 @@ class MessagingViewModelTest {
|
||||
activeConversationManager,
|
||||
settingsRepository,
|
||||
propagationNodeManager,
|
||||
locationSharingManager,
|
||||
)
|
||||
|
||||
@Test
|
||||
@@ -414,12 +422,15 @@ class MessagingViewModelTest {
|
||||
|
||||
val failingContactRepository: ContactRepository = mockk()
|
||||
every { failingContactRepository.hasContactFlow(any()) } returns flowOf(false)
|
||||
every { failingContactRepository.getEnrichedContacts() } returns flowOf(emptyList())
|
||||
|
||||
val failingActiveConversationManager: ActiveConversationManager = mockk(relaxed = true)
|
||||
val failingSettingsRepository: SettingsRepository = mockk(relaxed = true)
|
||||
val failingPropagationNodeManager: PropagationNodeManager = mockk(relaxed = true)
|
||||
val failingLocationSharingManager: LocationSharingManager = mockk(relaxed = true)
|
||||
every { failingPropagationNodeManager.isSyncing } returns MutableStateFlow(false)
|
||||
every { failingPropagationNodeManager.manualSyncResult } returns MutableSharedFlow()
|
||||
every { failingLocationSharingManager.activeSessions } returns MutableStateFlow(emptyList())
|
||||
|
||||
val viewModelWithoutIdentity =
|
||||
MessagingViewModel(
|
||||
@@ -430,6 +441,7 @@ class MessagingViewModelTest {
|
||||
failingActiveConversationManager,
|
||||
failingSettingsRepository,
|
||||
failingPropagationNodeManager,
|
||||
failingLocationSharingManager,
|
||||
)
|
||||
|
||||
// Attempt to send message
|
||||
@@ -870,6 +882,7 @@ class MessagingViewModelTest {
|
||||
activeConversationManager,
|
||||
settingsRepository,
|
||||
propagationNodeManager,
|
||||
locationSharingManager,
|
||||
)
|
||||
advanceUntilIdle()
|
||||
|
||||
@@ -929,6 +942,7 @@ class MessagingViewModelTest {
|
||||
activeConversationManager,
|
||||
settingsRepository,
|
||||
propagationNodeManager,
|
||||
locationSharingManager,
|
||||
)
|
||||
advanceUntilIdle()
|
||||
|
||||
@@ -984,6 +998,7 @@ class MessagingViewModelTest {
|
||||
activeConversationManager,
|
||||
settingsRepository,
|
||||
propagationNodeManager,
|
||||
locationSharingManager,
|
||||
)
|
||||
advanceUntilIdle()
|
||||
|
||||
@@ -1029,6 +1044,7 @@ class MessagingViewModelTest {
|
||||
activeConversationManager,
|
||||
settingsRepository,
|
||||
propagationNodeManager,
|
||||
locationSharingManager,
|
||||
)
|
||||
advanceUntilIdle()
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.lxmf.messenger.reticulum.model.NetworkStatus
|
||||
import com.lxmf.messenger.reticulum.protocol.ReticulumProtocol
|
||||
import com.lxmf.messenger.service.AvailableRelaysState
|
||||
import com.lxmf.messenger.service.InterfaceConfigManager
|
||||
import com.lxmf.messenger.service.LocationSharingManager
|
||||
import com.lxmf.messenger.service.PropagationNodeManager
|
||||
import com.lxmf.messenger.service.RelayInfo
|
||||
import com.lxmf.messenger.ui.theme.PresetTheme
|
||||
@@ -55,6 +56,7 @@ class SettingsViewModelTest {
|
||||
private lateinit var reticulumProtocol: ReticulumProtocol
|
||||
private lateinit var interfaceConfigManager: InterfaceConfigManager
|
||||
private lateinit var propagationNodeManager: PropagationNodeManager
|
||||
private lateinit var locationSharingManager: LocationSharingManager
|
||||
private lateinit var viewModel: SettingsViewModel
|
||||
|
||||
// Mutable flows for controlling test scenarios
|
||||
@@ -84,6 +86,10 @@ class SettingsViewModelTest {
|
||||
reticulumProtocol = mockk(relaxed = true)
|
||||
interfaceConfigManager = mockk(relaxed = true)
|
||||
propagationNodeManager = mockk(relaxed = true)
|
||||
locationSharingManager = mockk(relaxed = true)
|
||||
|
||||
// Mock locationSharingManager flows
|
||||
every { locationSharingManager.activeSessions } returns MutableStateFlow(emptyList())
|
||||
|
||||
// Setup repository flow mocks
|
||||
every { settingsRepository.preferOwnInstanceFlow } returns preferOwnInstanceFlow
|
||||
@@ -130,6 +136,7 @@ class SettingsViewModelTest {
|
||||
reticulumProtocol = reticulumProtocol,
|
||||
interfaceConfigManager = interfaceConfigManager,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
locationSharingManager = locationSharingManager,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1423,6 +1430,7 @@ class SettingsViewModelTest {
|
||||
reticulumProtocol = serviceProtocol,
|
||||
interfaceConfigManager = interfaceConfigManager,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
locationSharingManager = locationSharingManager,
|
||||
)
|
||||
|
||||
viewModel.state.test {
|
||||
@@ -1464,6 +1472,7 @@ class SettingsViewModelTest {
|
||||
reticulumProtocol = serviceProtocol,
|
||||
interfaceConfigManager = interfaceConfigManager,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
locationSharingManager = locationSharingManager,
|
||||
)
|
||||
|
||||
viewModel.state.test {
|
||||
@@ -1955,6 +1964,7 @@ class SettingsViewModelTest {
|
||||
reticulumProtocol = serviceProtocol,
|
||||
interfaceConfigManager = interfaceConfigManager,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
locationSharingManager = locationSharingManager,
|
||||
)
|
||||
|
||||
// Wait for any potential async operations to settle
|
||||
@@ -1993,6 +2003,7 @@ class SettingsViewModelTest {
|
||||
reticulumProtocol = serviceProtocol,
|
||||
interfaceConfigManager = interfaceConfigManager,
|
||||
propagationNodeManager = propagationNodeManager,
|
||||
locationSharingManager = locationSharingManager,
|
||||
)
|
||||
|
||||
// The ViewModel should be created successfully with ServiceReticulumProtocol
|
||||
|
||||
Reference in New Issue
Block a user