Lines
100 %
Functions
Branches
use super::{envelope, read_frame, ssh_args, write_frame};
use rpc::FrameDecoder;
use std::io::{Cursor, Read};
#[test]
fn ssh_args_force_no_pty_and_request_the_subsystem() {
assert_eq!(
ssh_args("user@host", None),
vec!["-T", "-s", "user@host", "nomisync-eval"]
);
ssh_args("host", Some(2222)),
vec!["-p", "2222", "-T", "-s", "host", "nomisync-eval"]
}
fn envelope_wraps_form_with_id_and_closing_paren_on_its_own_line() {
assert_eq!(envelope(1, "(+ 1 2)"), "(:id 1 :form (+ 1 2)\n)\n");
envelope(42, "(list-transactions)"),
"(:id 42 :form (list-transactions)\n)\n"
fn write_frame_emits_the_envelope_bytes() {
let mut out: Vec<u8> = Vec::new();
write_frame(&mut out, 7, "(get-balance \"x\")").unwrap();
String::from_utf8(out).unwrap(),
"(:id 7 :form (get-balance \"x\")\n)\n"
fn envelope_with_trailing_comment_still_frames_on_the_server() {
// Regression: a trailing line-comment in the form must NOT swallow
// the envelope's closing paren. Feeding the rendered frame into the
// same FrameDecoder the daemon uses must yield exactly one complete
// frame (pre-fix it stayed Incomplete forever, hanging the client).
let mut decoder = FrameDecoder::new();
decoder
.feed(envelope(1, "(+ 1 2) ; note").as_bytes())
.unwrap();
let frame = decoder
.next_frame()
.expect("a complete frame")
.expect("a valid frame");
assert!(frame.contains("(+ 1 2)"), "{frame}");
assert!(decoder.next_frame().is_none(), "no dangling bytes");
fn read_frame_returns_one_complete_balanced_envelope() {
let mut reader = Cursor::new(b"(:id 1 :value 3)\n".to_vec());
let frame = read_frame(&mut decoder, &mut reader).unwrap();
assert_eq!(frame, "(:id 1 :value 3)");
/// A `Read` that hands back its bytes in fixed-size chunks, to prove
/// `read_frame` reassembles a frame split across multiple reads.
struct ChunkedReader {
data: Vec<u8>,
pos: usize,
chunk: usize,
impl Read for ChunkedReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let remaining = &self.data[self.pos..];
let take = remaining.len().min(self.chunk).min(buf.len());
buf[..take].copy_from_slice(&remaining[..take]);
self.pos += take;
Ok(take)
fn read_frame_reassembles_a_frame_split_across_reads() {
let mut reader = ChunkedReader {
data: b"(:id 2 :value (a b c))\n".to_vec(),
pos: 0,
chunk: 3,
};
assert_eq!(frame, "(:id 2 :value (a b c))");
fn read_frame_errors_when_connection_closes_before_a_frame() {
let mut reader = Cursor::new(b"(:id 3 :value".to_vec());
let err = read_frame(&mut decoder, &mut reader).unwrap_err();
assert!(
err.to_string().contains("closed before a response"),
"{err}"
fn read_frame_yields_frames_in_order_for_pipelined_responses() {
let mut reader = Cursor::new(b"(:id 1 :value 1)\n(:id 2 :value 2)\n".to_vec());
read_frame(&mut decoder, &mut reader).unwrap(),
"(:id 1 :value 1)"
"(:id 2 :value 2)"