This commit is contained in:
11 files changed
+175
-73
No files matched your search
@@ -1,8 +1,10 @@
|
|||||||
package com.m57.hermescontrol.data.ws
|
package com.m57.hermescontrol.data.ws
|
||||||
|
|
||||||
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
||||||
import kotlinx.coroutines.CancellationException
|
import com.m57.hermescontrol.data.ws.contract.HermesRpcCaller
|
||||||
import kotlinx.serialization.json.Json
|
import com.m57.hermescontrol.data.ws.contract.PluginsManageParams
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.TypedRpcCaller
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.decodeFromJsonElement
|
import kotlinx.serialization.json.decodeFromJsonElement
|
||||||
@@ -11,23 +13,14 @@ import kotlinx.serialization.json.decodeFromJsonElement
|
|||||||
* Manages plugin settings and inspection through the gateway's canonical `plugins.manage` RPC.
|
* Manages plugin settings and inspection through the gateway's canonical `plugins.manage` RPC.
|
||||||
*/
|
*/
|
||||||
class PluginManageRepository(
|
class PluginManageRepository(
|
||||||
private val rpcRequest: suspend (String, Map<String, Any?>) -> Any? = { method, params ->
|
private val caller: TypedRpcCaller = HermesRpcCaller,
|
||||||
val deferred = HermesWsClient.request(method, params.filterValues { it != null }.mapValues { it.value ?: "" })
|
|
||||||
try {
|
|
||||||
deferred.await()
|
|
||||||
} catch (e: CancellationException) {
|
|
||||||
deferred.cancel(e)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
suspend fun listPlugins(profile: String? = null): List<AgentPluginRow> {
|
suspend fun listPlugins(profile: String? = null): List<AgentPluginRow> {
|
||||||
val params = mutableMapOf<String, Any?>("action" to "list")
|
val element =
|
||||||
if (!profile.isNullOrBlank()) {
|
caller.call(
|
||||||
params["profile"] = profile
|
RpcMethods.PLUGINS_MANAGE,
|
||||||
}
|
PluginsManageParams(action = "list", profile = profile.orNullIfBlank()),
|
||||||
val result = rpcRequest(WsMethods.PLUGINS_MANAGE, params)
|
)
|
||||||
val element = result.toJsonElement()
|
|
||||||
val pluginsElement = (element as? JsonObject)?.get("plugins") ?: return emptyList()
|
val pluginsElement = (element as? JsonObject)?.get("plugins") ?: return emptyList()
|
||||||
return OkHttpProvider.json.decodeFromJsonElement<List<AgentPluginRow>>(pluginsElement)
|
return OkHttpProvider.json.decodeFromJsonElement<List<AgentPluginRow>>(pluginsElement)
|
||||||
}
|
}
|
||||||
@@ -38,15 +31,15 @@ class PluginManageRepository(
|
|||||||
profile: String? = null,
|
profile: String? = null,
|
||||||
): PluginsManageSettingsResult {
|
): PluginsManageSettingsResult {
|
||||||
val params =
|
val params =
|
||||||
mutableMapOf<String, Any?>(
|
PluginsManageParams(
|
||||||
"action" to "settings",
|
action = "settings",
|
||||||
"key" to key,
|
key = key,
|
||||||
"values" to JsonObject(values),
|
values = JsonObject(values),
|
||||||
|
profile = profile.orNullIfBlank(),
|
||||||
)
|
)
|
||||||
if (!profile.isNullOrBlank()) {
|
val result = caller.call(RpcMethods.PLUGINS_MANAGE, params)
|
||||||
params["profile"] = profile
|
return OkHttpProvider.json.decodeFromJsonElement<PluginsManageSettingsResult>(result)
|
||||||
}
|
|
||||||
val result = rpcRequest(WsMethods.PLUGINS_MANAGE, params)
|
|
||||||
return OkHttpProvider.json.decodeFromJsonElement<PluginsManageSettingsResult>(result.toJsonElement())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun String?.orNullIfBlank(): String? = this?.takeIf { it.isNotBlank() }
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.m57.hermescontrol.data.ws
|
package com.m57.hermescontrol.data.ws
|
||||||
|
|
||||||
import kotlinx.coroutines.CancellationException
|
import com.m57.hermescontrol.data.ws.contract.HermesRpcCaller
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.PluginsManageParams
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.TypedRpcCaller
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.decodeFromJsonElement
|
import kotlinx.serialization.json.decodeFromJsonElement
|
||||||
@@ -14,18 +17,10 @@ data class PluginRemovalResult(
|
|||||||
|
|
||||||
/** Removes a user-installed plugin through the gateway's canonical plugin manager. */
|
/** Removes a user-installed plugin through the gateway's canonical plugin manager. */
|
||||||
class PluginRemovalRepository(
|
class PluginRemovalRepository(
|
||||||
private val rpcRequest: suspend (String, Map<String, Any>) -> Any? = { method, params ->
|
private val caller: TypedRpcCaller = HermesRpcCaller,
|
||||||
val deferred = HermesWsClient.request(method, params)
|
|
||||||
try {
|
|
||||||
deferred.await()
|
|
||||||
} catch (e: CancellationException) {
|
|
||||||
deferred.cancel(e)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
suspend fun remove(name: String): PluginRemovalResult {
|
suspend fun remove(name: String): PluginRemovalResult {
|
||||||
val result = rpcRequest(WsMethods.PLUGINS_MANAGE, mapOf("action" to "remove", "name" to name))
|
val result = caller.call(RpcMethods.PLUGINS_MANAGE, PluginsManageParams(action = "remove", name = name))
|
||||||
return Json.decodeFromJsonElement<PluginRemovalResult>(result.toJsonElement())
|
return Json.decodeFromJsonElement<PluginRemovalResult>(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,10 @@ package com.m57.hermescontrol.data.ws
|
|||||||
import com.m57.hermescontrol.data.model.ProjectInfo
|
import com.m57.hermescontrol.data.model.ProjectInfo
|
||||||
import com.m57.hermescontrol.data.model.ProjectsListResponse
|
import com.m57.hermescontrol.data.model.ProjectsListResponse
|
||||||
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.HermesRpcCaller
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.ProjectsListParams
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||||
|
import com.m57.hermescontrol.data.ws.contract.TypedRpcCaller
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
|
||||||
@@ -17,14 +21,12 @@ class HermesProjectsSource(
|
|||||||
// Only ask a live socket: an offline request would be queued and force a reconnect on
|
// Only ask a live socket: an offline request would be queued and force a reconnect on
|
||||||
// every refresh. The view model refetches when the connection comes back.
|
// every refresh. The view model refetches when the connection comes back.
|
||||||
private val isConnected: () -> Boolean = { HermesWsClient.connectionStatus.value == ConnectionStatus.CONNECTED },
|
private val isConnected: () -> Boolean = { HermesWsClient.connectionStatus.value == ConnectionStatus.CONNECTED },
|
||||||
private val rpcRequest: suspend (method: String, params: Map<String, Any>) -> Any? = { method, params ->
|
private val caller: TypedRpcCaller = HermesRpcCaller,
|
||||||
HermesWsClient.request(method, params).await()
|
|
||||||
},
|
|
||||||
) : ProjectsSource {
|
) : ProjectsSource {
|
||||||
override suspend fun fetchProjects(): List<ProjectInfo>? {
|
override suspend fun fetchProjects(): List<ProjectInfo>? {
|
||||||
if (!isConnected()) return null
|
if (!isConnected()) return null
|
||||||
return try {
|
return try {
|
||||||
val element = SessionLiveStatusDecoder.anyToJsonElement(rpcRequest(WsMethods.PROJECTS_LIST, emptyMap()))
|
val element = caller.call(RpcMethods.PROJECTS_LIST, ProjectsListParams)
|
||||||
if (element is JsonObject && element.containsKey("projects")) {
|
if (element is JsonObject && element.containsKey("projects")) {
|
||||||
OkHttpProvider.json.decodeFromJsonElement(ProjectsListResponse.serializer(), element).projects
|
OkHttpProvider.json.decodeFromJsonElement(ProjectsListResponse.serializer(), element).projects
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.m57.hermescontrol.data.ws.contract
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `plugins.manage` params. [action] has no default so it is always encoded; every other field is
|
||||||
|
* omitted when null. [profile] is an explicit override only; otherwise WsProfileParams injects it.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class PluginsManageParams(
|
||||||
|
@SerialName("action") val action: String,
|
||||||
|
@SerialName("profile") val profile: String? = null,
|
||||||
|
@SerialName("key") val key: String? = null,
|
||||||
|
@SerialName("name") val name: String? = null,
|
||||||
|
@SerialName("enable") val enable: Boolean? = null,
|
||||||
|
@SerialName("identifier") val identifier: String? = null,
|
||||||
|
@SerialName("repo") val repo: String? = null,
|
||||||
|
@SerialName("catalog_name") val catalogName: String? = null,
|
||||||
|
@SerialName("force") val force: Boolean? = null,
|
||||||
|
@SerialName("ref") val ref: String? = null,
|
||||||
|
@SerialName("accept_capabilities") val acceptCapabilities: Boolean? = null,
|
||||||
|
@SerialName("values") val values: JsonObject? = null,
|
||||||
|
)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.m57.hermescontrol.data.ws.contract
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/** `projects.list` takes no explicit params; the active profile is injected by WsProfileParams. */
|
||||||
|
@Serializable
|
||||||
|
data object ProjectsListParams
|
||||||
@@ -363,6 +363,22 @@ object RpcMethods {
|
|||||||
JsonElement.serializer(),
|
JsonElement.serializer(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Passthrough JsonElement results: callers keep their existing legacy decoders.
|
||||||
|
val PLUGINS_MANAGE: RpcMethod<PluginsManageParams, JsonElement> =
|
||||||
|
RpcMethod(
|
||||||
|
WsMethods.PLUGINS_MANAGE,
|
||||||
|
PluginsManageParams.serializer(),
|
||||||
|
JsonElement.serializer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Passthrough JsonElement results: callers keep their existing legacy decoders.
|
||||||
|
val PROJECTS_LIST: RpcMethod<ProjectsListParams, JsonElement> =
|
||||||
|
RpcMethod(
|
||||||
|
WsMethods.PROJECTS_LIST,
|
||||||
|
ProjectsListParams.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(
|
||||||
@@ -410,5 +426,7 @@ object RpcMethods {
|
|||||||
SUBAGENT_TAIL,
|
SUBAGENT_TAIL,
|
||||||
PROCESS_LIST,
|
PROCESS_LIST,
|
||||||
PROCESS_KILL,
|
PROCESS_KILL,
|
||||||
|
PLUGINS_MANAGE,
|
||||||
|
PROJECTS_LIST,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,9 @@ import org.junit.Assert.fail
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class PluginManageRepositoryTest {
|
class PluginManageRepositoryTest {
|
||||||
|
private fun pluginRepo(handler: suspend (String, Map<String, Any>) -> Any?) =
|
||||||
|
PluginManageRepository(fakeCaller(handler))
|
||||||
|
|
||||||
// CONTRACT: `listPlugins(profile)` calls WsMethods.PLUGINS_MANAGE with {"action": "list"} (and profile if set) and decodes plugins list.
|
// CONTRACT: `listPlugins(profile)` calls WsMethods.PLUGINS_MANAGE with {"action": "list"} (and profile if set) and decodes plugins list.
|
||||||
@Test
|
@Test
|
||||||
fun `listPlugins without profile calls PLUGINS_MANAGE action list and decodes plugins list`() =
|
fun `listPlugins without profile calls PLUGINS_MANAGE action list and decodes plugins list`() =
|
||||||
@@ -44,7 +47,7 @@ class PluginManageRepositoryTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val repository =
|
val repository =
|
||||||
PluginManageRepository { method, params ->
|
pluginRepo { method, params ->
|
||||||
capturedMethod = method
|
capturedMethod = method
|
||||||
capturedParams = params
|
capturedParams = params
|
||||||
mockPayload
|
mockPayload
|
||||||
@@ -80,7 +83,7 @@ class PluginManageRepositoryTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val repository =
|
val repository =
|
||||||
PluginManageRepository { method, params ->
|
pluginRepo { method, params ->
|
||||||
capturedMethod = method
|
capturedMethod = method
|
||||||
capturedParams = params
|
capturedParams = params
|
||||||
mockPayload
|
mockPayload
|
||||||
@@ -98,19 +101,19 @@ class PluginManageRepositoryTest {
|
|||||||
fun `listPlugins returns empty list when plugins key is missing or response is not a JsonObject`() =
|
fun `listPlugins returns empty list when plugins key is missing or response is not a JsonObject`() =
|
||||||
runTest {
|
runTest {
|
||||||
val emptyObjRepo =
|
val emptyObjRepo =
|
||||||
PluginManageRepository { _, _ ->
|
pluginRepo { _, _ ->
|
||||||
OkHttpProvider.json.parseToJsonElement("{}")
|
OkHttpProvider.json.parseToJsonElement("{}")
|
||||||
}
|
}
|
||||||
assertTrue(emptyObjRepo.listPlugins().isEmpty())
|
assertTrue(emptyObjRepo.listPlugins().isEmpty())
|
||||||
|
|
||||||
val nonObjRepo =
|
val nonObjRepo =
|
||||||
PluginManageRepository { _, _ ->
|
pluginRepo { _, _ ->
|
||||||
OkHttpProvider.json.parseToJsonElement("[]")
|
OkHttpProvider.json.parseToJsonElement("[]")
|
||||||
}
|
}
|
||||||
assertTrue(nonObjRepo.listPlugins().isEmpty())
|
assertTrue(nonObjRepo.listPlugins().isEmpty())
|
||||||
|
|
||||||
val nullRepo =
|
val nullRepo =
|
||||||
PluginManageRepository { _, _ ->
|
pluginRepo { _, _ ->
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
assertTrue(nullRepo.listPlugins().isEmpty())
|
assertTrue(nullRepo.listPlugins().isEmpty())
|
||||||
@@ -139,7 +142,7 @@ class PluginManageRepositoryTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val repository =
|
val repository =
|
||||||
PluginManageRepository { method, params ->
|
pluginRepo { method, params ->
|
||||||
capturedMethod = method
|
capturedMethod = method
|
||||||
capturedParams = params
|
capturedParams = params
|
||||||
mockResponse
|
mockResponse
|
||||||
@@ -156,7 +159,7 @@ class PluginManageRepositoryTest {
|
|||||||
assertEquals(WsMethods.PLUGINS_MANAGE, capturedMethod)
|
assertEquals(WsMethods.PLUGINS_MANAGE, capturedMethod)
|
||||||
assertEquals("settings", capturedParams["action"])
|
assertEquals("settings", capturedParams["action"])
|
||||||
assertEquals("camofox", capturedParams["key"])
|
assertEquals("camofox", capturedParams["key"])
|
||||||
assertEquals(JsonObject(valuesToSave), capturedParams["values"])
|
assertEquals(mapOf("port" to 9222, "headless" to true), capturedParams["values"])
|
||||||
assertFalse(capturedParams.containsKey("profile"))
|
assertFalse(capturedParams.containsKey("profile"))
|
||||||
|
|
||||||
assertTrue(result.ok)
|
assertTrue(result.ok)
|
||||||
@@ -186,7 +189,7 @@ class PluginManageRepositoryTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val repository =
|
val repository =
|
||||||
PluginManageRepository { method, params ->
|
pluginRepo { method, params ->
|
||||||
capturedMethod = method
|
capturedMethod = method
|
||||||
capturedParams = params
|
capturedParams = params
|
||||||
mockResponse
|
mockResponse
|
||||||
@@ -204,7 +207,7 @@ class PluginManageRepositoryTest {
|
|||||||
mapOf(
|
mapOf(
|
||||||
"action" to "settings",
|
"action" to "settings",
|
||||||
"key" to "camofox",
|
"key" to "camofox",
|
||||||
"values" to JsonObject(valuesToSave),
|
"values" to mapOf("port" to 8080),
|
||||||
"profile" to "staging",
|
"profile" to "staging",
|
||||||
),
|
),
|
||||||
capturedParams,
|
capturedParams,
|
||||||
@@ -222,7 +225,7 @@ class PluginManageRepositoryTest {
|
|||||||
fun `saveSettings throws SerializationException when response is malformed`() =
|
fun `saveSettings throws SerializationException when response is malformed`() =
|
||||||
runTest {
|
runTest {
|
||||||
val repository =
|
val repository =
|
||||||
PluginManageRepository { _, _ ->
|
pluginRepo { _, _ ->
|
||||||
OkHttpProvider.json.parseToJsonElement("{\"ok\": \"not-a-boolean\"}")
|
OkHttpProvider.json.parseToJsonElement("{\"ok\": \"not-a-boolean\"}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,16 @@ import org.junit.Assert.assertTrue
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class PluginRemovalRepositoryTest {
|
class PluginRemovalRepositoryTest {
|
||||||
|
private fun removalRepo(handler: suspend (String, Map<String, Any>) -> Any?) =
|
||||||
|
PluginRemovalRepository(fakeCaller(handler))
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `remove sends scoped gateway action and decodes success`() =
|
fun `remove sends scoped gateway action and decodes success`() =
|
||||||
runTest {
|
runTest {
|
||||||
var method = ""
|
var method = ""
|
||||||
var params: Map<String, Any> = emptyMap()
|
var params: Map<String, Any> = emptyMap()
|
||||||
val repository =
|
val repository =
|
||||||
PluginRemovalRepository { requestedMethod, requestedParams ->
|
removalRepo { requestedMethod, requestedParams ->
|
||||||
method = requestedMethod
|
method = requestedMethod
|
||||||
params = requestedParams
|
params = requestedParams
|
||||||
Json.parseToJsonElement("""{"ok":true,"name":"demo"}""")
|
Json.parseToJsonElement("""{"ok":true,"name":"demo"}""")
|
||||||
@@ -33,11 +36,11 @@ class PluginRemovalRepositoryTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `refusal is a typed failure and missing ok cannot masquerade as success`() =
|
fun `refusal is a typed failure and missing ok cannot masquerade as success`() =
|
||||||
runTest {
|
runTest {
|
||||||
val refused = PluginRemovalRepository { _, _ -> mapOf("ok" to false, "error" to "bundled plugin") }
|
val refused = removalRepo { _, _ -> mapOf("ok" to false, "error" to "bundled plugin") }
|
||||||
assertFalse(refused.remove("builtin").ok)
|
assertFalse(refused.remove("builtin").ok)
|
||||||
assertEquals("bundled plugin", refused.remove("builtin").error)
|
assertEquals("bundled plugin", refused.remove("builtin").error)
|
||||||
|
|
||||||
val malformed = PluginRemovalRepository { _, _ -> mapOf("name" to "demo") }
|
val malformed = removalRepo { _, _ -> mapOf("name" to "demo") }
|
||||||
try {
|
try {
|
||||||
malformed.remove("demo")
|
malformed.remove("demo")
|
||||||
throw AssertionError("Missing ok must fail decoding")
|
throw AssertionError("Missing ok must fail decoding")
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ import org.junit.Assert.assertTrue
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class ProjectsSourceTest {
|
class ProjectsSourceTest {
|
||||||
|
private fun projectsSource(
|
||||||
|
connected: Boolean = true,
|
||||||
|
handler: suspend (String, Map<String, Any>) -> Any?,
|
||||||
|
) = HermesProjectsSource(isConnected = { connected }, caller = fakeCaller(handler))
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `decodes the untyped rpc map`() =
|
fun `decodes the untyped rpc map`() =
|
||||||
runTest {
|
runTest {
|
||||||
var calledMethod: String? = null
|
var calledMethod: String? = null
|
||||||
val source =
|
val source =
|
||||||
HermesProjectsSource(isConnected = { true }) { method, _ ->
|
projectsSource { method, _ ->
|
||||||
calledMethod = method
|
calledMethod = method
|
||||||
mapOf(
|
mapOf(
|
||||||
"projects" to
|
"projects" to
|
||||||
@@ -49,13 +54,10 @@ class ProjectsSourceTest {
|
|||||||
runTest {
|
runTest {
|
||||||
var calls = 0
|
var calls = 0
|
||||||
val source =
|
val source =
|
||||||
HermesProjectsSource(
|
projectsSource(connected = false) { _, _ ->
|
||||||
rpcRequest = { _, _ ->
|
calls++
|
||||||
calls++
|
mapOf("projects" to emptyList<Any>())
|
||||||
mapOf("projects" to emptyList<Any>())
|
}
|
||||||
},
|
|
||||||
isConnected = { false },
|
|
||||||
)
|
|
||||||
|
|
||||||
assertNull(source.fetchProjects())
|
assertNull(source.fetchProjects())
|
||||||
assertEquals("a disconnected fetch must not queue an RPC", 0, calls)
|
assertEquals("a disconnected fetch must not queue an RPC", 0, calls)
|
||||||
@@ -65,10 +67,10 @@ class ProjectsSourceTest {
|
|||||||
fun `rpc failure or unexpected payload yields null`() =
|
fun `rpc failure or unexpected payload yields null`() =
|
||||||
runTest {
|
runTest {
|
||||||
assertNull(
|
assertNull(
|
||||||
HermesProjectsSource(isConnected = { true }) { _, _ -> error("method not found") }.fetchProjects(),
|
projectsSource { _, _ -> error("method not found") }.fetchProjects(),
|
||||||
)
|
)
|
||||||
assertNull(HermesProjectsSource(isConnected = { true }) { _, _ -> "nope" }.fetchProjects())
|
assertNull(projectsSource { _, _ -> "nope" }.fetchProjects())
|
||||||
assertNull(HermesProjectsSource(isConnected = { true }) { _, _ -> null }.fetchProjects())
|
assertNull(projectsSource { _, _ -> null }.fetchProjects())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -76,9 +78,7 @@ class ProjectsSourceTest {
|
|||||||
runTest {
|
runTest {
|
||||||
val thrown =
|
val thrown =
|
||||||
runCatching {
|
runCatching {
|
||||||
HermesProjectsSource(
|
projectsSource { _, _ -> throw CancellationException("gone") }.fetchProjects()
|
||||||
isConnected = { true },
|
|
||||||
) { _, _ -> throw CancellationException("gone") }.fetchProjects()
|
|
||||||
}.exceptionOrNull()
|
}.exceptionOrNull()
|
||||||
assertTrue(thrown is CancellationException)
|
assertTrue(thrown is CancellationException)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,10 +318,6 @@ class RawRpcParamsRatchetTest {
|
|||||||
listOf(
|
listOf(
|
||||||
"data/ws/HermesWsClient.kt::CLIENT_CAPABILITIES",
|
"data/ws/HermesWsClient.kt::CLIENT_CAPABILITIES",
|
||||||
"data/ws/ModelOptionsRepository.kt::MODEL_OPTIONS",
|
"data/ws/ModelOptionsRepository.kt::MODEL_OPTIONS",
|
||||||
"data/ws/PluginManageRepository.kt::PLUGINS_MANAGE",
|
|
||||||
"data/ws/PluginManageRepository.kt::PLUGINS_MANAGE",
|
|
||||||
"data/ws/PluginRemovalRepository.kt::PLUGINS_MANAGE",
|
|
||||||
"data/ws/ProjectsSource.kt::PROJECTS_LIST",
|
|
||||||
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
|
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
|
||||||
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
|
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
|
||||||
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
|
"ui/bots/BotsViewModel.kt::PROFILES_CONFIGURE",
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.m57.hermescontrol.data.ws.contract
|
||||||
|
|
||||||
|
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
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 PluginsProjectsTypesTest {
|
||||||
|
private fun encode(params: PluginsManageParams) =
|
||||||
|
OkHttpProvider.json.encodeToJsonElement(PluginsManageParams.serializer(), params)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun listEncodesOnlyAction() {
|
||||||
|
assertEquals(buildJsonObject { put("action", "list") }, encode(PluginsManageParams(action = "list")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeEncodesActionAndName() {
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject {
|
||||||
|
put("action", "remove")
|
||||||
|
put("name", "demo")
|
||||||
|
},
|
||||||
|
encode(PluginsManageParams(action = "remove", name = "demo")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun settingsEncodesKeyValuesAndExplicitProfile() {
|
||||||
|
val values = buildJsonObject { put("port", JsonPrimitive(8080)) }
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject {
|
||||||
|
put("action", "settings")
|
||||||
|
put("profile", "staging")
|
||||||
|
put("key", "camofox")
|
||||||
|
put("values", values)
|
||||||
|
},
|
||||||
|
encode(PluginsManageParams(action = "settings", key = "camofox", values = values, profile = "staging")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun projectsListEncodesAsEmptyObject() {
|
||||||
|
assertEquals(
|
||||||
|
buildJsonObject { },
|
||||||
|
OkHttpProvider.json.encodeToJsonElement(ProjectsListParams.serializer(), ProjectsListParams),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun descriptorsAreRegisteredWithContractNames() {
|
||||||
|
assertTrue(RpcMethods.PLUGINS_MANAGE in RpcMethods.all)
|
||||||
|
assertTrue(RpcMethods.PROJECTS_LIST in RpcMethods.all)
|
||||||
|
assertEquals("plugins.manage", RpcMethods.PLUGINS_MANAGE.name)
|
||||||
|
assertEquals("projects.list", RpcMethods.PROJECTS_LIST.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user