Skip to content

Commit 6da63c4

Browse files
committed
test(acp-nats): cover TerminalKillFailingClient and jsonrpc_error_response for coverage gate
1 parent 492d917 commit 6da63c4

1 file changed

Lines changed: 148 additions & 9 deletions

File tree

  • rsworkspace/crates/acp-nats/src/client

rsworkspace/crates/acp-nats/src/client/mod.rs

Lines changed: 148 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,9 @@ fn jsonrpc_error_response(
3737
"message": message
3838
}
3939
});
40-
serde_json::to_vec(&response).unwrap_or_else(|e| {
41-
format!(
42-
"{{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{{\"code\":{},\"message\":\"failed to serialize error response: {}\"}}}}",
43-
i32::from(code),
44-
e
45-
)
46-
.into_bytes()
47-
})
48-
.into()
40+
serde_json::to_vec(&response)
41+
.expect("serializing JSON-RPC error response should not fail")
42+
.into()
4943
}
5044

5145
async fn publish_backpressure_error_reply<N: PublishClient + FlushClient, S: JsonSerialize>(
@@ -301,6 +295,7 @@ mod tests {
301295
KillTerminalCommandRequest, KillTerminalCommandResponse, ReadTextFileRequest,
302296
ReadTextFileResponse, Request, RequestId, RequestPermissionOutcome,
303297
RequestPermissionRequest, RequestPermissionResponse, SessionNotification, SessionUpdate,
298+
ToolCallUpdate, ToolCallUpdateFields,
304299
};
305300
use async_trait::async_trait;
306301
use std::cell::RefCell;
@@ -849,6 +844,150 @@ mod tests {
849844
assert_eq!(nats.published_messages(), vec!["_INBOX.reply"]);
850845
}
851846

847+
#[tokio::test]
848+
async fn dispatch_client_method_dispatches_session_update_with_terminal_kill_failing_client() {
849+
let nats = MockNatsClient::new();
850+
let client = TerminalKillFailingClient;
851+
let session_id = AcpSessionId::new("sess-1").unwrap();
852+
853+
let notification = SessionNotification::new(
854+
"sess-1",
855+
SessionUpdate::AgentMessageChunk(ContentChunk::new(ContentBlock::from("hi"))),
856+
);
857+
let payload = bytes::Bytes::from(serde_json::to_vec(&notification).unwrap());
858+
859+
let parsed = crate::nats::ParsedClientSubject {
860+
session_id,
861+
method: ClientMethod::SessionUpdate,
862+
};
863+
864+
let ctx = DispatchContext {
865+
nats: &nats,
866+
client: &client,
867+
serializer: &StdJsonSerialize,
868+
};
869+
dispatch_client_method(
870+
"acp.sess-1.client.session.update",
871+
parsed,
872+
payload,
873+
None,
874+
&ctx,
875+
)
876+
.await;
877+
}
878+
879+
#[tokio::test]
880+
async fn dispatch_client_method_dispatches_fs_read_text_file_with_terminal_kill_failing_client()
881+
{
882+
let nats = MockNatsClient::new();
883+
let client = TerminalKillFailingClient;
884+
let session_id = AcpSessionId::new("sess-1").unwrap();
885+
886+
let envelope = Request {
887+
id: RequestId::Number(1),
888+
method: std::sync::Arc::from("fs/read_text_file"),
889+
params: Some(ReadTextFileRequest::new(
890+
agent_client_protocol::SessionId::from("sess-1"),
891+
"/tmp/foo.txt".to_string(),
892+
)),
893+
};
894+
let payload = bytes::Bytes::from(serde_json::to_vec(&envelope).unwrap());
895+
896+
let parsed = crate::nats::ParsedClientSubject {
897+
session_id,
898+
method: ClientMethod::FsReadTextFile,
899+
};
900+
901+
let ctx = DispatchContext {
902+
nats: &nats,
903+
client: &client,
904+
serializer: &StdJsonSerialize,
905+
};
906+
dispatch_client_method(
907+
"acp.sess-1.client.fs.read_text_file",
908+
parsed,
909+
payload,
910+
Some("_INBOX.reply".to_string()),
911+
&ctx,
912+
)
913+
.await;
914+
915+
assert_eq!(nats.published_messages(), vec!["_INBOX.reply"]);
916+
}
917+
918+
#[tokio::test]
919+
async fn dispatch_client_method_dispatches_terminal_create_with_terminal_kill_failing_client() {
920+
let nats = MockNatsClient::new();
921+
let client = TerminalKillFailingClient;
922+
let session_id = AcpSessionId::new("sess-1").unwrap();
923+
924+
let envelope = Request {
925+
id: RequestId::Number(1),
926+
method: std::sync::Arc::from("terminal/create"),
927+
params: Some(CreateTerminalRequest::new("sess-1", "echo hi")),
928+
};
929+
let payload = bytes::Bytes::from(serde_json::to_vec(&envelope).unwrap());
930+
931+
let parsed = crate::nats::ParsedClientSubject {
932+
session_id,
933+
method: ClientMethod::TerminalCreate,
934+
};
935+
936+
let ctx = DispatchContext {
937+
nats: &nats,
938+
client: &client,
939+
serializer: &StdJsonSerialize,
940+
};
941+
dispatch_client_method(
942+
"acp.sess-1.client.terminal.create",
943+
parsed,
944+
payload,
945+
Some("_INBOX.reply".to_string()),
946+
&ctx,
947+
)
948+
.await;
949+
950+
assert_eq!(nats.published_messages(), vec!["_INBOX.reply"]);
951+
}
952+
953+
#[tokio::test]
954+
async fn dispatch_client_method_dispatches_request_permission_with_terminal_kill_failing_client()
955+
{
956+
let nats = MockNatsClient::new();
957+
let client = TerminalKillFailingClient;
958+
let session_id = AcpSessionId::new("sess-1").unwrap();
959+
960+
let tool_call = ToolCallUpdate::new("call-1", ToolCallUpdateFields::new());
961+
let request = RequestPermissionRequest::new("sess-1", tool_call, vec![]);
962+
let envelope = Request {
963+
id: RequestId::Number(1),
964+
method: std::sync::Arc::from("session/request_permission"),
965+
params: Some(request),
966+
};
967+
let payload = bytes::Bytes::from(serde_json::to_vec(&envelope).unwrap());
968+
969+
let parsed = crate::nats::ParsedClientSubject {
970+
session_id,
971+
method: ClientMethod::SessionRequestPermission,
972+
};
973+
974+
let ctx = DispatchContext {
975+
nats: &nats,
976+
client: &client,
977+
serializer: &StdJsonSerialize,
978+
};
979+
dispatch_client_method(
980+
"acp.sess-1.client.session.request_permission",
981+
parsed,
982+
payload,
983+
Some("_INBOX.reply".to_string()),
984+
&ctx,
985+
)
986+
.await;
987+
988+
assert_eq!(nats.published_messages(), vec!["_INBOX.reply"]);
989+
}
990+
852991
#[tokio::test]
853992
async fn dispatch_client_method_dispatches_fs_read_text_file_with_advanced_mock() {
854993
let nats = AdvancedMockNatsClient::new();

0 commit comments

Comments
 (0)