feat(#471): implement DataStore-based server configuration and self-h… (#477)

* feat(#471): implement DataStore-based server configuration and self-healing

* refactor(#471): address Sourcery feedback on ANR risk and reset method splitting

* refactor(#471): implement synchronous ServerStore initialization to fully remove ANR risk

* feat(#471): show profile name, host and port on add profile, redirect to login on save, and support shortcut login buttons for existing profiles
This commit is contained in:
M57 authored and GitHub committed 2026-07-06 22:40:44 +03:00
1 parent 5697405b19
commit 430a083647
20 files changed
+774 -288

No files matched your search

@@ -2,6 +2,7 @@ package com.m57.hermescontrol.ui.connect
import android.app.Application
import com.m57.hermescontrol.R
import com.m57.hermescontrol.data.config.ConnectionProfile
import com.m57.hermescontrol.data.local.AuthManager
import com.m57.hermescontrol.data.model.StatusResponse
import com.m57.hermescontrol.data.remote.ApiClient
@@ -388,7 +389,7 @@ class ConnectViewModelTest {
@Test
fun testSelectProfile_updatesState() {
val profile =
com.m57.hermescontrol.data.model.ConnectionProfile(
ConnectionProfile(
id = "test-id",
name = "Test Profile",
host = "192.168.1.100",
@@ -463,7 +464,7 @@ class ConnectViewModelTest {
@Test
fun testLoadSavedValues_withProfiles_loadsSelected() {
val profile =
com.m57.hermescontrol.data.model.ConnectionProfile(
ConnectionProfile(
id = "prof-1",
name = "Work",
host = "10.0.0.1",
@@ -488,7 +489,7 @@ class ConnectViewModelTest {
@Test
fun testLoadSavedValues_withStaleSelectedId_fallsBackToDefaults() {
val profile =
com.m57.hermescontrol.data.model.ConnectionProfile(
ConnectionProfile(
id = "prof-1",
name = "Work",
host = "10.0.0.1",
@@ -510,7 +511,7 @@ class ConnectViewModelTest {
every { AuthManager.getProfileToken(any()) } returns null
val profile =
com.m57.hermescontrol.data.model.ConnectionProfile(
ConnectionProfile(
id = "prof-2",
name = "NoToken",
host = "10.0.0.2",
@@ -574,10 +575,8 @@ class ConnectViewModelTest {
fun testMultipleProfiles_loadedOnInit() {
val profiles =
listOf(
com.m57.hermescontrol.data.model
.ConnectionProfile("a", "Alpha", "10.0.0.1", 9119),
com.m57.hermescontrol.data.model
.ConnectionProfile("b", "Beta", "10.0.0.2", 9220),
ConnectionProfile("a", "Alpha", "10.0.0.1", 9119),
ConnectionProfile("b", "Beta", "10.0.0.2", 9220),
)
every { AuthManager.getConnectionProfiles() } returns profiles
every { AuthManager.getSelectedProfileId() } returns null
@@ -1,7 +1,7 @@
package com.m57.hermescontrol.ui.settings
import com.m57.hermescontrol.data.config.ConnectionProfile
import com.m57.hermescontrol.data.local.AuthManager
import com.m57.hermescontrol.data.model.ConnectionProfile
import com.m57.hermescontrol.data.remote.ApiClient
import com.m57.hermescontrol.theme.BottomNavDisplayMode
import com.m57.hermescontrol.theme.ThemePreference
@@ -218,4 +218,25 @@ class SettingsViewModelTest {
verify(exactly = 0) { AuthManager.setSelectedProfileId(null) }
verify { ApiClient.rebuild() }
}
@Test
fun testSaveProfileFromDialog_addsNewProfileAndTriggersLoginRedirection() {
every { AuthManager.getConnectionProfiles() } returns emptyList()
val viewModel = createViewModel()
viewModel.openAddProfile()
viewModel.onDialogProfileNameChange("Test Profile")
viewModel.onDialogProfileHostChange("192.168.1.100")
viewModel.onDialogProfilePortChange("9999")
viewModel.saveProfileFromDialog()
testDispatcher.scheduler.advanceUntilIdle()
verify { AuthManager.saveConnectionProfiles(any()) }
verify { AuthManager.setSelectedProfileId(any()) }
assertEquals(true, viewModel.uiState.value.navigateToLogin)
viewModel.clearNavigateToLogin()
assertEquals(false, viewModel.uiState.value.navigateToLogin)
}
}