//! A fake `gatewayd` on a Unix socket: it accepts each courier's connection, reads the approval, //! and lets the test post it, answer it, close, or read the outcome. Do not edit. #![allow(dead_code)] // each test file uses a different part of this module use std::os::unix::net::{UnixListener, UnixStream}; use std::path::{Path, PathBuf}; use std::sync::mpsc::{self, Receiver}; use std::time::Duration; use bxcore::{ ApprovalPosted, ApprovalReply, Envelope, ErrorCode, GatewayOutcome, Message, PROTOCOL_VERSION, PendingApproval, WireError, read_frame, write_frame, }; pub const KYLE: &str = "k0000000000000000000000000"; pub const POST: &str = "c0000000000000000000000001"; pub struct FakeGateway { pub socket: PathBuf, conns: Receiver, } /// One courier's connection, its request already read. pub struct Conn { stream: UnixStream, pub request: PendingApproval, } fn frame(id: u64, last: bool, msg: Message) -> Envelope { Envelope { v: PROTOCOL_VERSION, id, r#final: last, msg, } } impl FakeGateway { /// Listen on `socket` (created now). pub fn start(socket: &Path) -> FakeGateway { let listener = UnixListener::bind(socket).unwrap(); let (tx, conns) = mpsc::channel(); std::thread::spawn(move || { for stream in listener.incoming() { let Ok(mut stream) = stream else { continue }; let Ok(env) = read_frame(&mut stream) else { continue; }; let Message::ApprovalRequest(req) = env.msg else { panic!("not a request: {env:?}") }; assert_eq!((env.id, env.r#final), (1, true)); if tx .send(Conn { stream, request: req.approval, }) .is_err() { return; } } }); FakeGateway { socket: socket.to_path_buf(), conns, } } /// The next courier connection within `wait`, or `None`. pub fn next(&self, wait: Duration) -> Option { self.conns.recv_timeout(wait).ok() } } impl Conn { pub fn posted(&mut self) { let posts = vec![POST.to_string()]; write_frame( &mut self.stream, &frame(1, false, Message::ApprovalPosted(ApprovalPosted { posts })), ) .unwrap(); } pub fn cannot_post(&mut self) { let e = WireError { code: ErrorCode::Internal, detail: "Mattermost is down".to_string(), }; write_frame(&mut self.stream, &frame(1, true, Message::Error(e))).unwrap(); } pub fn reply(&mut self, reply: ApprovalReply) { let _ = write_frame( &mut self.stream, &frame(1, true, Message::ApprovalReply(reply)), ); } pub fn approve(&mut self) { let approval = self.request.approval; self.reply(ApprovalReply { approval, approved: true, by: KYLE.to_string(), post: POST.to_string(), reason: None, }); } pub fn refuse(&mut self, reason: &str) { let approval = self.request.approval; self.reply(ApprovalReply { approval, approved: false, by: KYLE.to_string(), post: POST.to_string(), reason: Some(reason.to_string()), }); } /// Frame 4 within five seconds, or `None` if the courier closed without one. pub fn outcome(&mut self) -> Option { self.stream .set_read_timeout(Some(Duration::from_secs(5))) .unwrap(); match read_frame(&mut self.stream) { Ok(env) => { assert_eq!((env.id, env.r#final), (2, true)); match env.msg { Message::ApprovalOutcome(o) => Some(o.outcome), other => panic!("not an outcome: {other:?}"), } } Err(_) => None, } } /// Did the courier close the connection within `wait`, without sending anything? pub fn closed_within(&mut self, wait: Duration) -> bool { self.stream.set_read_timeout(Some(wait)).unwrap(); matches!( read_frame(&mut self.stream), Err(bxcore::FrameError::Closed) ) } pub fn close(self) { let _ = self.stream.shutdown(std::net::Shutdown::Both); } }