@@ -3,39 +3,62 @@ package soy.iko.opencode.core
33import kotlinx.coroutines.CoroutineScope
44import kotlinx.coroutines.Dispatchers
55import kotlinx.coroutines.test.runTest
6+import okhttp3.OkHttpClient
7+import okhttp3.Interceptor
68import org.junit.Assert.assertEquals
79import org.junit.Assert.assertTrue
810import org.junit.Test
11+import java.io.IOException
912
1013/**
11− * Regression coverage for the "app crashes after submitting a URL" bug: a
12− * scheme-less server URL (e.g. "192.168.1.10:4096") used to reach OkHttp's URL
13− * parser and throw, crashing the connect coroutine. It must now (a) be
14− * normalized to an http:// URL by the core, and (b) never crash the HTTP client
15− * even if something un-parseable slips through.
14+ * Covers two related connect-screen bugs:
15+ * - the URL field must stay editable — normalizing per keystroke made it
16+ * "impossible to input" (it ate the "://" as you typed);
17+ * - a scheme-less URL must still be normalized to http:// on connect, and the
18+ * HTTP client must never crash on a malformed URL.
1619 */
1720class ConnectUrlTest {
1821 private fun core() = OpencodeCore(CoroutineScope(Dispatchers.Unconfined))
1922
23+ /** A client that never touches the network, so Connect's probe can't hang. */
24+ private fun offlineCore(): OpencodeCore {
25+ val offline = OkHttpClient.Builder()
26+ .addInterceptor(Interceptor { throw IOException("offline in test") })
27+ .build()
28+ return OpencodeCore(CoroutineScope(Dispatchers.Unconfined), HttpClient(offline))
29+ }
30+
2031 @Test
21− fun `scheme-less url defaults to http`() {
32+ fun `typing preserves raw input verbatim`() {
2233 val core = core()
34+ // Mid-scheme text the user is still typing must not be rewritten.
35+ core.dispatch(Event.ServerUrlChanged("http:/"))
36+ assertEquals("http:/", core.view.value.serverUrl)
37+ }
38+
39+ @Test
40+ fun `connect normalizes a scheme-less url to http`() {
41+ val core = offlineCore()
2342 core.dispatch(Event.ServerUrlChanged("192.168.1.10:4096"))
24− assertEquals("http://192.168.1.10:4096", core.view.value.serverUrl)
43+ assertEquals("192.168.1.10:4096", core.view.value.serverUrl) // raw while typing
44+ core.dispatch(Event.Connect)
45+ assertEquals("http://192.168.1.10:4096", core.view.value.serverUrl) // normalized on connect
2546 }
2647
2748 @Test
28− fun `url with scheme is preserved and trimmed`() {
29− val core = core()
49+ fun `connect trims and keeps an explicit scheme`() {
50+ val core = offlineCore()
3051 core.dispatch(Event.ServerUrlChanged(" https://host:4096/ "))
52+ core.dispatch(Event.Connect)
3153 assertEquals("https://host:4096", core.view.value.serverUrl)
3254 }
3355
3456 @Test
35− fun `blank url normalizes to empty`() {
57+ fun `connect on a blank url reports an error`() {
3658 val core = core()
3759 core.dispatch(Event.ServerUrlChanged(" "))
38− assertEquals("", core.view.value.serverUrl)
60+ core.dispatch(Event.Connect)
61+ assertEquals("Enter a server URL", core.view.value.error)
3962 }
4063
4164 @Test