refactor(#1375): typed params for session.create, session.resume, prompt.submit (#1381)

This commit is contained in:
m57 authored and GitHub committed 2026-09-29 16:31:03 +03:00
1 parent d466414563
commit a780df3261
9 files changed
+942 -90

No files matched your search

@@ -0,0 +1,484 @@
package com.m57.hermescontrol.data.ws
import android.util.Log
import com.m57.hermescontrol.data.local.AuthManager
import com.m57.hermescontrol.data.remote.CookieManager
import com.m57.hermescontrol.data.remote.DashboardSessionTokenRefresher
import com.m57.hermescontrol.data.remote.OkHttpProvider
import com.m57.hermescontrol.data.remote.buildFakePersistentCookieJar
import com.m57.hermescontrol.data.ws.contract.PromptSubmitParams
import com.m57.hermescontrol.data.ws.contract.RpcMethods
import com.m57.hermescontrol.data.ws.contract.SessionCreateParams
import com.m57.hermescontrol.data.ws.contract.SessionResumeParams
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
class HermesWsClientTypedSendTest {
private lateinit var mockWebServer: MockWebServer
@Before
fun setUp() {
mockkStatic(Log::class)
every { Log.d(any<String>(), any<String>()) } returns 0
every { Log.i(any<String>(), any<String>()) } returns 0
every { Log.w(any<String>(), any<String>()) } returns 0
every { Log.w(any<String>(), any<String>(), any<Throwable>()) } returns 0
every { Log.e(any<String>(), any<String>(), any()) } returns 0
mockWebServer = MockWebServer()
mockWebServer.start()
mockkObject(AuthManager)
every { AuthManager.wsUrl() } returns mockWebServer.url("/").toString().replace("http://", "ws://")
every { AuthManager.isAutoReconnect() } returns false
every { AuthManager.getSessionCookie() } returns null
every { AuthManager.serverStore } returns
mockk<com.m57.hermescontrol.data.config.ServerStore>().also {
every { it.getLatestState() } returns
com.m57.hermescontrol.data.config
.ServerStoreState()
}
CookieManager.setJarForTest(buildFakePersistentCookieJar())
mockkObject(DashboardSessionTokenRefresher)
coEvery { DashboardSessionTokenRefresher.refreshAsync() } returns null
every { DashboardSessionTokenRefresher.refresh() } returns null
HermesWsClient.disconnect(clearPendingMessages = true)
HermesWsClient.releaseExternalActivityConnectionLease()
HermesWsClient.releaseBackgroundConnectionLease()
HermesWsClient.setAppForeground(true)
AuthManager.resetAuthStateForTest()
}
@After
fun tearDown() {
AuthManager.resetAuthStateForTest()
HermesWsClient.releaseExternalActivityConnectionLease()
HermesWsClient.releaseBackgroundConnectionLease()
HermesWsClient.disconnect(clearPendingMessages = true)
runBlocking {
withTimeout(5000) { HermesWsClient.connectionStatus.first { it == ConnectionStatus.DISCONNECTED } }
}
runCatching { mockWebServer.shutdown() }
unmockkAll()
}
private fun connectClient() {
HermesWsClient.connect()
runBlocking {
withTimeout(5000) { HermesWsClient.connectionStatus.first { it == ConnectionStatus.CONNECTED } }
}
}
@Test
fun typedSendSessionResumePutsExpectedFrameAndInvokesOnSent() {
val opened = CountDownLatch(1)
val captured = CountDownLatch(1)
val frameRef = AtomicReference<JsonObject?>(null)
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response,
) = opened.countDown()
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val json = OkHttpProvider.json.parseToJsonElement(text) as? JsonObject ?: return
val name = (json["method"] as? JsonPrimitive)?.content
if (name == "session.resume") {
frameRef.set(json)
captured.countDown()
}
}
},
),
)
connectClient()
assertTrue(opened.await(5, TimeUnit.SECONDS))
var onSentId: String? = null
val returnedId =
HermesWsClient.send(
method = RpcMethods.SESSION_RESUME,
params = SessionResumeParams(sessionId = "sess-1", omitMessages = true),
onSent = { id -> onSentId = id },
)
assertTrue(returnedId.isNotBlank())
assertEquals(returnedId, onSentId)
assertTrue("Frame must reach the server", captured.await(5, TimeUnit.SECONDS))
val frame = requireNotNull(frameRef.get())
assertFalse("Outgoing frames have NO jsonrpc field", frame.containsKey("jsonrpc"))
assertEquals("session.resume", (frame["method"] as? JsonPrimitive)?.content)
assertEquals(returnedId, (frame["id"] as? JsonPrimitive)?.content)
val params = frame["params"] as? JsonObject
assertNotNull(params)
val expectedParams =
buildJsonObject {
put("session_id", "sess-1")
put("omit_messages", true)
}
assertEquals(expectedParams, params)
}
@Test
fun typedSendWithActiveProfileInjectsProfileIntoSessionResume() {
val opened = CountDownLatch(1)
val captured = CountDownLatch(1)
val frameRef = AtomicReference<JsonObject?>(null)
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response,
) = opened.countDown()
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val json = OkHttpProvider.json.parseToJsonElement(text) as? JsonObject ?: return
val name = (json["method"] as? JsonPrimitive)?.content
if (name == "session.resume") {
frameRef.set(json)
captured.countDown()
}
}
},
),
)
AuthManager.setActiveProfileId("active-profile-1")
connectClient()
assertTrue(opened.await(5, TimeUnit.SECONDS))
HermesWsClient.send(
method = RpcMethods.SESSION_RESUME,
params = SessionResumeParams(sessionId = "sess-active", omitMessages = true),
)
assertTrue(captured.await(5, TimeUnit.SECONDS))
val frame = requireNotNull(frameRef.get())
val params = frame["params"] as? JsonObject
assertNotNull(params)
val expectedParams =
buildJsonObject {
put("session_id", "sess-active")
put("omit_messages", true)
put("profile", "active-profile-1")
}
assertEquals(expectedParams, params)
assertEquals("active-profile-1", (params!!["profile"] as? JsonPrimitive)?.content)
}
@Test
fun typedSendExplicitProfileWinsOverActiveProfileForSessionResume() {
val opened = CountDownLatch(1)
val captured = CountDownLatch(1)
val frameRef = AtomicReference<JsonObject?>(null)
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response,
) = opened.countDown()
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val json = OkHttpProvider.json.parseToJsonElement(text) as? JsonObject ?: return
val name = (json["method"] as? JsonPrimitive)?.content
if (name == "session.resume") {
frameRef.set(json)
captured.countDown()
}
}
},
),
)
AuthManager.setActiveProfileId("active-profile-1")
connectClient()
assertTrue(opened.await(5, TimeUnit.SECONDS))
HermesWsClient.send(
method = RpcMethods.SESSION_RESUME,
params =
SessionResumeParams(
sessionId = "sess-active",
omitMessages = true,
profile = "explicit-resume-profile",
),
)
assertTrue(captured.await(5, TimeUnit.SECONDS))
val frame = requireNotNull(frameRef.get())
val params = frame["params"] as? JsonObject
assertNotNull(params)
val expectedParams =
buildJsonObject {
put("session_id", "sess-active")
put("omit_messages", true)
put("profile", "explicit-resume-profile")
}
assertEquals(expectedParams, params)
assertEquals("explicit-resume-profile", (params!!["profile"] as? JsonPrimitive)?.content)
}
@Test
fun sessionCreateWithExplicitProfileKeepsExplicitWhenDifferentProfileActive() {
val opened = CountDownLatch(1)
val captured = CountDownLatch(1)
val frameRef = AtomicReference<JsonObject?>(null)
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response,
) = opened.countDown()
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val json = OkHttpProvider.json.parseToJsonElement(text) as? JsonObject ?: return
val name = (json["method"] as? JsonPrimitive)?.content
if (name == "session.create") {
frameRef.set(json)
captured.countDown()
}
}
},
),
)
AuthManager.setActiveProfileId("active-profile-2")
connectClient()
assertTrue(opened.await(5, TimeUnit.SECONDS))
HermesWsClient.send(
method = RpcMethods.SESSION_CREATE,
params = SessionCreateParams(profile = "bot-1"),
)
assertTrue(captured.await(5, TimeUnit.SECONDS))
val frame = requireNotNull(frameRef.get())
val params = frame["params"] as? JsonObject
assertNotNull(params)
val expectedParams =
buildJsonObject {
put("profile", "bot-1")
}
assertEquals(expectedParams, params)
assertEquals("bot-1", (params!!["profile"] as? JsonPrimitive)?.content)
}
@Test
fun sendMessagePutsSessionIdAndTextOnTheWire() {
val opened = CountDownLatch(1)
val captured = CountDownLatch(1)
val frameRef = AtomicReference<JsonObject?>(null)
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response,
) = opened.countDown()
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val json = OkHttpProvider.json.parseToJsonElement(text) as? JsonObject ?: return
val name = (json["method"] as? JsonPrimitive)?.content
if (name == "prompt.submit") {
frameRef.set(json)
captured.countDown()
}
}
},
),
)
connectClient()
assertTrue(opened.await(5, TimeUnit.SECONDS))
var onSentId: String? = null
val returnedId =
HermesWsClient.sendMessage(
sessionId = "sess-msg-1",
text = "plain message",
onSent = { id -> onSentId = id },
)
assertTrue(returnedId.isNotBlank())
assertEquals(returnedId, onSentId)
assertTrue(captured.await(5, TimeUnit.SECONDS))
val frame = requireNotNull(frameRef.get())
val params = frame["params"] as? JsonObject
assertNotNull(params)
val expectedParams =
buildJsonObject {
put("session_id", "sess-msg-1")
put("text", "plain message")
}
assertEquals(expectedParams, params)
assertFalse(params!!.containsKey("queued"))
}
@Test
fun sendMessageWithQueuedTrueAddsQueuedTrue() {
val opened = CountDownLatch(1)
val captured = CountDownLatch(1)
val frameRef = AtomicReference<JsonObject?>(null)
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response,
) = opened.countDown()
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val json = OkHttpProvider.json.parseToJsonElement(text) as? JsonObject ?: return
val name = (json["method"] as? JsonPrimitive)?.content
if (name == "prompt.submit") {
frameRef.set(json)
captured.countDown()
}
}
},
),
)
connectClient()
assertTrue(opened.await(5, TimeUnit.SECONDS))
HermesWsClient.sendMessage(
sessionId = "sess-msg-2",
text = "queued message",
queued = true,
)
assertTrue(captured.await(5, TimeUnit.SECONDS))
val frame = requireNotNull(frameRef.get())
val params = frame["params"] as? JsonObject
assertNotNull(params)
val expectedParams =
buildJsonObject {
put("session_id", "sess-msg-2")
put("text", "queued message")
put("queued", true)
}
assertEquals(expectedParams, params)
val queuedElement = params!!["queued"] as? JsonPrimitive
assertTrue(queuedElement != null && queuedElement.isString.not())
assertEquals("true", queuedElement!!.content)
}
@Test
fun sendMessageWithQueuedFalseHasNoQueuedKey() {
val opened = CountDownLatch(1)
val captured = CountDownLatch(1)
val frameRef = AtomicReference<JsonObject?>(null)
mockWebServer.enqueue(
MockResponse().withWebSocketUpgrade(
object : WebSocketListener() {
override fun onOpen(
webSocket: WebSocket,
response: Response,
) = opened.countDown()
override fun onMessage(
webSocket: WebSocket,
text: String,
) {
val json = OkHttpProvider.json.parseToJsonElement(text) as? JsonObject ?: return
val name = (json["method"] as? JsonPrimitive)?.content
if (name == "prompt.submit") {
frameRef.set(json)
captured.countDown()
}
}
},
),
)
connectClient()
assertTrue(opened.await(5, TimeUnit.SECONDS))
HermesWsClient.sendMessage(
sessionId = "sess-msg-3",
text = "un为其queued message",
queued = false,
)
assertTrue(captured.await(5, TimeUnit.SECONDS))
val frame = requireNotNull(frameRef.get())
val params = frame["params"] as? JsonObject
assertNotNull(params)
val expectedParams =
buildJsonObject {
put("session_id", "sess-msg-3")
put("text", "un为其queued message")
}
assertEquals(expectedParams, params)
assertFalse(params!!.containsKey("queued"))
}
}
@@ -334,7 +334,6 @@ class RawRpcParamsRatchetTest {
"data/ws/ConnectorRepository.kt::CONNECTORS_CONNECT",
"data/ws/ConnectorRepository.kt::CONNECTORS_LIST",
"data/ws/HermesWsClient.kt::CLIENT_CAPABILITIES",
"data/ws/HermesWsClient.kt::PROMPT_SUBMIT",
"data/ws/HermesWsClient.kt::SESSION_REDIRECT",
"data/ws/ModelOptionsRepository.kt::MODEL_OPTIONS",
"data/ws/PluginManageRepository.kt::PLUGINS_MANAGE",
@@ -349,8 +348,6 @@ class RawRpcParamsRatchetTest {
"data/ws/VaultRepository.kt::VAULT_SOURCES",
"data/ws/VaultRepository.kt::VAULT_SOURCE_SET",
"data/ws/VaultRepository.kt::VAULT_UNLOCK",
"notification/NotificationReplyReceiver.kt::PROMPT_SUBMIT",
"notification/NotificationReplyReceiver.kt::SESSION_RESUME",
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
@@ -359,9 +356,6 @@ class RawRpcParamsRatchetTest {
"ui/bots/group/GroupChatViewModel.kt::PROFILES_CONFIGURE",
"ui/bots/group/GroupChatViewModel.kt::PROFILES_CONFIGURE",
"ui/bots/group/GroupChatViewModel.kt::PROFILES_LIST",
"ui/bots/group/GroupChatViewModel.kt::PROMPT_SUBMIT",
"ui/bots/group/GroupChatViewModel.kt::PROMPT_SUBMIT",
"ui/bots/group/GroupChatViewModel.kt::SESSION_CREATE",
"ui/bots/group/GroupChatViewModel.kt::SESSION_INTERRUPT",
"ui/chat/ChatApprovalsDelegate.kt::APPROVAL_PENDING",
"ui/chat/ChatApprovalsDelegate.kt::APPROVAL_RECEIVED",
@@ -0,0 +1,290 @@
package com.m57.hermescontrol.data.ws.contract
import com.m57.hermescontrol.data.remote.OkHttpProvider
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.put
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class SessionPromptTypesTest {
@Test
fun createWithSourceOnlyEncodesExactlySource() {
val params = SessionCreateParams(source = "cli")
val encoded = OkHttpProvider.json.encodeToJsonElement(SessionCreateParams.serializer(), params)
val expected =
buildJsonObject {
put("source", "cli")
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("source"), (encoded as JsonObject).keys)
}
@Test
fun groupStyleCreateEncodesExpectedFourKeysWithHiddenBoolean() {
val params =
SessionCreateParams(
profile = "work",
title = "Team sync",
source = "matrix",
hidden = true,
)
val encoded = OkHttpProvider.json.encodeToJsonElement(SessionCreateParams.serializer(), params)
val expected =
buildJsonObject {
put("source", "matrix")
put("profile", "work")
put("title", "Team sync")
put("hidden", true)
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
val hiddenElement = (encoded as JsonObject)["hidden"] as? JsonPrimitive
assertTrue(hiddenElement != null && hiddenElement.isString.not())
}
@Test
fun allNullCreateParamsEncodeToEmptyObject() {
val params = SessionCreateParams()
val encoded = OkHttpProvider.json.encodeToJsonElement(SessionCreateParams.serializer(), params)
val expected = buildJsonObject {}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertTrue((encoded as JsonObject).isEmpty())
}
@Test
fun createWithExplicitFalseHiddenIsEncoded() {
val params = SessionCreateParams(hidden = false)
val encoded = OkHttpProvider.json.encodeToJsonElement(SessionCreateParams.serializer(), params)
val expected =
buildJsonObject {
put("hidden", false)
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("hidden"), (encoded as JsonObject).keys)
}
@Test
fun resumeEncodesSessionIdAndOmitMessagesExactly() {
val params = SessionResumeParams(sessionId = "s-123", omitMessages = true)
val encoded = OkHttpProvider.json.encodeToJsonElement(SessionResumeParams.serializer(), params)
val expected =
buildJsonObject {
put("session_id", "s-123")
put("omit_messages", true)
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("session_id", "omit_messages"), (encoded as JsonObject).keys)
}
@Test
fun resumeWithProfileEncodesExpectedThreeKeys() {
val params =
SessionResumeParams(
sessionId = "s-456",
omitMessages = true,
profile = "personal",
)
val encoded = OkHttpProvider.json.encodeToJsonElement(SessionResumeParams.serializer(), params)
val expected =
buildJsonObject {
put("session_id", "s-456")
put("omit_messages", true)
put("profile", "personal")
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("session_id", "omit_messages", "profile"), (encoded as JsonObject).keys)
}
@Test
fun resumeWithExplicitFalseOmitMessagesIsEncoded() {
val params = SessionResumeParams(sessionId = "s-789", omitMessages = false)
val encoded = OkHttpProvider.json.encodeToJsonElement(SessionResumeParams.serializer(), params)
val expected =
buildJsonObject {
put("session_id", "s-789")
put("omit_messages", false)
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("session_id", "omit_messages"), (encoded as JsonObject).keys)
}
@Test
fun submitEncodesSessionIdAndText() {
val params = PromptSubmitParams(sessionId = "s-100", text = "hello hermes")
val encoded = OkHttpProvider.json.encodeToJsonElement(PromptSubmitParams.serializer(), params)
val expected =
buildJsonObject {
put("session_id", "s-100")
put("text", "hello hermes")
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("session_id", "text"), (encoded as JsonObject).keys)
}
@Test
fun submitWithQueuedTrueEncodesQueued() {
val params = PromptSubmitParams(sessionId = "s-101", text = "queued prompt", queued = true)
val encoded = OkHttpProvider.json.encodeToJsonElement(PromptSubmitParams.serializer(), params)
val expected =
buildJsonObject {
put("session_id", "s-101")
put("text", "queued prompt")
put("queued", true)
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("session_id", "text", "queued"), (encoded as JsonObject).keys)
}
@Test
fun submitWithExplicitFalseQueuedIsEncoded() {
val params = PromptSubmitParams(sessionId = "s-102", text = "direct prompt", queued = false)
val encoded = OkHttpProvider.json.encodeToJsonElement(PromptSubmitParams.serializer(), params)
val expected =
buildJsonObject {
put("session_id", "s-102")
put("text", "direct prompt")
put("queued", false)
}
assertEquals(expected, encoded)
assertTrue(encoded is JsonObject)
assertEquals(setOf("session_id", "text", "queued"), (encoded as JsonObject).keys)
}
@Test
fun sessionCreateResultDecodesFullPayloadAndIgnoresUnknownKeys() {
val payload =
buildJsonObject {
put("session_id", "sess-created-1")
put("stored_session_id", "stored-1")
put("created_at", 123456789)
put("unknown_flag", true)
}
val decoded =
OkHttpProvider.json.decodeFromJsonElement(
SessionCreateResult.serializer(),
payload,
)
assertEquals("sess-created-1", decoded.sessionId)
assertEquals("stored-1", decoded.storedSessionId)
}
@Test
fun sessionCreateResultDecodesEmptyObjectToNulls() {
val payload = buildJsonObject {}
val decoded =
OkHttpProvider.json.decodeFromJsonElement(
SessionCreateResult.serializer(),
payload,
)
assertNull(decoded.sessionId)
assertNull(decoded.storedSessionId)
}
@Test
fun sessionResumeResultDecodesFullPayloadAndIgnoresUnknownKeys() {
val payload =
buildJsonObject {
put("session_id", "sess-resumed-1")
put("active_model", "gemini")
put("unread_count", 0)
}
val decoded =
OkHttpProvider.json.decodeFromJsonElement(
SessionResumeResult.serializer(),
payload,
)
assertEquals("sess-resumed-1", decoded.sessionId)
}
@Test
fun sessionResumeResultDecodesEmptyObjectToNulls() {
val payload = buildJsonObject {}
val decoded =
OkHttpProvider.json.decodeFromJsonElement(
SessionResumeResult.serializer(),
payload,
)
assertNull(decoded.sessionId)
}
@Test
fun promptSubmitResultDecodesFullPayloadAndIgnoresUnknownKeys() {
val payload =
buildJsonObject {
put("status", "accepted")
put("ticket_id", "tick-42")
put("queued_at", "2026-09-29T10:00:00Z")
}
val decoded =
OkHttpProvider.json.decodeFromJsonElement(
PromptSubmitResult.serializer(),
payload,
)
assertEquals("accepted", decoded.status)
}
@Test
fun promptSubmitResultDecodesEmptyObjectToNulls() {
val payload = buildJsonObject {}
val decoded =
OkHttpProvider.json.decodeFromJsonElement(
PromptSubmitResult.serializer(),
payload,
)
assertNull(decoded.status)
}
@Test
fun rpcMethodsRegistrationMatchesContract() {
assertEquals("session.create", RpcMethods.SESSION_CREATE.name)
assertEquals("session.resume", RpcMethods.SESSION_RESUME.name)
assertEquals("prompt.submit", RpcMethods.PROMPT_SUBMIT.name)
assertTrue(RpcMethods.all.contains(RpcMethods.SESSION_CREATE))
assertTrue(RpcMethods.all.contains(RpcMethods.SESSION_RESUME))
assertTrue(RpcMethods.all.contains(RpcMethods.PROMPT_SUBMIT))
}
}
@@ -21,6 +21,7 @@ import io.mockk.slot
import io.mockk.unmockkAll
import io.mockk.verify
import kotlinx.coroutines.CompletableDeferred
import kotlinx.serialization.json.JsonPrimitive
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -108,7 +109,7 @@ class NotificationReplyReceiverTest {
val params = arg<Map<String, Any>>(1)
val result =
if (method == WsMethods.SESSION_RESUME) {
mapOf("session_id" to "runtime-${params["session_id"]}")
mapOf("session_id" to "runtime-${params.str("session_id")}")
} else {
mapOf("accepted" to true)
}
@@ -147,7 +148,7 @@ class NotificationReplyReceiverTest {
verify {
HermesWsClient.request(
WsMethods.PROMPT_SUBMIT,
match { it["session_id"] == "runtime-session-abc" && it["text"] == "Hello" },
match { it.str("session_id") == "runtime-session-abc" && it.str("text") == "Hello" },
any(),
)
}
@@ -329,19 +330,19 @@ class NotificationReplyReceiverTest {
verify {
HermesWsClient.request(
WsMethods.SESSION_RESUME,
match { it["session_id"] == "session-abc" && it["omit_messages"] == true },
match { it.str("session_id") == "session-abc" && it["omit_messages"] == JsonPrimitive(true) },
any(),
)
HermesWsClient.request(
WsMethods.PROMPT_SUBMIT,
match { it["session_id"] == "runtime-session-abc" },
match { it.str("session_id") == "runtime-session-abc" },
any(),
)
}
verify(inverse = true) {
HermesWsClient.request(
WsMethods.PROMPT_SUBMIT,
match { it["session_id"] == "stale-runtime" },
match { it.str("session_id") == "stale-runtime" },
any(),
)
}
@@ -454,3 +455,5 @@ class NotificationReplyReceiverTest {
every { mockIntent.getStringExtra(NotificationReplyReceiver.EXTRA_SESSION_ID) } returns sessionId
}
}
private fun Map<String, Any>.str(key: String): String? = (this[key] as? JsonPrimitive)?.content ?: this[key] as? String