git.iko.soy/opencode-android
on 489f8c7 help sign in
opencode-android/app/src/test/java/soy/iko/opencode/data/repo/SessionRepositoryTest.kt
73 lines 2.8 kB history raw
1package soy.iko.opencode.data.repo
3import io.ktor.client.HttpClient
4import io.ktor.client.engine.mock.MockEngine
5import io.ktor.client.engine.mock.respond
6import io.ktor.client.engine.mock.respondError
7import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
8import io.ktor.client.plugins.sse.SSE
9import io.ktor.http.ContentType
10import io.ktor.http.HttpHeaders
11import io.ktor.http.HttpStatusCode
12import io.ktor.http.headersOf
13import io.ktor.serialization.kotlinx.json.json
14import kotlinx.coroutines.CoroutineScope
15import kotlinx.coroutines.Dispatchers
16import kotlinx.coroutines.ExperimentalCoroutinesApi
17import kotlinx.coroutines.SupervisorJob
18import kotlinx.coroutines.cancel
19import kotlinx.coroutines.flow.first
20import kotlinx.coroutines.runBlocking
21import org.junit.Assert.assertEquals
22import org.junit.Test
23import soy.iko.opencode.data.network.EventStreamClient
24import soy.iko.opencode.data.network.OpencodeApiClient
25import soy.iko.opencode.data.network.OpencodeJson
27@OptIn(ExperimentalCoroutinesApi::class)
28class SessionRepositoryTest {
30 private val session = "s1"
31 private val jsonHeaders = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString())
33 /**
34 * Verify that observeMessages seeds from the REST endpoint. The SSE stream uses
35 * a MockEngine that always errors (MockEngine can't produce a streaming SSE body),
36 * so only the REST seed path is exercised here. The SSE→REST merge logic is
37 * unit-tested in [MessageStoreTest].
38 */
39 @Test
40 fun seedsFromRestWithSseErrors() = runBlocking {
41 val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
43 // REST: listMessages returns one user message.
44 val restEngine = MockEngine {
45 respond(
46 """[{"info":{"role":"user","id":"u1","sessionID":"$session"},"parts":[]}]""",
47 HttpStatusCode.OK,
48 jsonHeaders,
49 )
50 }
51 val restClient = HttpClient(restEngine) {
52 install(ContentNegotiation) { json(OpencodeJson) }
53 }
54 val api = OpencodeApiClient(restClient)
56 // SSE: always errors (we only test the REST seed path here).
57 val sseEngine = MockEngine { respondError(HttpStatusCode.InternalServerError) }
58 val sseClient = HttpClient(sseEngine) { install(SSE) }
59 val eventStream = EventStreamClient(sseClient, scope)
61 val repo = SessionRepository(api, eventStream)
63 // The first non-empty emission should be the REST seed.
64 val result = repo.observeMessages(session).first { it.isNotEmpty() }
66 assertEquals(1, result.size)
67 assertEquals("u1", result[0].info.id)
69 scope.cancel()
70 restClient.close()
71 sseClient.close()
72 }