This commit is contained in:
8 files changed
+262
-64
No files matched your search
@@ -830,6 +830,17 @@ object HermesWsClient {
|
|||||||
return deferred
|
return deferred
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-suspending typed twin of [request] for callers that must enqueue inside a
|
||||||
|
* non-suspending owner check and await the raw result themselves.
|
||||||
|
*/
|
||||||
|
fun <P, R> requestTyped(
|
||||||
|
method: RpcMethod<P, R>,
|
||||||
|
params: P,
|
||||||
|
timeoutMs: Long = REQUEST_TIMEOUT_MS,
|
||||||
|
suppressErrorEvent: Boolean = false,
|
||||||
|
): CompletableDeferred<Any?> = request(method.name, encodeParams(method, params), timeoutMs, suppressErrorEvent)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a typed JSON-RPC request and await its deserialized result.
|
* Send a typed JSON-RPC request and await its deserialized result.
|
||||||
*
|
*
|
||||||
@@ -844,8 +855,7 @@ object HermesWsClient {
|
|||||||
timeoutMs: Long = REQUEST_TIMEOUT_MS,
|
timeoutMs: Long = REQUEST_TIMEOUT_MS,
|
||||||
suppressErrorEvent: Boolean = false,
|
suppressErrorEvent: Boolean = false,
|
||||||
): R {
|
): R {
|
||||||
val encoded = encodeParams(method, params)
|
val deferred = requestTyped(method, params, timeoutMs, suppressErrorEvent)
|
||||||
val deferred = request(method.name, encoded, timeoutMs, suppressErrorEvent)
|
|
||||||
val result =
|
val result =
|
||||||
try {
|
try {
|
||||||
deferred.await()
|
deferred.await()
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.m57.hermescontrol.data.ws.contract
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/** `commands.catalog` is sent without explicit params; the active profile is injected by WsProfileParams. */
|
||||||
|
@Serializable
|
||||||
|
data object CommandsCatalogParams
|
||||||
|
|
||||||
|
/** `arg` is sent whenever non-null, including an empty string, exactly like the old map payload. */
|
||||||
|
@Serializable
|
||||||
|
data class CommandDispatchParams(
|
||||||
|
@SerialName("name") val name: String,
|
||||||
|
@SerialName("arg") val arg: String? = null,
|
||||||
|
@SerialName("session_id") val sessionId: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SlashExecParams(
|
||||||
|
@SerialName("session_id") val sessionId: String,
|
||||||
|
@SerialName("command") val command: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** `data_url` carries the bytes for an uploaded file; `path` is for gateway-visible files. */
|
||||||
|
@Serializable
|
||||||
|
data class FileAttachParams(
|
||||||
|
@SerialName("session_id") val sessionId: String,
|
||||||
|
@SerialName("path") val path: String? = null,
|
||||||
|
@SerialName("data_url") val dataUrl: String? = null,
|
||||||
|
@SerialName("name") val name: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** The mobile client always sends the payload as `content_base64`; the `data` alias is not modelled. */
|
||||||
|
@Serializable
|
||||||
|
data class ImageAttachBytesParams(
|
||||||
|
@SerialName("session_id") val sessionId: String,
|
||||||
|
@SerialName("content_base64") val contentBase64: String? = null,
|
||||||
|
@SerialName("filename") val filename: String? = null,
|
||||||
|
@SerialName("ext") val ext: String? = null,
|
||||||
|
)
|
||||||
@@ -427,6 +427,46 @@ object RpcMethods {
|
|||||||
JsonElement.serializer(),
|
JsonElement.serializer(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Passthrough JsonElement results: callers keep their existing legacy decoders.
|
||||||
|
val COMMANDS_CATALOG: RpcMethod<CommandsCatalogParams, JsonElement> =
|
||||||
|
RpcMethod(
|
||||||
|
WsMethods.COMMANDS_CATALOG,
|
||||||
|
CommandsCatalogParams.serializer(),
|
||||||
|
JsonElement.serializer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Passthrough JsonElement results: callers keep their existing legacy decoders.
|
||||||
|
val COMMAND_DISPATCH: RpcMethod<CommandDispatchParams, JsonElement> =
|
||||||
|
RpcMethod(
|
||||||
|
WsMethods.COMMAND_DISPATCH,
|
||||||
|
CommandDispatchParams.serializer(),
|
||||||
|
JsonElement.serializer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Passthrough JsonElement results: callers keep their existing legacy decoders.
|
||||||
|
val SLASH_EXEC: RpcMethod<SlashExecParams, JsonElement> =
|
||||||
|
RpcMethod(
|
||||||
|
WsMethods.SLASH_EXEC,
|
||||||
|
SlashExecParams.serializer(),
|
||||||
|
JsonElement.serializer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Passthrough JsonElement results: callers keep their existing legacy decoders.
|
||||||
|
val FILE_ATTACH: RpcMethod<FileAttachParams, JsonElement> =
|
||||||
|
RpcMethod(
|
||||||
|
WsMethods.FILE_ATTACH,
|
||||||
|
FileAttachParams.serializer(),
|
||||||
|
JsonElement.serializer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Passthrough JsonElement results: callers keep their existing legacy decoders.
|
||||||
|
val IMAGE_ATTACH_BYTES: RpcMethod<ImageAttachBytesParams, JsonElement> =
|
||||||
|
RpcMethod(
|
||||||
|
WsMethods.IMAGE_ATTACH_BYTES,
|
||||||
|
ImageAttachBytesParams.serializer(),
|
||||||
|
JsonElement.serializer(),
|
||||||
|
)
|
||||||
|
|
||||||
/** Every typed method. GatewayContractTest iterates this; future migrations append here. */
|
/** Every typed method. GatewayContractTest iterates this; future migrations append here. */
|
||||||
val all: List<RpcMethod<*, *>> =
|
val all: List<RpcMethod<*, *>> =
|
||||||
listOf(
|
listOf(
|
||||||
@@ -482,5 +522,10 @@ object RpcMethods {
|
|||||||
CONFIG_GET,
|
CONFIG_GET,
|
||||||
MODEL_OPTIONS,
|
MODEL_OPTIONS,
|
||||||
CLIENT_CAPABILITIES,
|
CLIENT_CAPABILITIES,
|
||||||
|
COMMANDS_CATALOG,
|
||||||
|
COMMAND_DISPATCH,
|
||||||
|
SLASH_EXEC,
|
||||||
|
FILE_ATTACH,
|
||||||
|
IMAGE_ATTACH_BYTES,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -37,8 +37,12 @@ import com.m57.hermescontrol.data.ws.HermesWsClient
|
|||||||
import com.m57.hermescontrol.data.ws.JsonRpcError
|
import com.m57.hermescontrol.data.ws.JsonRpcError
|
||||||
import com.m57.hermescontrol.data.ws.WsEvent
|
import com.m57.hermescontrol.data.ws.WsEvent
|
||||||
import com.m57.hermescontrol.data.ws.WsMethods
|
import com.m57.hermescontrol.data.ws.WsMethods
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.CommandDispatchParams
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.CommandsCatalogParams
|
||||||
import com.m57.hermescontrol.data.ws.contract.ConfigGetParams
|
import com.m57.hermescontrol.data.ws.contract.ConfigGetParams
|
||||||
import com.m57.hermescontrol.data.ws.contract.ConfigSetParams
|
import com.m57.hermescontrol.data.ws.contract.ConfigSetParams
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.FileAttachParams
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.ImageAttachBytesParams
|
||||||
import com.m57.hermescontrol.data.ws.contract.PromptBtwParams
|
import com.m57.hermescontrol.data.ws.contract.PromptBtwParams
|
||||||
import com.m57.hermescontrol.data.ws.contract.RpcMethod
|
import com.m57.hermescontrol.data.ws.contract.RpcMethod
|
||||||
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||||
@@ -51,6 +55,7 @@ import com.m57.hermescontrol.data.ws.contract.SessionIdParams
|
|||||||
import com.m57.hermescontrol.data.ws.contract.SessionInterruptParams
|
import com.m57.hermescontrol.data.ws.contract.SessionInterruptParams
|
||||||
import com.m57.hermescontrol.data.ws.contract.SessionListParams
|
import com.m57.hermescontrol.data.ws.contract.SessionListParams
|
||||||
import com.m57.hermescontrol.data.ws.contract.SessionResumeParams
|
import com.m57.hermescontrol.data.ws.contract.SessionResumeParams
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.SlashExecParams
|
||||||
import com.m57.hermescontrol.data.ws.contract.TypedRpcSender
|
import com.m57.hermescontrol.data.ws.contract.TypedRpcSender
|
||||||
import com.m57.hermescontrol.data.ws.toAny
|
import com.m57.hermescontrol.data.ws.toAny
|
||||||
import com.m57.hermescontrol.data.ws.toJsonElement
|
import com.m57.hermescontrol.data.ws.toJsonElement
|
||||||
@@ -3400,13 +3405,13 @@ class ChatViewModel(
|
|||||||
if (attachment.isImage) {
|
if (attachment.isImage) {
|
||||||
val deferred =
|
val deferred =
|
||||||
enqueueOwned {
|
enqueueOwned {
|
||||||
wsClient.request(
|
wsClient.requestTyped(
|
||||||
WsMethods.IMAGE_ATTACH_BYTES,
|
RpcMethods.IMAGE_ATTACH_BYTES,
|
||||||
mapOf(
|
ImageAttachBytesParams(
|
||||||
"session_id" to owner.agentSessionId,
|
sessionId = owner.agentSessionId,
|
||||||
"content_base64" to "data:${attachment.mimeType};base64,$b64",
|
contentBase64 = "data:${attachment.mimeType};base64,$b64",
|
||||||
"filename" to attachment.name,
|
filename = attachment.name,
|
||||||
"ext" to attachment.fileExtension,
|
ext = attachment.fileExtension,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} ?: return@launch
|
} ?: return@launch
|
||||||
@@ -3419,12 +3424,12 @@ class ChatViewModel(
|
|||||||
} else {
|
} else {
|
||||||
val deferred =
|
val deferred =
|
||||||
enqueueOwned {
|
enqueueOwned {
|
||||||
wsClient.request(
|
wsClient.requestTyped(
|
||||||
WsMethods.FILE_ATTACH,
|
RpcMethods.FILE_ATTACH,
|
||||||
mapOf(
|
FileAttachParams(
|
||||||
"session_id" to owner.agentSessionId,
|
sessionId = owner.agentSessionId,
|
||||||
"data_url" to "data:${attachment.mimeType};base64,$b64",
|
dataUrl = "data:${attachment.mimeType};base64,$b64",
|
||||||
"name" to attachment.name,
|
name = attachment.name,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} ?: return@launch
|
} ?: return@launch
|
||||||
@@ -4076,12 +4081,11 @@ class ChatViewModel(
|
|||||||
// #576). For those we fall back to slash.exec, which runs the
|
// #576). For those we fall back to slash.exec, which runs the
|
||||||
// full COMMAND_REGISTRY through the worker.
|
// full COMMAND_REGISTRY through the worker.
|
||||||
val result =
|
val result =
|
||||||
wsClient
|
wsClient.call(
|
||||||
.request(
|
RpcMethods.COMMAND_DISPATCH,
|
||||||
WsMethods.COMMAND_DISPATCH,
|
CommandDispatchParams(name = name, arg = arg, sessionId = sessionId),
|
||||||
mapOf("name" to name, "arg" to arg, "session_id" to sessionId),
|
)
|
||||||
).await()
|
handleDispatchResult(result.toAny())
|
||||||
handleDispatchResult(result)
|
|
||||||
} catch (e: HermesWsClient.HermesRpcException) {
|
} catch (e: HermesWsClient.HermesRpcException) {
|
||||||
val msg = e.message.orEmpty()
|
val msg = e.message.orEmpty()
|
||||||
// Registry miss on command.dispatch: the backend emits exactly
|
// Registry miss on command.dispatch: the backend emits exactly
|
||||||
@@ -4093,15 +4097,14 @@ class ChatViewModel(
|
|||||||
// which routes the full CLI command set through the worker.
|
// which routes the full CLI command set through the worker.
|
||||||
try {
|
try {
|
||||||
val result =
|
val result =
|
||||||
wsClient
|
wsClient.call(
|
||||||
.request(
|
RpcMethods.SLASH_EXEC,
|
||||||
WsMethods.SLASH_EXEC,
|
SlashExecParams(
|
||||||
mapOf(
|
sessionId = sessionId,
|
||||||
"command" to "/$name${if (arg.isNotEmpty()) " $arg" else ""}",
|
command = "/$name${if (arg.isNotEmpty()) " $arg" else ""}",
|
||||||
"session_id" to sessionId,
|
|
||||||
),
|
),
|
||||||
).await()
|
)
|
||||||
val output = (result as? Map<*, *>)?.get("output") as? String
|
val output = (result.toAny() as? Map<*, *>)?.get("output") as? String
|
||||||
if (!output.isNullOrBlank()) addAssistantMessage(output)
|
if (!output.isNullOrBlank()) addAssistantMessage(output)
|
||||||
} catch (e2: HermesWsClient.HermesRpcException) {
|
} catch (e2: HermesWsClient.HermesRpcException) {
|
||||||
addAssistantMessage("/$name: ${e2.message}")
|
addAssistantMessage("/$name: ${e2.message}")
|
||||||
@@ -4173,12 +4176,11 @@ class ChatViewModel(
|
|||||||
viewModelScope.launch(ioDispatcher) {
|
viewModelScope.launch(ioDispatcher) {
|
||||||
try {
|
try {
|
||||||
val result =
|
val result =
|
||||||
wsClient
|
wsClient.call(
|
||||||
.request(
|
RpcMethods.COMMAND_DISPATCH,
|
||||||
WsMethods.COMMAND_DISPATCH,
|
CommandDispatchParams(name = "undo", arg = count, sessionId = sessionId),
|
||||||
mapOf("name" to "undo", "arg" to count, "session_id" to sessionId),
|
)
|
||||||
).await()
|
handleDispatchResult(result.toAny())
|
||||||
handleDispatchResult(result)
|
|
||||||
} catch (e: HermesWsClient.HermesRpcException) {
|
} catch (e: HermesWsClient.HermesRpcException) {
|
||||||
addAssistantMessage(e.message ?: "Failed to undo.")
|
addAssistantMessage(e.message ?: "Failed to undo.")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -4474,7 +4476,8 @@ class ChatViewModel(
|
|||||||
private fun fetchCommandCatalog() {
|
private fun fetchCommandCatalog() {
|
||||||
viewModelScope.launch(ioDispatcher) {
|
viewModelScope.launch(ioDispatcher) {
|
||||||
wsClient.send(
|
wsClient.send(
|
||||||
WsMethods.COMMANDS_CATALOG,
|
RpcMethods.COMMANDS_CATALOG,
|
||||||
|
CommandsCatalogParams,
|
||||||
onSent = { id -> trackRequest(id, WsMethods.COMMANDS_CATALOG) },
|
onSent = { id -> trackRequest(id, WsMethods.COMMANDS_CATALOG) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,14 +314,6 @@ class RawRpcParamsRatchetTest {
|
|||||||
* Notice HermesWsClient.kt retains its other raw uses: `CLIENT_CAPABILITIES`, `PROMPT_SUBMIT`,
|
* Notice HermesWsClient.kt retains its other raw uses: `CLIENT_CAPABILITIES`, `PROMPT_SUBMIT`,
|
||||||
* and `SESSION_REDIRECT`.
|
* and `SESSION_REDIRECT`.
|
||||||
*/
|
*/
|
||||||
val BASELINE: List<String> =
|
val BASELINE: List<String> = emptyList()
|
||||||
listOf(
|
|
||||||
"ui/chat/ChatViewModel.kt::COMMANDS_CATALOG",
|
|
||||||
"ui/chat/ChatViewModel.kt::COMMAND_DISPATCH",
|
|
||||||
"ui/chat/ChatViewModel.kt::COMMAND_DISPATCH",
|
|
||||||
"ui/chat/ChatViewModel.kt::FILE_ATTACH",
|
|
||||||
"ui/chat/ChatViewModel.kt::IMAGE_ATTACH_BYTES",
|
|
||||||
"ui/chat/ChatViewModel.kt::SLASH_EXEC",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.m57.hermescontrol.data.ws.contract
|
||||||
|
|
||||||
|
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
||||||
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
import kotlinx.serialization.json.put
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class ChatCommandsTypesTest {
|
||||||
|
private val json = OkHttpProvider.json
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun commandsCatalogEncodesAsEmptyObject() {
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject { },
|
||||||
|
json.encodeToJsonElement(CommandsCatalogParams.serializer(), CommandsCatalogParams),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun commandDispatchKeepsEmptyArgAndOmitsNullOnes() {
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject {
|
||||||
|
put("name", "help")
|
||||||
|
put("arg", "")
|
||||||
|
put("session_id", "s1")
|
||||||
|
},
|
||||||
|
json.encodeToJsonElement(
|
||||||
|
CommandDispatchParams.serializer(),
|
||||||
|
CommandDispatchParams(name = "help", arg = "", sessionId = "s1"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject { put("name", "help") },
|
||||||
|
json.encodeToJsonElement(CommandDispatchParams.serializer(), CommandDispatchParams(name = "help")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun slashExecEncodesSessionAndCommand() {
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject {
|
||||||
|
put("session_id", "s1")
|
||||||
|
put("command", "/status")
|
||||||
|
},
|
||||||
|
json.encodeToJsonElement(
|
||||||
|
SlashExecParams.serializer(),
|
||||||
|
SlashExecParams(sessionId = "s1", command = "/status"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fileAttachSendsDataUrlAndNameWithoutPath() {
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject {
|
||||||
|
put("session_id", "s1")
|
||||||
|
put("data_url", "data:text/plain;base64,QQ==")
|
||||||
|
put("name", "a.txt")
|
||||||
|
},
|
||||||
|
json.encodeToJsonElement(
|
||||||
|
FileAttachParams.serializer(),
|
||||||
|
FileAttachParams(sessionId = "s1", dataUrl = "data:text/plain;base64,QQ==", name = "a.txt"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun imageAttachBytesEncodesContentBase64FilenameAndExt() {
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject {
|
||||||
|
put("session_id", "s1")
|
||||||
|
put("content_base64", "data:image/png;base64,QQ==")
|
||||||
|
put("filename", "p.png")
|
||||||
|
put("ext", "png")
|
||||||
|
},
|
||||||
|
json.encodeToJsonElement(
|
||||||
|
ImageAttachBytesParams.serializer(),
|
||||||
|
ImageAttachBytesParams(
|
||||||
|
sessionId = "s1",
|
||||||
|
contentBase64 = "data:image/png;base64,QQ==",
|
||||||
|
filename = "p.png",
|
||||||
|
ext = "png",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun descriptorsAreRegisteredWithContractNames() {
|
||||||
|
listOf(
|
||||||
|
RpcMethods.COMMANDS_CATALOG to "commands.catalog",
|
||||||
|
RpcMethods.COMMAND_DISPATCH to "command.dispatch",
|
||||||
|
RpcMethods.SLASH_EXEC to "slash.exec",
|
||||||
|
RpcMethods.FILE_ATTACH to "file.attach",
|
||||||
|
RpcMethods.IMAGE_ATTACH_BYTES to "image.attach_bytes",
|
||||||
|
).forEach { (method, name) ->
|
||||||
|
assertTrue("$name not in RpcMethods.all", method in RpcMethods.all)
|
||||||
|
assertEquals(name, method.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1483,7 +1483,11 @@ class ChatViewModelTest {
|
|||||||
every {
|
every {
|
||||||
HermesWsClient.request(
|
HermesWsClient.request(
|
||||||
WsMethods.COMMAND_DISPATCH,
|
WsMethods.COMMAND_DISPATCH,
|
||||||
mapOf("name" to "undo", "arg" to "2", "session_id" to sessionId),
|
buildJsonObject {
|
||||||
|
put("name", "undo")
|
||||||
|
put("arg", "2")
|
||||||
|
put("session_id", sessionId)
|
||||||
|
},
|
||||||
any(),
|
any(),
|
||||||
)
|
)
|
||||||
} returns
|
} returns
|
||||||
@@ -7238,7 +7242,7 @@ class ChatViewModelTest {
|
|||||||
verify { HermesWsClient.request(WsMethods.IMAGE_ATTACH_BYTES, any()) }
|
verify { HermesWsClient.request(WsMethods.IMAGE_ATTACH_BYTES, any()) }
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"session_id must be forwarded to image.attach_bytes",
|
"session_id must be forwarded to image.attach_bytes",
|
||||||
sessionId,
|
JsonPrimitive(sessionId),
|
||||||
paramsSlot.captured["session_id"],
|
paramsSlot.captured["session_id"],
|
||||||
)
|
)
|
||||||
assertTrue("encoded attachment cache must be cleaned", attachmentCacheDir.listFiles().isNullOrEmpty())
|
assertTrue("encoded attachment cache must be cleaned", attachmentCacheDir.listFiles().isNullOrEmpty())
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import kotlinx.coroutines.test.advanceUntilIdle
|
|||||||
import kotlinx.coroutines.test.resetMain
|
import kotlinx.coroutines.test.resetMain
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlinx.coroutines.test.setMain
|
import kotlinx.coroutines.test.setMain
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
@@ -218,9 +219,9 @@ class SlashCommandDispatchRpcTest {
|
|||||||
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
||||||
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
||||||
val params = paramsCalls[dispatchIndex]
|
val params = paramsCalls[dispatchIndex]
|
||||||
assertEquals("help", params["name"])
|
assertEquals(JsonPrimitive("help"), params["name"])
|
||||||
assertEquals("", params["arg"])
|
assertEquals(JsonPrimitive(""), params["arg"])
|
||||||
assertEquals(sessionId, params["session_id"])
|
assertEquals(JsonPrimitive(sessionId), params["session_id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -482,8 +483,8 @@ class SlashCommandDispatchRpcTest {
|
|||||||
assertTrue("expected SLASH_EXEC fallback, got $methodCalls", execIndex >= 0)
|
assertTrue("expected SLASH_EXEC fallback, got $methodCalls", execIndex >= 0)
|
||||||
assertTrue("dispatch must precede fallback", dispatchIndex < execIndex)
|
assertTrue("dispatch must precede fallback", dispatchIndex < execIndex)
|
||||||
val params = paramsCalls[execIndex]
|
val params = paramsCalls[execIndex]
|
||||||
assertEquals("/status", params["command"])
|
assertEquals(JsonPrimitive("/status"), params["command"])
|
||||||
assertEquals(sessionId, params["session_id"])
|
assertEquals(JsonPrimitive(sessionId), params["session_id"])
|
||||||
|
|
||||||
// And the slash.exec output must be surfaced to the user.
|
// And the slash.exec output must be surfaced to the user.
|
||||||
val last =
|
val last =
|
||||||
@@ -629,9 +630,9 @@ class SlashCommandDispatchRpcTest {
|
|||||||
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
||||||
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
||||||
val params = paramsCalls[dispatchIndex]
|
val params = paramsCalls[dispatchIndex]
|
||||||
assertEquals("init", params["name"])
|
assertEquals(JsonPrimitive("init"), params["name"])
|
||||||
assertEquals("", params["arg"])
|
assertEquals(JsonPrimitive(""), params["arg"])
|
||||||
assertEquals(sessionId, params["session_id"])
|
assertEquals(JsonPrimitive(sessionId), params["session_id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -654,9 +655,9 @@ class SlashCommandDispatchRpcTest {
|
|||||||
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
||||||
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
||||||
val params = paramsCalls[dispatchIndex]
|
val params = paramsCalls[dispatchIndex]
|
||||||
assertEquals("init", params["name"])
|
assertEquals(JsonPrimitive("init"), params["name"])
|
||||||
assertEquals("extra context here", params["arg"])
|
assertEquals(JsonPrimitive("extra context here"), params["arg"])
|
||||||
assertEquals(sessionId, params["session_id"])
|
assertEquals(JsonPrimitive(sessionId), params["session_id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -682,9 +683,9 @@ class SlashCommandDispatchRpcTest {
|
|||||||
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
||||||
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
||||||
val params = paramsCalls[dispatchIndex]
|
val params = paramsCalls[dispatchIndex]
|
||||||
assertEquals("focus", params["name"])
|
assertEquals(JsonPrimitive("focus"), params["name"])
|
||||||
assertEquals("", params["arg"])
|
assertEquals(JsonPrimitive(""), params["arg"])
|
||||||
assertEquals(sessionId, params["session_id"])
|
assertEquals(JsonPrimitive(sessionId), params["session_id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -707,9 +708,9 @@ class SlashCommandDispatchRpcTest {
|
|||||||
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
val dispatchIndex = methodCalls.indexOf(WsMethods.COMMAND_DISPATCH)
|
||||||
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
assertTrue("expected COMMAND_DISPATCH, got $methodCalls", dispatchIndex >= 0)
|
||||||
val params = paramsCalls[dispatchIndex]
|
val params = paramsCalls[dispatchIndex]
|
||||||
assertEquals("focus", params["name"])
|
assertEquals(JsonPrimitive("focus"), params["name"])
|
||||||
assertEquals("on", params["arg"])
|
assertEquals(JsonPrimitive("on"), params["arg"])
|
||||||
assertEquals(sessionId, params["session_id"])
|
assertEquals(JsonPrimitive(sessionId), params["session_id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in new issue
Block a user