#[cfg(test)]
mod client_tests {
use crate::client::builder::ClientBuilder;
use crate::client::LanguageClient;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
struct NoopClient;
impl LanguageClient for NoopClient {}
fn make_response(id: u64, result: serde_json::Value) -> Vec<u8> {
let body = format!(
"{{\"jsonrpc\":\"2.0\",\"id\":{id},\"result\":{result}}}",
id = id,
result = result
);
let frame = format!("Content-Length: {}\r\n\r\n{}", body.len(), body);
frame.into_bytes()
}
#[tokio::test]
async fn write_loop_frames_outgoing_message() {
let (client_side, mut server_side) = tokio::io::duplex(4096);
let (_, empty_input) = tokio::io::duplex(64);
let handle = ClientBuilder::new().build(NoopClient, empty_input, client_side);
handle.exit().await;
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
let mut buf = vec![0u8; 256];
let n = server_side.read(&mut buf).await.unwrap_or(0);
let written = std::str::from_utf8(&buf[..n]).unwrap_or("");
assert!(
written.starts_with("Content-Length: "),
"expected LSP framing, got: {:?}",
written
);
}
#[tokio::test]
async fn read_loop_delivers_response_to_request() {
let (mut response_writer, response_reader) = tokio::io::duplex(4096);
let (_, discard_output) = tokio::io::duplex(4096);
let handle = ClientBuilder::new().build(NoopClient, response_reader, discard_output);
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
let frame = make_response(1, serde_json::Value::Null);
response_writer.write_all(&frame).await.ok();
});
let result = handle.shutdown().await;
assert!(
result.is_ok(),
"shutdown should return Ok, got {:?}",
result
);
}
#[tokio::test]
async fn read_loop_handles_notification_without_panicking() {
let (mut notification_writer, notification_reader) = tokio::io::duplex(4096);
let (_, discard_output) = tokio::io::duplex(64);
let _handle = ClientBuilder::new().build(NoopClient, notification_reader, discard_output);
let body = r#"{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":3,"message":"hello"}}"#;
let frame = format!("Content-Length: {}\r\n\r\n{}", body.len(), body);
notification_writer
.write_all(frame.as_bytes())
.await
.unwrap();
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}
}