Skip to content

Commit 2e94835

Browse files
committed
test(acp-nats): add coverage for client/mod.rs helpers and dispatcher
Add unit tests for the previously uncovered code in client/mod.rs: - jsonrpc_error_response: valid id, code, and message serialization - extract_request_id: valid id, missing id, and invalid JSON - handle_client_request: dispatches SessionUpdate to the handler These cover the private helpers and the dispatch path that cannot be reached through run() without a live NATS subscriber. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent 2b88854 commit 2e94835

File tree

1 file changed

+91
-4
lines changed
  • rsworkspace/crates/acp-nats/src/client

1 file changed

+91
-4
lines changed

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

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,35 @@ async fn handle_client_request<
192192
#[cfg(test)]
193193
mod tests {
194194
use super::*;
195-
use agent_client_protocol::{RequestPermissionRequest, RequestPermissionResponse};
195+
use crate::session_id::AcpSessionId;
196+
use agent_client_protocol::{
197+
ContentBlock, ContentChunk, RequestPermissionRequest, RequestPermissionResponse,
198+
SessionNotification, SessionUpdate,
199+
};
196200
use async_trait::async_trait;
201+
use std::cell::RefCell;
197202
use trogon_nats::MockNatsClient;
198203
use trogon_std::time::SystemClock;
199204

200-
struct MockClient;
205+
struct MockClient {
206+
notifications: RefCell<Vec<String>>,
207+
}
208+
209+
impl MockClient {
210+
fn new() -> Self {
211+
Self {
212+
notifications: RefCell::new(Vec::new()),
213+
}
214+
}
215+
}
201216

202217
#[async_trait(?Send)]
203218
impl Client for MockClient {
204219
async fn session_notification(
205220
&self,
206-
_: agent_client_protocol::SessionNotification,
221+
n: agent_client_protocol::SessionNotification,
207222
) -> agent_client_protocol::Result<()> {
223+
self.notifications.borrow_mut().push(format!("{:?}", n));
208224
Ok(())
209225
}
210226

@@ -228,8 +244,79 @@ mod tests {
228244
&opentelemetry::global::meter("acp-nats-test"),
229245
crate::config::Config::for_test("acp"),
230246
));
231-
let client = Rc::new(MockClient);
247+
let client = Rc::new(MockClient::new());
232248

233249
run(nats, client, bridge).await;
234250
}
251+
252+
#[test]
253+
fn jsonrpc_error_response_contains_code_and_message() {
254+
let bytes = jsonrpc_error_response(
255+
serde_json::Value::Number(1.into()),
256+
ErrorCode::InvalidParams,
257+
"bad input",
258+
);
259+
let v: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
260+
assert_eq!(v["jsonrpc"], "2.0");
261+
assert_eq!(v["id"], 1);
262+
assert_eq!(v["error"]["message"], "bad input");
263+
assert_eq!(v["error"]["code"], i32::from(ErrorCode::InvalidParams));
264+
}
265+
266+
#[test]
267+
fn extract_request_id_returns_id_from_valid_payload() {
268+
let payload = br#"{"jsonrpc":"2.0","id":42,"method":"foo"}"#;
269+
let id = extract_request_id(payload);
270+
assert_eq!(id, serde_json::Value::Number(42.into()));
271+
}
272+
273+
#[test]
274+
fn extract_request_id_returns_null_for_missing_id() {
275+
let payload = br#"{"jsonrpc":"2.0","method":"foo"}"#;
276+
let id = extract_request_id(payload);
277+
assert_eq!(id, serde_json::Value::Null);
278+
}
279+
280+
#[test]
281+
fn extract_request_id_returns_null_for_invalid_json() {
282+
let id = extract_request_id(b"not json");
283+
assert_eq!(id, serde_json::Value::Null);
284+
}
285+
286+
#[tokio::test]
287+
async fn handle_client_request_dispatches_session_update() {
288+
let nats = MockNatsClient::new();
289+
let bridge = Bridge::new(
290+
nats.clone(),
291+
SystemClock,
292+
&opentelemetry::global::meter("acp-nats-test"),
293+
crate::config::Config::for_test("acp"),
294+
);
295+
let client = MockClient::new();
296+
let session_id = AcpSessionId::new("sess-1").unwrap();
297+
298+
let notification = SessionNotification::new(
299+
"sess-1",
300+
SessionUpdate::AgentMessageChunk(ContentChunk::new(ContentBlock::from("hi"))),
301+
);
302+
let payload = bytes::Bytes::from(serde_json::to_vec(&notification).unwrap());
303+
304+
let parsed = crate::nats::ParsedClientSubject {
305+
session_id,
306+
method: ClientMethod::SessionUpdate,
307+
};
308+
309+
handle_client_request(
310+
"acp.sess-1.client.session.update",
311+
parsed,
312+
payload,
313+
None,
314+
&nats,
315+
&client,
316+
&bridge,
317+
)
318+
.await;
319+
320+
assert_eq!(client.notifications.borrow().len(), 1);
321+
}
235322
}

0 commit comments

Comments
 (0)