use super::*;
use serde_json::Value;
struct Peer {
says: std::io::Cursor<Vec<u8>>,
heard: Vec<u8>,
}
impl Peer {
fn stating(preface: &Value) -> Self {
let mut says = Vec::new();
frame::write_value(&mut says, preface).expect("frame");
Self {
says: std::io::Cursor::new(says),
heard: Vec::new(),
}
}
fn silent() -> Self {
Self {
says: std::io::Cursor::new(Vec::new()),
heard: Vec::new(),
}
}
fn frames(&self) -> Vec<Value> {
let mut cursor = std::io::Cursor::new(self.heard.clone());
let mut out = Vec::new();
while let Ok(Some(v)) = frame::read_value(&mut cursor) {
out.push(v);
}
out
}
}
impl Read for Peer {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.says.read(buf)
}
}
impl Write for Peer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.heard.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
struct Gone;
impl Read for Gone {
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
Err(io::Error::other("gone"))
}
}
impl Write for Gone {
fn write(&mut self, _: &[u8]) -> io::Result<usize> {
Err(io::Error::other("gone"))
}
fn flush(&mut self) -> io::Result<()> {
Err(io::Error::other("gone"))
}
}
#[test]
fn a_peer_of_this_version_is_admitted_and_told_ours() {
let mut peer = Peer::stating(&json!({ "protocol": PROTOCOL }));
assert!(admit(&mut peer));
assert_eq!(peer.frames(), vec![json!({ "protocol": PROTOCOL })]);
}
#[test]
fn a_peer_of_another_version_is_refused_by_name() {
let other = PROTOCOL + 1;
let mut peer = Peer::stating(&json!({ "protocol": other }));
assert!(!admit(&mut peer));
let frames = peer.frames();
assert_eq!(frames.len(), 2, "our preface, then the refusal");
assert_eq!(frames[0], json!({ "protocol": PROTOCOL }));
assert_eq!(frames[1]["ok"], json!(false));
let said = frames[1]["error"].as_str().expect("a sentence");
assert!(said.contains(&format!("version {PROTOCOL}")), "{said}");
assert!(said.contains(&format!("the peer speaks {other}")), "{said}");
assert!(said.contains("no negotiation"), "{said}");
}
#[test]
fn a_peer_that_states_no_version_is_refused_the_same_way() {
for mut peer in [
Peer::stating(&json!({ "op": "workspaces" })),
Peer::stating(&json!("not an object")),
Peer::silent(),
] {
assert!(!admit(&mut peer));
let frames = peer.frames();
let said = frames[1]["error"].as_str().expect("a sentence");
assert!(said.contains("the peer speaks no version"), "{said}");
}
}
#[test]
fn a_version_that_is_not_a_number_states_none() {
let mut peer = Peer::stating(&json!({ "protocol": "1" }));
assert!(!admit(&mut peer));
let said = peer.frames()[1]["error"]
.as_str()
.expect("a sentence")
.to_owned();
assert!(said.contains("the peer speaks no version"), "{said}");
}
#[test]
fn a_peer_that_cannot_be_greeted_is_refused() {
assert!(!admit(&mut Gone));
}
#[test]
fn the_seat_confirms_or_refuses_on_the_same_sentence() {
let mut agreed = Peer::stating(&json!({ "protocol": PROTOCOL }));
assert_eq!(confirm(&mut agreed), Ok(()));
let mut skewed = Peer::stating(&json!({ "protocol": 99 }));
let said = confirm(&mut skewed).expect_err("a mismatch refuses");
assert!(said.contains(&format!("version {PROTOCOL}")), "{said}");
assert!(said.contains("the peer speaks 99"), "{said}");
let said = confirm(&mut Gone).expect_err("a silent engine refuses");
assert!(said.contains("the peer speaks no version"), "{said}");
}
#[test]
fn stating_onto_a_dead_socket_is_an_error() {
assert!(state(&mut Gone).is_err());
}