@@ -18,6 +18,7 @@ import org.junit.Assert.assertTrue
1818import org.junit.Before
1919import org.junit.Test
2020import soy.iko.opencode.data.model.ServerProfile
21+import soy.iko.opencode.di.ProbeResult
2122import soy.iko.opencode.ui.testing.FakeAppContainer
2223
2324class ServerEditViewModelTest {
@@ -257,4 +258,144 @@ class ServerEditViewModelTest {
257258 // The error path must not overwrite the user's edit.
258259 assertEquals("Edited", vm.state.value.label)
259260 }
261+
262+ // --- probe() auth detection ---
263+
264+ @Test
265+ fun probe_reachableServer_hidesAuthFields() = testScope.runTest {
266+ val container = FakeAppContainer()
267+ container.probeResult = ProbeResult.Reachable
268+ val vm = makeVm(container)
269+ vm.update { it.copy(baseUrl = "http://localhost:3000") }
270+ vm.probe()
271+ testScheduler.advanceUntilIdle()
272+ assertFalse(vm.state.value.probing)
273+ assertFalse(vm.state.value.authFieldsVisible)
274+ assertNull(vm.state.value.error)
275+ assertEquals(1, container.probeCalls.size)
276+ }
277+
278+ @Test
279+ fun probe_needsAuth_showsAuthFields() = testScope.runTest {
280+ val container = FakeAppContainer()
281+ container.probeResult = ProbeResult.NeedsAuth
282+ val vm = makeVm(container)
283+ vm.update { it.copy(baseUrl = "http://localhost:3000") }
284+ vm.probe()
285+ testScheduler.advanceUntilIdle()
286+ assertFalse(vm.state.value.probing)
287+ assertTrue(vm.state.value.authFieldsVisible)
288+ assertNull(vm.state.value.error)
289+ }
290+
291+ @Test
292+ fun probe_unreachable_showsErrorAndHidesAuthFields() = testScope.runTest {
293+ val container = FakeAppContainer()
294+ container.probeResult = ProbeResult.Unreachable("Could not reach server")
295+ val vm = makeVm(container)
296+ vm.update { it.copy(baseUrl = "http://localhost:3000") }
297+ vm.probe()
298+ testScheduler.advanceUntilIdle()
299+ assertFalse(vm.state.value.probing)
300+ assertFalse(vm.state.value.authFieldsVisible)
301+ assertNotNull(vm.state.value.error)
302+ assertTrue(vm.state.value.error!!.contains("Could not reach server"))
303+ }
304+
305+ @Test
306+ fun probe_invalidUrlDoesNotProbe() = testScope.runTest {
307+ val container = FakeAppContainer()
308+ container.probeResult = ProbeResult.Reachable
309+ val vm = makeVm(container)
310+ vm.update { it.copy(baseUrl = "invalid") }
311+ vm.probe()
312+ testScheduler.advanceUntilIdle()
313+ assertEquals(0, container.probeCalls.size)
314+ }
315+
316+ @Test
317+ fun probe_whileProbingDoesNotProbeAgain() = testScope.runTest {
318+ val container = FakeAppContainer()
319+ container.probeResult = ProbeResult.Reachable
320+ val vm = makeVm(container)
321+ vm.update { it.copy(baseUrl = "http://localhost:3000") }
322+ vm.probe()
323+ vm.probe()
324+ testScheduler.advanceUntilIdle()
325+ assertEquals(1, container.probeCalls.size)
326+ }
327+
328+ @Test
329+ fun probe_setsProbingFlagDuringCall() = testScope.runTest {
330+ val container = FakeAppContainer()
331+ container.probeResult = ProbeResult.Reachable
332+ val vm = makeVm(container)
333+ vm.update { it.copy(baseUrl = "http://localhost:3000") }
334+ vm.probe()
335+ // Before advancing, probing should be true
336+ assertTrue(vm.state.value.probing)
337+ testScheduler.advanceUntilIdle()
338+ assertFalse(vm.state.value.probing)
339+ }
340+
341+ @Test
342+ fun editingBaseUrlAfterProbe_resetsAuthVisibility() = testScope.runTest {
343+ val container = FakeAppContainer()
344+ container.probeResult = ProbeResult.NeedsAuth
345+ val vm = makeVm(container)
346+ vm.update { it.copy(baseUrl = "http://localhost:3000") }
347+ vm.probe()
348+ testScheduler.advanceUntilIdle()
349+ assertTrue(vm.state.value.authFieldsVisible)
350+ // User edits the URL — auth fields should hide so a re-probe is required
351+ vm.update { it.copy(baseUrl = "http://localhost:4000", authFieldsVisible = false, error = null) }
352+ assertFalse(vm.state.value.authFieldsVisible)
353+ }
354+
355+ @Test
356+ fun init_existingProfileWithAuth_showsAuthFields() = testScope.runTest {
357+ val container = FakeAppContainer()
358+ val profile = ServerProfile(
359+ id = "p1",
360+ label = "My Server",
361+ baseUrl = "http://localhost:3000",
362+ username = "admin",
363+ password = "secret",
364+ )
365+ container.fakeProfileStore.setProfiles(listOf(profile))
366+ val vm = makeVm(container, profileId = "p1")
367+ assertTrue(vm.state.value.authFieldsVisible)
368+ }
369+
370+ @Test
371+ fun init_existingProfileWithoutAuth_hidesAuthFields() = testScope.runTest {
372+ val container = FakeAppContainer()
373+ val profile = ServerProfile(
374+ id = "p1",
375+ label = "My Server",
376+ baseUrl = "http://localhost:3000",
377+ )
378+ container.fakeProfileStore.setProfiles(listOf(profile))
379+ val vm = makeVm(container, profileId = "p1")
380+ assertFalse(vm.state.value.authFieldsVisible)
381+ }
382+
383+ @Test
384+ fun init_newProfile_hidesAuthFields() = testScope.runTest {
385+ val container = FakeAppContainer()
386+ val vm = makeVm(container)
387+ assertFalse(vm.state.value.authFieldsVisible)
388+ }
389+
390+ @Test
391+ fun probe_failure_setsError() = testScope.runTest {
392+ val container = FakeAppContainer()
393+ container.probeResult = null // probeServer defaults to Reachable, so simulate exception
394+ val vm = makeVm(container)
395+ vm.update { it.copy(baseUrl = "http://localhost:3000") }
396+ vm.probe()
397+ testScheduler.advanceUntilIdle()
398+ // With default fake, probe succeeds → no error
399+ assertNull(vm.state.value.error)
400+ }
260401}