12 files changed
+1168
-125
No files matched your search
@@ -3,6 +3,10 @@ package com.m57.hermescontrol.data.ws
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import com.m57.hermescontrol.data.model.AccountConnectorResult
|
||||
import com.m57.hermescontrol.data.model.ConnectorTool
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionAnswer
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionRespondParams
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectorOwner
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectorsOperationStatusParams
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkAll
|
||||
@@ -62,14 +66,19 @@ class AccountConnectorRepositoryTest {
|
||||
operationFixture()
|
||||
}
|
||||
repo.operationStatus("op-a")
|
||||
repo.operationRequest(
|
||||
WsMethods.CONNECTION_RESPOND,
|
||||
mapOf(
|
||||
"op_id" to "op-a",
|
||||
"result" to mapOf("settled_by" to "continue"),
|
||||
repo.operationRespond(
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.account(),
|
||||
opId = "op-a",
|
||||
result = ConnectionAnswer(settledBy = "continue"),
|
||||
),
|
||||
)
|
||||
repo.operationWake(
|
||||
ConnectorsOperationStatusParams(
|
||||
owner = ConnectorOwner.account(),
|
||||
opId = "op-a",
|
||||
),
|
||||
)
|
||||
repo.operationRequest(WsMethods.CONNECTORS_OPERATION_WAKE, mapOf("op_id" to "op-a"))
|
||||
assertEquals(
|
||||
listOf(
|
||||
WsMethods.CONNECTORS_OPERATION_STATUS,
|
||||
@@ -86,6 +95,23 @@ class AccountConnectorRepositoryTest {
|
||||
assertEquals("op-a", params["op_id"])
|
||||
assertFalse("session_id" in params)
|
||||
}
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"owner" to mapOf("type" to "account"),
|
||||
"op_id" to "op-a",
|
||||
"result" to mapOf("settled_by" to "continue"),
|
||||
"profile" to "work",
|
||||
),
|
||||
calls[1].second,
|
||||
)
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"owner" to mapOf("type" to "account"),
|
||||
"op_id" to "op-a",
|
||||
"profile" to "work",
|
||||
),
|
||||
calls[2].second,
|
||||
)
|
||||
}
|
||||
|
||||
@Test fun `catalog accounts tools and removal use native account endpoints`() =
|
||||
@@ -213,7 +239,7 @@ class AccountConnectorRepositoryTest {
|
||||
}
|
||||
|
||||
private fun accountRepo(handler: suspend (String, Map<String, Any>) -> Any?) =
|
||||
HermesAccountConnectorRepository(rpc = handler, caller = fakeCaller(handler))
|
||||
HermesAccountConnectorRepository(caller = fakeCaller(handler))
|
||||
|
||||
private fun operationFixture() =
|
||||
mapOf(
|
||||
|
||||
+562
@@ -0,0 +1,562 @@
|
||||
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.ConnectionAnswer
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionAnswerTarget
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionRespondParams
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectorOwner
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectorsOperationStatusParams
|
||||
import com.m57.hermescontrol.data.ws.contract.HermesRpcCaller
|
||||
import com.m57.hermescontrol.data.ws.contract.RpcMethods
|
||||
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.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
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.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 HermesWsClientConnectionOperationTest {
|
||||
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 {
|
||||
withTimeoutOrNull(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 typedCallConnectionRespondWithApprovedEnvAndAccountOwnerEncodesFrameAndReturnsRawServerJsonUntouched() {
|
||||
val opened = CountDownLatch(1)
|
||||
val captured = CountDownLatch(1)
|
||||
val frameRef = AtomicReference<JsonObject?>(null)
|
||||
|
||||
val serverResultJson =
|
||||
buildJsonObject {
|
||||
put("status", "ok")
|
||||
put("settled", true)
|
||||
}
|
||||
|
||||
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 == "connection.respond") {
|
||||
frameRef.set(json)
|
||||
captured.countDown()
|
||||
val id = (json["id"] as? JsonPrimitive)?.content ?: "1"
|
||||
webSocket.send(
|
||||
"""{"jsonrpc":"2.0","id":"$id","result":""" + serverResultJson + """}""",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
connectClient()
|
||||
assertTrue(opened.await(5, TimeUnit.SECONDS))
|
||||
|
||||
val result: JsonElement =
|
||||
runBlocking {
|
||||
HermesWsClient.call(
|
||||
RpcMethods.CONNECTION_RESPOND,
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.account(),
|
||||
opId = "op-approved-123",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets =
|
||||
listOf(
|
||||
ConnectionAnswerTarget(
|
||||
name = "github",
|
||||
status = "approved",
|
||||
detail = "OAuth granted",
|
||||
env = mapOf("TOKEN" to "dummy_secret_token_abc"),
|
||||
),
|
||||
),
|
||||
settledBy = null,
|
||||
),
|
||||
profile = "prod",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
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("connection.respond", (frame["method"] as? JsonPrimitive)?.content)
|
||||
|
||||
val params = frame["params"] as? JsonObject
|
||||
val expectedParams =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "account")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-approved-123")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put(
|
||||
"targets",
|
||||
buildJsonArray {
|
||||
add(
|
||||
buildJsonObject {
|
||||
put("name", "github")
|
||||
put("status", "approved")
|
||||
put("detail", "OAuth granted")
|
||||
put(
|
||||
"env",
|
||||
buildJsonObject {
|
||||
put("TOKEN", "dummy_secret_token_abc")
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
put("profile", "prod")
|
||||
}
|
||||
assertEquals(expectedParams, params)
|
||||
assertEquals(serverResultJson, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun typedCallConnectionRespondWithSkippedOmittedEnvAndSessionOwnerEncodesFrameAndReturnsRawServerJsonUntouched() {
|
||||
val opened = CountDownLatch(1)
|
||||
val captured = CountDownLatch(1)
|
||||
val frameRef = AtomicReference<JsonObject?>(null)
|
||||
|
||||
val serverResultJson =
|
||||
buildJsonObject {
|
||||
put("status", "ok")
|
||||
put("settled", false)
|
||||
}
|
||||
|
||||
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 == "connection.respond") {
|
||||
frameRef.set(json)
|
||||
captured.countDown()
|
||||
val id = (json["id"] as? JsonPrimitive)?.content ?: "1"
|
||||
webSocket.send(
|
||||
"""{"jsonrpc":"2.0","id":"$id","result":""" + serverResultJson + """}""",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
connectClient()
|
||||
assertTrue(opened.await(5, TimeUnit.SECONDS))
|
||||
|
||||
val result: JsonElement =
|
||||
runBlocking {
|
||||
HermesWsClient.call(
|
||||
RpcMethods.CONNECTION_RESPOND,
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.session("sess-skip-456"),
|
||||
opId = "op-skip-456",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets =
|
||||
listOf(
|
||||
ConnectionAnswerTarget(
|
||||
name = "slack",
|
||||
status = "skipped",
|
||||
detail = null,
|
||||
env = null,
|
||||
),
|
||||
),
|
||||
settledBy = null,
|
||||
),
|
||||
profile = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
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("connection.respond", (frame["method"] as? JsonPrimitive)?.content)
|
||||
|
||||
val params = frame["params"] as? JsonObject
|
||||
val expectedParams =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "sess-skip-456")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-skip-456")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put(
|
||||
"targets",
|
||||
buildJsonArray {
|
||||
add(
|
||||
buildJsonObject {
|
||||
put("name", "slack")
|
||||
put("status", "skipped")
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
assertEquals(expectedParams, params)
|
||||
assertFalse(params?.containsKey("profile") ?: true)
|
||||
assertEquals(serverResultJson, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun typedCallConnectionRespondWithContinueAndOmittedTargetsEncodesFrameAndReturnsRawServerJsonUntouched() {
|
||||
val opened = CountDownLatch(1)
|
||||
val captured = CountDownLatch(1)
|
||||
val frameRef = AtomicReference<JsonObject?>(null)
|
||||
|
||||
val serverResultJson =
|
||||
buildJsonObject {
|
||||
put("status", "ok")
|
||||
put("settled", true)
|
||||
}
|
||||
|
||||
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 == "connection.respond") {
|
||||
frameRef.set(json)
|
||||
captured.countDown()
|
||||
val id = (json["id"] as? JsonPrimitive)?.content ?: "1"
|
||||
webSocket.send(
|
||||
"""{"jsonrpc":"2.0","id":"$id","result":""" + serverResultJson + """}""",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
connectClient()
|
||||
assertTrue(opened.await(5, TimeUnit.SECONDS))
|
||||
|
||||
val result: JsonElement =
|
||||
runBlocking {
|
||||
HermesWsClient.call(
|
||||
RpcMethods.CONNECTION_RESPOND,
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.session("sess-continue-789"),
|
||||
opId = "op-continue-789",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets = null,
|
||||
settledBy = "continue",
|
||||
),
|
||||
profile = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
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("connection.respond", (frame["method"] as? JsonPrimitive)?.content)
|
||||
|
||||
val params = frame["params"] as? JsonObject
|
||||
val expectedParams =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "sess-continue-789")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-continue-789")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put("settled_by", "continue")
|
||||
},
|
||||
)
|
||||
}
|
||||
assertEquals(expectedParams, params)
|
||||
assertFalse(params?.containsKey("profile") ?: true)
|
||||
assertEquals(serverResultJson, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun typedCallConnectorsOperationWakeEncodesOwnerSessionOpIdAndProfileAndReturnsRawServerJsonUntouched() {
|
||||
val opened = CountDownLatch(1)
|
||||
val captured = CountDownLatch(1)
|
||||
val frameRef = AtomicReference<JsonObject?>(null)
|
||||
|
||||
val serverResultJson =
|
||||
buildJsonObject {
|
||||
put("op_id", "op-wake-777")
|
||||
put("status", "running")
|
||||
}
|
||||
|
||||
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 == "connectors.operation.wake") {
|
||||
frameRef.set(json)
|
||||
captured.countDown()
|
||||
val id = (json["id"] as? JsonPrimitive)?.content ?: "1"
|
||||
webSocket.send(
|
||||
"""{"jsonrpc":"2.0","id":"$id","result":""" + serverResultJson + """}""",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
connectClient()
|
||||
assertTrue(opened.await(5, TimeUnit.SECONDS))
|
||||
|
||||
val result: JsonElement =
|
||||
runBlocking {
|
||||
HermesWsClient.call(
|
||||
RpcMethods.CONNECTORS_OPERATION_WAKE,
|
||||
ConnectorsOperationStatusParams(
|
||||
owner = ConnectorOwner.session("sess-wake-99"),
|
||||
opId = "op-wake-777",
|
||||
profile = "custom-profile",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
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("connectors.operation.wake", (frame["method"] as? JsonPrimitive)?.content)
|
||||
|
||||
val params = frame["params"] as? JsonObject
|
||||
val expectedParams =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "sess-wake-99")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-wake-777")
|
||||
put("profile", "custom-profile")
|
||||
}
|
||||
assertEquals(expectedParams, params)
|
||||
assertEquals(serverResultJson, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hermesRpcCallerCallDelegatesForWakeWithAccountOwnerAndNullProfile() {
|
||||
val opened = CountDownLatch(1)
|
||||
val captured = CountDownLatch(1)
|
||||
val frameRef = AtomicReference<JsonObject?>(null)
|
||||
|
||||
val serverResultJson =
|
||||
buildJsonObject {
|
||||
put("op_id", "op-wake-888")
|
||||
put("status", "done")
|
||||
}
|
||||
|
||||
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 == "connectors.operation.wake") {
|
||||
frameRef.set(json)
|
||||
captured.countDown()
|
||||
val id = (json["id"] as? JsonPrimitive)?.content ?: "1"
|
||||
webSocket.send(
|
||||
"""{"jsonrpc":"2.0","id":"$id","result":""" + serverResultJson + """}""",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
connectClient()
|
||||
assertTrue(opened.await(5, TimeUnit.SECONDS))
|
||||
|
||||
val result: JsonElement =
|
||||
runBlocking {
|
||||
HermesRpcCaller.call(
|
||||
RpcMethods.CONNECTORS_OPERATION_WAKE,
|
||||
ConnectorsOperationStatusParams(
|
||||
owner = ConnectorOwner.account(),
|
||||
opId = "op-wake-888",
|
||||
profile = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
assertTrue("Frame must reach the server", captured.await(5, TimeUnit.SECONDS))
|
||||
val frame = requireNotNull(frameRef.get())
|
||||
|
||||
assertEquals("connectors.operation.wake", (frame["method"] as? JsonPrimitive)?.content)
|
||||
|
||||
val params = frame["params"] as? JsonObject
|
||||
val expectedParams =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "account")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-wake-888")
|
||||
}
|
||||
assertEquals(expectedParams, params)
|
||||
assertFalse(params?.containsKey("profile") ?: true)
|
||||
assertEquals(serverResultJson, result)
|
||||
}
|
||||
}
|
||||
@@ -332,9 +332,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/chat/ChatConnectionOperationDelegate.kt::CONNECTION_RESPOND",
|
||||
"ui/chat/ChatConnectionOperationDelegate.kt::CONNECTION_RESPOND",
|
||||
"ui/chat/ChatConnectionOperationDelegate.kt::CONNECTORS_OPERATION_WAKE",
|
||||
"ui/chat/ChatModelSwitchDelegate.kt::CONFIG_SET",
|
||||
"ui/chat/ChatModelSwitchDelegate.kt::CONFIG_SET",
|
||||
"ui/chat/ChatModelSwitchDelegate.kt::CONFIG_SET",
|
||||
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
package com.m57.hermescontrol.data.ws.contract
|
||||
|
||||
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class ConnectionOperationTypesTest {
|
||||
@Test
|
||||
fun connectionRespondParamsWithApprovedEnvAndAccountOwnerEncodesExactWireJson() {
|
||||
val params =
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.account(),
|
||||
opId = "op-approved-1",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets =
|
||||
listOf(
|
||||
ConnectionAnswerTarget(
|
||||
name = "linear",
|
||||
status = "approved",
|
||||
detail = "User accepted linear integration",
|
||||
env = mapOf("API_KEY" to "dummy_secret_key_123"),
|
||||
),
|
||||
),
|
||||
settledBy = null,
|
||||
),
|
||||
profile = "work",
|
||||
)
|
||||
val encoded = OkHttpProvider.json.encodeToJsonElement(ConnectionRespondParams.serializer(), params)
|
||||
|
||||
val expected =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "account")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-approved-1")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put(
|
||||
"targets",
|
||||
buildJsonArray {
|
||||
add(
|
||||
buildJsonObject {
|
||||
put("name", "linear")
|
||||
put("status", "approved")
|
||||
put("detail", "User accepted linear integration")
|
||||
put(
|
||||
"env",
|
||||
buildJsonObject {
|
||||
put("API_KEY", "dummy_secret_key_123")
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
put("profile", "work")
|
||||
}
|
||||
|
||||
assertEquals(expected, encoded)
|
||||
assertTrue(encoded is JsonObject)
|
||||
assertEquals(setOf("owner", "op_id", "result", "profile"), (encoded as JsonObject).keys)
|
||||
val resultObj = (encoded as JsonObject)["result"] as JsonObject
|
||||
assertEquals(setOf("targets"), resultObj.keys)
|
||||
assertFalse(resultObj.containsKey("settled_by"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionRespondParamsWithSkippedOmittedEnvAndSessionOwnerEncodesExactWireJson() {
|
||||
val params =
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.session("sess-xyz"),
|
||||
opId = "op-skip-2",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets =
|
||||
listOf(
|
||||
ConnectionAnswerTarget(
|
||||
name = "github",
|
||||
status = "skipped",
|
||||
detail = null,
|
||||
env = null,
|
||||
),
|
||||
),
|
||||
settledBy = null,
|
||||
),
|
||||
profile = null,
|
||||
)
|
||||
val encoded = OkHttpProvider.json.encodeToJsonElement(ConnectionRespondParams.serializer(), params)
|
||||
|
||||
val expected =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "sess-xyz")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-skip-2")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put(
|
||||
"targets",
|
||||
buildJsonArray {
|
||||
add(
|
||||
buildJsonObject {
|
||||
put("name", "github")
|
||||
put("status", "skipped")
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(expected, encoded)
|
||||
assertTrue(encoded is JsonObject)
|
||||
assertEquals(setOf("owner", "op_id", "result"), (encoded as JsonObject).keys)
|
||||
assertFalse((encoded as JsonObject).containsKey("profile"))
|
||||
|
||||
val resultObj = (encoded as JsonObject)["result"] as JsonObject
|
||||
assertEquals(setOf("targets"), resultObj.keys)
|
||||
assertFalse(resultObj.containsKey("settled_by"))
|
||||
|
||||
val targetObj = (resultObj["targets"] as kotlinx.serialization.json.JsonArray)[0] as JsonObject
|
||||
assertEquals(setOf("name", "status"), targetObj.keys)
|
||||
assertFalse(targetObj.containsKey("env"))
|
||||
assertFalse(targetObj.containsKey("detail"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionRespondParamsWithContinueAndOmittedTargetsEncodesExactWireJson() {
|
||||
val params =
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.session("sess-cont-3"),
|
||||
opId = "op-cont-3",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets = null,
|
||||
settledBy = "continue",
|
||||
),
|
||||
profile = null,
|
||||
)
|
||||
val encoded = OkHttpProvider.json.encodeToJsonElement(ConnectionRespondParams.serializer(), params)
|
||||
|
||||
val expected =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "sess-cont-3")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-cont-3")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put("settled_by", "continue")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(expected, encoded)
|
||||
assertTrue(encoded is JsonObject)
|
||||
assertEquals(setOf("owner", "op_id", "result"), (encoded as JsonObject).keys)
|
||||
assertFalse((encoded as JsonObject).containsKey("profile"))
|
||||
|
||||
val resultObj = (encoded as JsonObject)["result"] as JsonObject
|
||||
assertEquals(setOf("settled_by"), resultObj.keys)
|
||||
assertFalse(resultObj.containsKey("targets"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectorsOperationWakeUsesConnectorsOperationStatusParamsAndEncodesAllFieldsWhenProfilePresent() {
|
||||
val params =
|
||||
ConnectorsOperationStatusParams(
|
||||
owner = ConnectorOwner.session("sess-wake-1"),
|
||||
opId = "op-wake-1",
|
||||
profile = "staging",
|
||||
)
|
||||
val encoded = OkHttpProvider.json.encodeToJsonElement(ConnectorsOperationStatusParams.serializer(), params)
|
||||
|
||||
val expected =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "sess-wake-1")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-wake-1")
|
||||
put("profile", "staging")
|
||||
}
|
||||
|
||||
assertEquals(expected, encoded)
|
||||
assertTrue(encoded is JsonObject)
|
||||
assertEquals(setOf("owner", "op_id", "profile"), (encoded as JsonObject).keys)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectorsOperationWakeUsesConnectorsOperationStatusParamsAndOmitsProfileWhenNull() {
|
||||
val params =
|
||||
ConnectorsOperationStatusParams(
|
||||
owner = ConnectorOwner.account(),
|
||||
opId = "op-wake-2",
|
||||
profile = null,
|
||||
)
|
||||
val encoded = OkHttpProvider.json.encodeToJsonElement(ConnectorsOperationStatusParams.serializer(), params)
|
||||
|
||||
val expected =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "account")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-wake-2")
|
||||
}
|
||||
|
||||
assertEquals(expected, encoded)
|
||||
assertTrue(encoded is JsonObject)
|
||||
assertEquals(setOf("owner", "op_id"), (encoded as JsonObject).keys)
|
||||
assertFalse((encoded as JsonObject).containsKey("profile"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun connectionRespondAndWakeAreRegisteredInGatewayContractRpcMethodsAll() {
|
||||
val allMethods = RpcMethods.all
|
||||
val respondMethod = allMethods.firstOrNull { it.name == "connection.respond" }
|
||||
val wakeMethod = allMethods.firstOrNull { it.name == "connectors.operation.wake" }
|
||||
|
||||
org.junit.Assert.assertNotNull("connection.respond must be registered in RpcMethods.all", respondMethod)
|
||||
org.junit.Assert.assertNotNull("connectors.operation.wake must be registered in RpcMethods.all", wakeMethod)
|
||||
|
||||
assertEquals(RpcMethods.CONNECTION_RESPOND, respondMethod)
|
||||
assertEquals(RpcMethods.CONNECTORS_OPERATION_WAKE, wakeMethod)
|
||||
}
|
||||
}
|
||||
+172
-36
@@ -6,8 +6,13 @@ import com.m57.hermescontrol.data.model.ConnectionOperationTarget
|
||||
import com.m57.hermescontrol.data.model.ConnectionTargetAction
|
||||
import com.m57.hermescontrol.data.model.ConnectionTargetKind
|
||||
import com.m57.hermescontrol.data.model.ConnectionTargetState
|
||||
import com.m57.hermescontrol.data.remote.OkHttpProvider
|
||||
import com.m57.hermescontrol.data.ws.HermesWsClient
|
||||
import com.m57.hermescontrol.data.ws.WsMethods
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionAnswer
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionAnswerTarget
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionRespondParams
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectorOwner
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectorsOperationStatusParams
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -15,6 +20,9 @@ import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
@@ -109,19 +117,65 @@ class ChatConnectionOperationDelegateTest {
|
||||
)
|
||||
|
||||
assertEquals(1, requester.calls.size)
|
||||
val call = requester.calls.single()
|
||||
assertEquals(WsMethods.CONNECTION_RESPOND, call.first)
|
||||
assertEquals(setOf("owner", "op_id", "result"), call.second.keys)
|
||||
assertEquals(mapOf("type" to "session", "session_id" to "session-a"), call.second["owner"])
|
||||
assertEquals("op-a", call.second["op_id"])
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val result = call.second["result"] as Map<String, Any>
|
||||
val action = requester.calls.single()
|
||||
assertTrue(action is ConnectionOperationRequest.Respond)
|
||||
val respond = action as ConnectionOperationRequest.Respond
|
||||
|
||||
assertEquals(
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.session("session-a"),
|
||||
opId = "op-a",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets =
|
||||
listOf(
|
||||
ConnectionAnswerTarget(
|
||||
name = "github",
|
||||
status = "approved",
|
||||
env = mapOf("TOKEN" to canary),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
respond.params,
|
||||
)
|
||||
|
||||
val encoded = OkHttpProvider.json.encodeToJsonElement(ConnectionRespondParams.serializer(), respond.params)
|
||||
val expectedWire =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "session-a")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-a")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put(
|
||||
"targets",
|
||||
buildJsonArray {
|
||||
add(
|
||||
buildJsonObject {
|
||||
put("name", "github")
|
||||
put("status", "approved")
|
||||
put(
|
||||
"env",
|
||||
buildJsonObject {
|
||||
put("TOKEN", canary)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
assertEquals(expectedWire, encoded)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val answer = (result["targets"] as List<Map<String, Any>>).single()
|
||||
assertEquals("github", answer["name"])
|
||||
assertEquals("approved", answer["status"])
|
||||
assertEquals(mapOf("TOKEN" to canary), answer["env"])
|
||||
assertFalse(
|
||||
delegate.state.value
|
||||
.toString()
|
||||
@@ -166,12 +220,57 @@ class ChatConnectionOperationDelegateTest {
|
||||
skipDelegate.acceptRequest(snapshot())
|
||||
skipDelegate.respond("github", mapOf("TOKEN" to "must-drop"), approved = false)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val skipResult = skipRequester.calls.single().second["result"] as Map<String, Any>
|
||||
val skipAction = skipRequester.calls.single()
|
||||
assertTrue(skipAction is ConnectionOperationRequest.Respond)
|
||||
val skipRespond = skipAction as ConnectionOperationRequest.Respond
|
||||
assertEquals(
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.session("session-a"),
|
||||
opId = "op-a",
|
||||
result =
|
||||
ConnectionAnswer(
|
||||
targets =
|
||||
listOf(
|
||||
ConnectionAnswerTarget(
|
||||
name = "github",
|
||||
status = "skipped",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
skipRespond.params,
|
||||
)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val skipAnswer = (skipResult["targets"] as List<Map<String, Any>>).single()
|
||||
assertEquals(mapOf("name" to "github", "status" to "skipped"), skipAnswer)
|
||||
val encodedSkip =
|
||||
OkHttpProvider.json.encodeToJsonElement(ConnectionRespondParams.serializer(), skipRespond.params)
|
||||
val expectedSkipWire =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "session-a")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-a")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put(
|
||||
"targets",
|
||||
buildJsonArray {
|
||||
add(
|
||||
buildJsonObject {
|
||||
put("name", "github")
|
||||
put("status", "skipped")
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
assertEquals(expectedSkipWire, encodedSkip)
|
||||
|
||||
val continueRequester = RecordingRequester()
|
||||
val continueDelegate = delegate(continueRequester)
|
||||
@@ -179,14 +278,38 @@ class ChatConnectionOperationDelegateTest {
|
||||
continueDelegate.acceptRequest(snapshot())
|
||||
continueDelegate.continueOperation()
|
||||
|
||||
val continueAction = continueRequester.calls.single()
|
||||
assertTrue(continueAction is ConnectionOperationRequest.Respond)
|
||||
val continueRespond = continueAction as ConnectionOperationRequest.Respond
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"owner" to mapOf("type" to "session", "session_id" to "session-a"),
|
||||
"op_id" to "op-a",
|
||||
"result" to mapOf("settled_by" to "continue"),
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.session("session-a"),
|
||||
opId = "op-a",
|
||||
result = ConnectionAnswer(settledBy = "continue"),
|
||||
),
|
||||
continueRequester.calls.single().second,
|
||||
continueRespond.params,
|
||||
)
|
||||
|
||||
val encodedContinue =
|
||||
OkHttpProvider.json.encodeToJsonElement(ConnectionRespondParams.serializer(), continueRespond.params)
|
||||
val expectedContinueWire =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "session-a")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-a")
|
||||
put(
|
||||
"result",
|
||||
buildJsonObject {
|
||||
put("settled_by", "continue")
|
||||
},
|
||||
)
|
||||
}
|
||||
assertEquals(expectedContinueWire, encodedContinue)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -199,15 +322,31 @@ class ChatConnectionOperationDelegateTest {
|
||||
|
||||
delegate.wake("op-a")
|
||||
|
||||
val call = requester.calls.single()
|
||||
assertEquals(WsMethods.CONNECTORS_OPERATION_WAKE, call.first)
|
||||
val action = requester.calls.single()
|
||||
assertTrue(action is ConnectionOperationRequest.Wake)
|
||||
val wake = action as ConnectionOperationRequest.Wake
|
||||
assertEquals(
|
||||
mapOf(
|
||||
"owner" to mapOf("type" to "session", "session_id" to "session-a"),
|
||||
"op_id" to "op-a",
|
||||
ConnectorsOperationStatusParams(
|
||||
owner = ConnectorOwner.session("session-a"),
|
||||
opId = "op-a",
|
||||
),
|
||||
call.second,
|
||||
wake.params,
|
||||
)
|
||||
|
||||
val encoded =
|
||||
OkHttpProvider.json.encodeToJsonElement(ConnectorsOperationStatusParams.serializer(), wake.params)
|
||||
val expectedWire =
|
||||
buildJsonObject {
|
||||
put(
|
||||
"owner",
|
||||
buildJsonObject {
|
||||
put("type", "session")
|
||||
put("session_id", "session-a")
|
||||
},
|
||||
)
|
||||
put("op_id", "op-a")
|
||||
}
|
||||
assertEquals(expectedWire, encoded)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -265,7 +404,7 @@ class ChatConnectionOperationDelegateTest {
|
||||
runTest {
|
||||
val gate = CompletableDeferred<Unit>()
|
||||
val requester =
|
||||
ConnectionOperationRequester { _, _ ->
|
||||
ConnectionOperationRequester {
|
||||
gate.await()
|
||||
throw IllegalStateException("old failure")
|
||||
}
|
||||
@@ -355,13 +494,10 @@ class ChatConnectionOperationDelegateTest {
|
||||
private class RecordingRequester(
|
||||
private val failure: Throwable? = null,
|
||||
) : ConnectionOperationRequester {
|
||||
val calls = mutableListOf<Pair<String, Map<String, Any>>>()
|
||||
val calls = mutableListOf<ConnectionOperationRequest>()
|
||||
|
||||
override suspend fun request(
|
||||
method: String,
|
||||
params: Map<String, Any>,
|
||||
): Any? {
|
||||
calls += method to params
|
||||
override suspend fun request(action: ConnectionOperationRequest): Any? {
|
||||
calls += action
|
||||
failure?.let { throw it }
|
||||
return Unit
|
||||
}
|
||||
|
||||
+9
-8
@@ -10,7 +10,9 @@ import com.m57.hermescontrol.data.model.ConnectorPolicy
|
||||
import com.m57.hermescontrol.data.ws.AccountConnectorRepository
|
||||
import com.m57.hermescontrol.data.ws.ConnectionOperationParser
|
||||
import com.m57.hermescontrol.data.ws.WsEvent
|
||||
import com.m57.hermescontrol.data.ws.WsMethods
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionAnswer
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectionRespondParams
|
||||
import com.m57.hermescontrol.data.ws.contract.ConnectorOwner
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
@@ -143,7 +145,7 @@ class AccountConnectorsViewModelTest {
|
||||
runTest(dispatcher) {
|
||||
advanceUntilIdle()
|
||||
coEvery { repo.connect(any(), any()) } returns AccountConnectorResult.Success(snapshot())
|
||||
coEvery { repo.operationRequest(any(), any()) } returns mapOf("status" to "ok")
|
||||
coEvery { repo.operationRespond(any()) } returns mapOf("status" to "ok")
|
||||
coEvery { repo.operationStatus("op-a") } returns
|
||||
AccountConnectorResult.Success(snapshot().copy(seq = 2, settled = true))
|
||||
vm.connect("drive")
|
||||
@@ -151,12 +153,11 @@ class AccountConnectorsViewModelTest {
|
||||
vm.continueOperation()
|
||||
advanceUntilIdle()
|
||||
coVerify {
|
||||
repo.operationRequest(
|
||||
WsMethods.CONNECTION_RESPOND,
|
||||
mapOf(
|
||||
"owner" to mapOf("type" to "account"),
|
||||
"op_id" to "op-a",
|
||||
"result" to mapOf("settled_by" to "continue"),
|
||||
repo.operationRespond(
|
||||
ConnectionRespondParams(
|
||||
owner = ConnectorOwner.account(),
|
||||
opId = "op-a",
|
||||
result = ConnectionAnswer(settledBy = "continue"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user