use super::{LinkLife, LinkRegistry};
use crate::link::LinkEnd;
use crate::link_carrier::{CarrierError, CarrierReceive, LinkCarrier, WireCarrier, write_frame};
use crate::link_handshake::MachineIdentity;
use liminal::protocol::{DisconnectReason, Frame, ProtocolVersion};
use std::net::{TcpListener, TcpStream};
#[test]
fn an_idle_dialed_carrier_reports_eof_without_a_receive_call() -> TestResult {
let (carrier, peer, life) = pair()?;
let (sender, receiver) = std::sync::mpsc::channel();
life.observe(
"local".to_owned(),
std::sync::Arc::new(move |change| {
if matches!(
change,
crate::console::TopologyChange::Link {
state: crate::console::records::LinkState::Down,
..
}
) && let Err(error) = sender.send(())
{
eprintln!("idle carrier observer departed: {error}");
}
}),
)?;
drop(peer);
let observed = receiver.recv_timeout(std::time::Duration::from_secs(5));
assert!(
observed.is_ok(),
"idle EOF did not end the keyed carrier: {observed:?}"
);
assert_eq!(life.reason()?, Some("peer_gone".to_owned()));
assert_eq!(life.snapshot().links_closed_by_peer_gone, 1);
drop(carrier);
assert_eq!(life.snapshot().links_closed_by_shutdown, 0);
Ok(())
}
type TestResult = Result<(), Box<dyn std::error::Error>>;
fn pair() -> Result<(WireCarrier, TcpStream, LinkLife), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let socket = TcpStream::connect(listener.local_addr()?)?;
let (peer, _) = listener.accept()?;
let life = LinkLife::new(MachineIdentity::new([7; 32]));
let mut carrier = WireCarrier::new(socket, 1)?;
carrier.install_life(life.clone())?;
Ok((carrier, peer, life))
}
#[test]
fn every_cause_counts_once_across_repeated_owners_and_peer_opens() -> TestResult {
let registry = LinkRegistry::default();
let peer = MachineIdentity::new([7; 32]);
let reasons = [
LinkEnd::PeerGone,
LinkEnd::Revoked,
LinkEnd::Refused("denied".to_owned()),
LinkEnd::Replaced,
LinkEnd::Shutdown,
];
let mut links = Vec::new();
for reason in reasons {
let life = registry.opened(peer)?;
life.end(reason.clone())?;
life.clone().end(LinkEnd::Shutdown)?;
assert_eq!(life.reason()?, Some(reason.to_string()));
links.push(life);
}
let counts = links[0].snapshot();
assert_eq!(counts.links_opened, 5);
assert_eq!(counts.links_closed_by_peer_gone, 1);
assert_eq!(counts.links_closed_by_revoked, 1);
assert_eq!(counts.links_closed_by_refused, 1);
assert_eq!(counts.links_closed_by_replaced, 1);
assert_eq!(counts.links_closed_by_shutdown, 1);
Ok(())
}
#[test]
fn dialed_carrier_eof_keeps_peer_gone_after_drop() -> TestResult {
let (mut carrier, peer, life) = pair()?;
drop(peer);
assert_eq!(
carrier.receive()?,
CarrierReceive::End {
reason: "wire_eof".to_owned()
}
);
drop(carrier);
assert_eq!(life.reason()?, Some("peer_gone".to_owned()));
assert_eq!(life.snapshot().links_closed_by_peer_gone, 1);
assert_eq!(life.snapshot().links_closed_by_shutdown, 0);
Ok(())
}
#[test]
fn dialed_carrier_revocation_keeps_revoked_after_drop() -> TestResult {
let (mut carrier, mut peer, life) = pair()?;
write_frame(
&mut peer,
&Frame::DisconnectReason {
flags: 0,
reason: DisconnectReason::GrantRevoked,
},
)?;
assert_eq!(
carrier.receive()?,
CarrierReceive::End {
reason: "grant_revoked".to_owned()
}
);
drop(carrier);
assert_eq!(life.reason()?, Some("revoked".to_owned()));
assert_eq!(life.snapshot().links_closed_by_revoked, 1);
assert_eq!(life.snapshot().links_closed_by_shutdown, 0);
Ok(())
}
#[test]
fn an_unexpected_carrier_frame_ends_by_refusal_once() -> TestResult {
let (mut carrier, mut peer, life) = pair()?;
write_frame(
&mut peer,
&Frame::ConnectAck {
flags: 0,
selected_version: ProtocolVersion::new(1, 3),
capabilities: 0,
},
)?;
assert!(matches!(
carrier.receive(),
Err(CarrierError::UnexpectedFrame)
));
drop(carrier);
assert_eq!(
life.reason()?,
Some("refused: unexpected frame on wire link".to_owned())
);
assert_eq!(life.snapshot().links_closed_by_refused, 1);
assert_eq!(life.snapshot().links_closed_by_shutdown, 0);
Ok(())
}