1
use super::{envelope, read_frame, ssh_args, write_frame};
2
use rpc::FrameDecoder;
3
use std::io::{Cursor, Read};
4

            
5
#[test]
6
1
fn ssh_args_force_no_pty_and_request_the_subsystem() {
7
1
    assert_eq!(
8
1
        ssh_args("user@host", None),
9
1
        vec!["-T", "-s", "user@host", "nomisync-eval"]
10
    );
11
1
    assert_eq!(
12
1
        ssh_args("host", Some(2222)),
13
1
        vec!["-p", "2222", "-T", "-s", "host", "nomisync-eval"]
14
    );
15
1
}
16

            
17
#[test]
18
1
fn envelope_wraps_form_with_id_and_closing_paren_on_its_own_line() {
19
1
    assert_eq!(envelope(1, "(+ 1 2)"), "(:id 1 :form (+ 1 2)\n)\n");
20
1
    assert_eq!(
21
1
        envelope(42, "(list-transactions)"),
22
        "(:id 42 :form (list-transactions)\n)\n"
23
    );
24
1
}
25

            
26
#[test]
27
1
fn write_frame_emits_the_envelope_bytes() {
28
1
    let mut out: Vec<u8> = Vec::new();
29
1
    write_frame(&mut out, 7, "(get-balance \"x\")").unwrap();
30
1
    assert_eq!(
31
1
        String::from_utf8(out).unwrap(),
32
        "(:id 7 :form (get-balance \"x\")\n)\n"
33
    );
34
1
}
35

            
36
#[test]
37
1
fn envelope_with_trailing_comment_still_frames_on_the_server() {
38
    // Regression: a trailing line-comment in the form must NOT swallow
39
    // the envelope's closing paren. Feeding the rendered frame into the
40
    // same FrameDecoder the daemon uses must yield exactly one complete
41
    // frame (pre-fix it stayed Incomplete forever, hanging the client).
42
1
    let mut decoder = FrameDecoder::new();
43
1
    decoder
44
1
        .feed(envelope(1, "(+ 1 2) ; note").as_bytes())
45
1
        .unwrap();
46
1
    let frame = decoder
47
1
        .next_frame()
48
1
        .expect("a complete frame")
49
1
        .expect("a valid frame");
50
1
    assert!(frame.contains("(+ 1 2)"), "{frame}");
51
1
    assert!(decoder.next_frame().is_none(), "no dangling bytes");
52
1
}
53

            
54
#[test]
55
1
fn read_frame_returns_one_complete_balanced_envelope() {
56
1
    let mut decoder = FrameDecoder::new();
57
1
    let mut reader = Cursor::new(b"(:id 1 :value 3)\n".to_vec());
58
1
    let frame = read_frame(&mut decoder, &mut reader).unwrap();
59
1
    assert_eq!(frame, "(:id 1 :value 3)");
60
1
}
61

            
62
/// A `Read` that hands back its bytes in fixed-size chunks, to prove
63
/// `read_frame` reassembles a frame split across multiple reads.
64
struct ChunkedReader {
65
    data: Vec<u8>,
66
    pos: usize,
67
    chunk: usize,
68
}
69

            
70
impl Read for ChunkedReader {
71
8
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
72
8
        let remaining = &self.data[self.pos..];
73
8
        let take = remaining.len().min(self.chunk).min(buf.len());
74
8
        buf[..take].copy_from_slice(&remaining[..take]);
75
8
        self.pos += take;
76
8
        Ok(take)
77
8
    }
78
}
79

            
80
#[test]
81
1
fn read_frame_reassembles_a_frame_split_across_reads() {
82
1
    let mut decoder = FrameDecoder::new();
83
1
    let mut reader = ChunkedReader {
84
1
        data: b"(:id 2 :value (a b c))\n".to_vec(),
85
1
        pos: 0,
86
1
        chunk: 3,
87
1
    };
88
1
    let frame = read_frame(&mut decoder, &mut reader).unwrap();
89
1
    assert_eq!(frame, "(:id 2 :value (a b c))");
90
1
}
91

            
92
#[test]
93
1
fn read_frame_errors_when_connection_closes_before_a_frame() {
94
1
    let mut decoder = FrameDecoder::new();
95
1
    let mut reader = Cursor::new(b"(:id 3 :value".to_vec());
96
1
    let err = read_frame(&mut decoder, &mut reader).unwrap_err();
97
1
    assert!(
98
1
        err.to_string().contains("closed before a response"),
99
        "{err}"
100
    );
101
1
}
102

            
103
#[test]
104
1
fn read_frame_yields_frames_in_order_for_pipelined_responses() {
105
1
    let mut decoder = FrameDecoder::new();
106
1
    let mut reader = Cursor::new(b"(:id 1 :value 1)\n(:id 2 :value 2)\n".to_vec());
107
1
    assert_eq!(
108
1
        read_frame(&mut decoder, &mut reader).unwrap(),
109
        "(:id 1 :value 1)"
110
    );
111
1
    assert_eq!(
112
1
        read_frame(&mut decoder, &mut reader).unwrap(),
113
        "(:id 2 :value 2)"
114
    );
115
1
}