151 lines
4.5 KiB
Rust
151 lines
4.5 KiB
Rust
//! 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 proto::{
|
|
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<Conn>,
|
|
}
|
|
|
|
/// 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<Conn> {
|
|
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<GatewayOutcome> {
|
|
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(proto::FrameError::Closed))
|
|
}
|
|
|
|
pub fn close(self) {
|
|
let _ = self.stream.shutdown(std::net::Shutdown::Both);
|
|
}
|
|
}
|