@@ -161,15 +161,19 @@ class ServerEditViewModelTest {
161161 assertTrue(vm.state.value.canSave)
162162 }
163163
164− // --- save() ---
164+ // --- connect() save path (probe Reachable → save + connect + ping) ---
165+ // The former save()-specific tests now exercise the same persist path via connect(),
166+ // since the standalone "Save without connecting" action was removed: connect() is the
167+ // single primary action and its probe IS the connection test.
165168
166169 @Test
167− fun save_persistsProfileAndCallsOnDone() = testScope.runTest {
170+ fun connect_persistsProfileAndCallsOnDone() = testScope.runTest {
168171 val container = FakeAppContainer()
172+ container.probeResult = ProbeResult.Reachable
169173 val vm = makeVm(container)
170174 vm.update { it.copy(baseUrl = "http://localhost:3000", label = "Test") }
171175 var doneCalled = false
172− vm.save { doneCalled = true }
176+ vm.connect { doneCalled = true }
173177 testScheduler.advanceUntilIdle()
174178 assertTrue(doneCalled)
175179 assertNotNull(container.fakeProfileStore.savedProfile)
@@ -178,36 +182,12 @@ class ServerEditViewModelTest {
178182 }
179183
180184 @Test
181− fun save_invalidUrlDoesNotSave() = testScope.runTest {
182− val container = FakeAppContainer()
183− val vm = makeVm(container)
184− vm.update { it.copy(baseUrl = "not a url") }
185− var doneCalled = false
186− vm.save { doneCalled = true }
187− testScheduler.advanceUntilIdle()
188− assertFalse(doneCalled)
189− assertNull(container.fakeProfileStore.savedProfile)
190− }
191−
192− @Test
193− fun save_whileSavingDoesNotSaveAgain() = testScope.runTest {
194− val container = FakeAppContainer()
195− val vm = makeVm(container)
196− vm.update { it.copy(baseUrl = "http://localhost:3000") }
197− var doneCount = 0
198− vm.save { doneCount++ }
199− // While saving, try to save again
200− vm.save { doneCount++ }
201− testScheduler.advanceUntilIdle()
202− assertEquals(1, doneCount)
203− }
204−
205− @Test
206− fun save_generatesIdForNewProfile() = testScope.runTest {
185+ fun connect_generatesIdForNewProfile() = testScope.runTest {
207186 val container = FakeAppContainer()
187+ container.probeResult = ProbeResult.Reachable
208188 val vm = makeVm(container)
209189 vm.update { it.copy(baseUrl = "http://localhost:3000") }
210− vm.save { }
190+ vm.connect { }
211191 testScheduler.advanceUntilIdle()
212192 val saved = container.fakeProfileStore.savedProfile
213193 assertNotNull(saved?.id)
@@ -215,23 +195,25 @@ class ServerEditViewModelTest {
215195 }
216196
217197 @Test
218− fun save_preservesIdForExistingProfile() = testScope.runTest {
198+ fun connect_preservesIdForExistingProfile() = testScope.runTest {
219199 val container = FakeAppContainer()
200+ container.probeResult = ProbeResult.Reachable
220201 val profile = ServerProfile(id = "existing-id", label = "Old", baseUrl = "http://old")
221202 container.fakeProfileStore.setProfiles(listOf(profile))
222203 val vm = makeVm(container, profileId = "existing-id")
223204 vm.update { it.copy(baseUrl = "http://new") }
224− vm.save { }
205+ vm.connect { }
225206 testScheduler.advanceUntilIdle()
226207 assertEquals("existing-id", container.fakeProfileStore.savedProfile?.id)
227208 }
228209
229210 @Test
230− fun save_trimsFields() = testScope.runTest {
211+ fun connect_trimsFields() = testScope.runTest {
231212 val container = FakeAppContainer()
213+ container.probeResult = ProbeResult.Reachable
232214 val vm = makeVm(container)
233215 vm.update { it.copy(baseUrl = " http://localhost:3000 ", label = " Test ", username = " admin ") }
234− vm.save { }
216+ vm.connect { }
235217 testScheduler.advanceUntilIdle()
236218 val saved = container.fakeProfileStore.savedProfile!!
237219 assertEquals("http://localhost:3000", saved.baseUrl)
@@ -240,11 +222,12 @@ class ServerEditViewModelTest {
240222 }
241223
242224 @Test
243− fun save_appliesHttpsSchemeToBareLanHost() = testScope.runTest {
225+ fun connect_appliesHttpsSchemeToBareLanHost() = testScope.runTest {
244226 val container = FakeAppContainer()
227+ container.probeResult = ProbeResult.Reachable
245228 val vm = makeVm(container)
246229 vm.update { it.copy(baseUrl = "localhost:3000", label = "LAN") }
247− vm.save { }
230+ vm.connect { }
248231 testScheduler.advanceUntilIdle()
249232 val saved = container.fakeProfileStore.savedProfile!!
250233 // A bare host is always schemed to https:// (the safe default). The user can still
@@ -258,35 +241,38 @@ class ServerEditViewModelTest {
258241 }
259242
260243 @Test
261− fun save_appliesHttpsSchemeToBarePublicDomain() = testScope.runTest {
244+ fun connect_appliesHttpsSchemeToBarePublicDomain() = testScope.runTest {
262245 val container = FakeAppContainer()
246+ container.probeResult = ProbeResult.Reachable
263247 val vm = makeVm(container)
264248 vm.update { it.copy(baseUrl = "example.com") }
265− vm.save { }
249+ vm.connect { }
266250 testScheduler.advanceUntilIdle()
267251 val saved = container.fakeProfileStore.savedProfile!!
268252 assertEquals("https://example.com", saved.baseUrl)
269253 }
270254
271255 @Test
272− fun save_preservesExplicitHttpSchemeForPublicDomain() = testScope.runTest {
256+ fun connect_preservesExplicitHttpSchemeForPublicDomain() = testScope.runTest {
273257 val container = FakeAppContainer()
258+ container.probeResult = ProbeResult.Reachable
274259 val vm = makeVm(container)
275260 // The user typed the scheme explicitly — don't upgrade a public domain to https://
276261 // behind their back; preserve their explicit http:// choice.
277262 vm.update { it.copy(baseUrl = "http://example.com") }
278− vm.save { }
263+ vm.connect { }
279264 testScheduler.advanceUntilIdle()
280265 val saved = container.fakeProfileStore.savedProfile!!
281266 assertEquals("http://example.com", saved.baseUrl)
282267 }
283268
284269 @Test
285− fun save_emptyUsernameClearsCredentials() = testScope.runTest {
270+ fun connect_emptyUsernameClearsCredentials() = testScope.runTest {
286271 val container = FakeAppContainer()
272+ container.probeResult = ProbeResult.Reachable
287273 val vm = makeVm(container)
288274 vm.update { it.copy(baseUrl = "http://localhost:3000", username = "", password = "") }
289− vm.save { }
275+ vm.connect { }
290276 testScheduler.advanceUntilIdle()
291277 val saved = container.fakeProfileStore.savedProfile!!
292278 assertNull(saved.username)
@@ -294,13 +280,14 @@ class ServerEditViewModelTest {
294280 }
295281
296282 @Test
297− fun save_failureSetsErrorAndClearsSaving() = testScope.runTest {
283+ fun connect_saveFailureSetsErrorAndClearsSaving() = testScope.runTest {
298284 val container = FakeAppContainer()
285+ container.probeResult = ProbeResult.Reachable
299286 container.fakeProfileStore.saveException = RuntimeException("disk full")
300287 val vm = makeVm(container)
301288 vm.update { it.copy(baseUrl = "http://localhost:3000") }
302289 var doneCalled = false
303− vm.save { doneCalled = true }
290+ vm.connect { doneCalled = true }
304291 testScheduler.advanceUntilIdle()
305292 assertFalse(doneCalled)
306293 assertFalse(vm.state.value.saving)
@@ -310,12 +297,13 @@ class ServerEditViewModelTest {
310297 }
311298
312299 @Test
313− fun save_failureDoesNotClobberUserEdits() = testScope.runTest {
300+ fun connect_saveFailureDoesNotClobberUserEdits() = testScope.runTest {
314301 val container = FakeAppContainer()
302+ container.probeResult = ProbeResult.Reachable
315303 container.fakeProfileStore.saveException = RuntimeException("disk full")
316304 val vm = makeVm(container)
317305 vm.update { it.copy(baseUrl = "http://localhost:3000", label = "Original") }
318− vm.save { }
306+ vm.connect { }
319307 // Simulate the user editing the label while the save coroutine is in-flight.
320308 vm.update { it.copy(label = "Edited") }
321309 testScheduler.advanceUntilIdle()