@@ -225,6 +225,79 @@ class SessionListViewModelTest {
225225 assertNull(vm.switchingId.value)
226226 }
227227
228+ @Test
229+ fun switchServer_failure_restoresPreviousAndShowsTransientError() = testScope.runTest {
230+ val repo = FakeSessionRepository(FakeOpencodeApiClient(), FakeEventStreamClient())
231+ repo.sessions = listOf(Session(id = "s1-chat", title = "Original"))
232+ val container = makeContainer(repo = repo)
233+ val vm = makeVm(container)
234+
235+ // connect() succeeds but the new server's ping fails.
236+ val newApi = FakeOpencodeApiClient().apply { pingThrows = IOException("switch failed") }
237+ val newRepo = FakeSessionRepository(newApi, FakeEventStreamClient())
238+ newRepo.sessions = listOf(Session(id = "restored", title = "Restored"))
239+ container.connectResult = FakeOpencodeConnection(
240+ newApi, FakeEventStreamClient(), newRepo,
241+ ServerProfile(id = "s2", label = "Server 2", baseUrl = "http://other"),
242+ )
243+
244+ vm.switchServer(ServerProfile(id = "s2", label = "Server 2", baseUrl = "http://other"))
245+ testScheduler.advanceUntilIdle()
246+
247+ // connect attempted for the new server, then again to restore the previous one.
248+ assertEquals(2, container.connectCalls.size)
249+ assertEquals("s2", container.connectCalls[0].id)
250+ assertEquals("s1", container.connectCalls[1].id)
251+ // The switch failure is surfaced as a transient error, not a list-replacing error.
252+ assertNotNull(vm.transientError.value)
253+ assertNull(vm.state.value.error)
254+ assertEquals(1, container.disconnectCalls)
255+ assertNull(vm.switchingId.value)
256+ }
257+
258+ @Test
259+ fun switchServer_failure_whenNoPreviousShowsErrorState() = testScope.runTest {
260+ // No active connection — there's no previous profile to restore to.
261+ val container = FakeAppContainer()
262+ val vm = makeVm(container)
263+
264+ val newApi = FakeOpencodeApiClient().apply { pingThrows = IOException("nope") }
265+ container.connectResult = FakeOpencodeConnection(
266+ newApi, FakeEventStreamClient(),
267+ FakeSessionRepository(newApi, FakeEventStreamClient()),
268+ ServerProfile(id = "s2", label = "Server 2", baseUrl = "http://other"),
269+ )
270+
271+ vm.switchServer(ServerProfile(id = "s2", label = "Server 2", baseUrl = "http://other"))
272+ testScheduler.advanceUntilIdle()
273+
274+ // No previous profile to restore — the error replaces the list state.
275+ assertNotNull(vm.state.value.error)
276+ assertFalse(vm.state.value.loading)
277+ assertNull(vm.switchingId.value)
278+ }
279+
280+ @Test
281+ fun switchServer_failure_restoreAlsoFailsShowsCombinedError() = testScope.runTest {
282+ val repo = FakeSessionRepository(FakeOpencodeApiClient(), FakeEventStreamClient())
283+ repo.sessions = listOf(Session(id = "s1-chat", title = "Original"))
284+ val container = makeContainer(repo = repo)
285+ val vm = makeVm(container)
286+
287+ // Both the new connect and the restore connect fail.
288+ container.connectException = IOException("unreachable")
289+
290+ vm.switchServer(ServerProfile(id = "s2", label = "Server 2", baseUrl = "http://other"))
291+ testScheduler.advanceUntilIdle()
292+
293+ // connect attempted for the new server, then again to restore the previous one.
294+ assertEquals(2, container.connectCalls.size)
295+ // Both failed — the combined error replaces the list state.
296+ assertNotNull(vm.state.value.error)
297+ assertFalse(vm.state.value.loading)
298+ assertNull(vm.switchingId.value)
299+ }
300+
228301 // --- setQuery / filtering ---
229302
230303 @Test