use std::sync::Arc;
use shep_client::shep_core::protocol::{ProcessEventKind, ProcessInfo};
use crate::bot::{
channel::Board,
monitor::{Monitor, refresh},
};
pub struct Wired<B> {
pub monitor: Arc<Monitor>,
pub board: Arc<B>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Redraw {
Draw,
Forget,
}
fn action_for(kind: ProcessEventKind) -> Option<Redraw> {
match kind {
ProcessEventKind::Start
| ProcessEventKind::Online
| ProcessEventKind::Exit
| ProcessEventKind::Restart
| ProcessEventKind::Stop => Some(Redraw::Draw),
ProcessEventKind::Delete => Some(Redraw::Forget),
_ => None,
}
}
impl<B: Board> Wired<B> {
pub async fn on_process_event(&self, kind: ProcessEventKind, info: &ProcessInfo) {
if !refresh::is_running(&self.monitor) {
return;
}
match action_for(kind) {
Some(Redraw::Draw) => {
if let Err(err) = self.monitor.update_one(&*self.board, info).await {
eprintln!("shep-discord: {err}");
}
}
Some(Redraw::Forget) => self.monitor.forget(&*self.board, info.id).await,
None => {}
}
}
}
#[cfg(test)]
mod tests {
use serenity::all::MessageId;
use shep_client::shep_core::status::ProcStatus;
use crate::test_support::CountingChannel;
use super::*;
fn info(id: u32, name: &str) -> ProcessInfo {
ProcessInfo::builder(id, name, ProcStatus::Online).build()
}
fn wired() -> (Wired<CountingChannel>, Arc<CountingChannel>) {
let board = Arc::new(CountingChannel::new());
let wired = Wired {
monitor: Arc::new(Monitor::new()),
board: Arc::clone(&board),
};
(wired, board)
}
#[test]
fn every_process_event_the_monitor_acts_on_is_named_here() {
for kind in [
ProcessEventKind::Start,
ProcessEventKind::Online,
ProcessEventKind::Exit,
ProcessEventKind::Restart,
ProcessEventKind::Stop,
] {
assert_eq!(action_for(kind), Some(Redraw::Draw), "{kind:?}");
}
assert_eq!(action_for(ProcessEventKind::Delete), Some(Redraw::Forget));
for kind in [
ProcessEventKind::Reload,
ProcessEventKind::Reloaded,
ProcessEventKind::ReloadAbandoned,
ProcessEventKind::Errored,
] {
assert_eq!(action_for(kind), None, "{kind:?}");
}
}
#[tokio::test]
async fn a_monitor_nobody_started_draws_nothing_for_a_bus_event() {
let (wired, board) = wired();
wired
.on_process_event(ProcessEventKind::Online, &info(1, "web"))
.await;
wired
.on_process_event(ProcessEventKind::Delete, &info(1, "web"))
.await;
assert_eq!(
(board.sends(), board.edits(), board.deletes()),
(0, 0, 0),
"the monitor is off, so nothing the bus says reaches the channel"
);
}
#[tokio::test]
async fn a_sheep_the_bus_names_is_drawn_and_then_edited_in_place() {
let (wired, board) = wired();
refresh::mark_running_with_a_stand_in_task(&wired.monitor);
wired
.on_process_event(ProcessEventKind::Online, &info(1, "web"))
.await;
assert_eq!((board.sends(), board.edits()), (1, 0), "drawn on sight");
wired
.on_process_event(ProcessEventKind::Stop, &info(1, "web"))
.await;
assert_eq!(
(board.sends(), board.edits()),
(1, 1),
"the second event edits the message the first posted"
);
}
#[tokio::test]
async fn a_deleted_sheep_loses_the_message_the_monitor_drew_for_it() {
let (wired, board) = wired();
refresh::mark_running_with_a_stand_in_task(&wired.monitor);
wired
.on_process_event(ProcessEventKind::Start, &info(1, "web"))
.await;
wired
.on_process_event(ProcessEventKind::Delete, &info(1, "web"))
.await;
assert_eq!(board.deleted(), vec![MessageId::new(1)]);
}
#[tokio::test]
async fn an_event_the_table_ignores_draws_nothing_even_while_running() {
let (wired, board) = wired();
refresh::mark_running_with_a_stand_in_task(&wired.monitor);
for kind in [
ProcessEventKind::Reload,
ProcessEventKind::Reloaded,
ProcessEventKind::ReloadAbandoned,
ProcessEventKind::Errored,
] {
wired.on_process_event(kind, &info(1, "web")).await;
}
assert_eq!(
(board.sends(), board.edits(), board.deletes()),
(0, 0, 0),
"these four are left to the interval refresh"
);
}
}