use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpListener;
use ircbot::bot::run_bot_internal;
use ircbot::handler::{HandlerEntry, HandlerFn, Trigger};
use ircbot::{BoxFuture, Context, State};
const WELCOME: &[u8] = b":server 001 testbot :Welcome to the mock IRC server\r\n";
#[tokio::test]
async fn test_cron_handler_fires_periodically() {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap().to_string();
tokio::spawn(async move {
let (mut sock, _) = listener.accept().await.unwrap();
let _ = sock.write_all(WELCOME).await;
tokio::time::sleep(Duration::from_secs(60)).await;
});
let state = State::connect("testbot".to_string(), &addr, vec![])
.await
.expect("failed to connect to mock server");
let fire_count = Arc::new(AtomicUsize::new(0));
let fire_count_handler = Arc::clone(&fire_count);
let handler: HandlerFn<()> = Box::new(
move |_bot: Arc<()>, _ctx: Context| -> BoxFuture<ircbot::Result> {
let fc = Arc::clone(&fire_count_handler);
Box::pin(async move {
fc.fetch_add(1, Ordering::Relaxed);
Ok(())
})
},
);
let handlers = vec![HandlerEntry {
trigger: Trigger::Cron {
schedule: "* * * * * *".to_string(),
tz: "UTC".to_string(),
target: None,
},
handler,
}];
let bot = Arc::new(());
let handler_set = ircbot::internal::make_handler_set(handlers);
let bot_task = tokio::spawn(run_bot_internal(bot, state, handler_set));
tokio::time::timeout(Duration::from_secs(5), async {
loop {
if fire_count.load(Ordering::Relaxed) >= 2 {
return;
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
})
.await
.expect("cron handler did not fire at least twice within 5 s");
bot_task.abort();
}
#[tokio::test]
async fn test_cron_handler_context_target() {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap().to_string();
tokio::spawn(async move {
let (mut sock, _) = listener.accept().await.unwrap();
let _ = sock.write_all(WELCOME).await;
tokio::time::sleep(Duration::from_secs(60)).await;
});
let state = State::connect("testbot".to_string(), &addr, vec![])
.await
.expect("failed to connect to mock server");
let seen_target: Arc<std::sync::Mutex<Option<(String, bool)>>> =
Arc::new(std::sync::Mutex::new(None));
let seen_clone = Arc::clone(&seen_target);
let handler: HandlerFn<()> = Box::new(
move |_bot: Arc<()>, ctx: Context| -> BoxFuture<ircbot::Result> {
let sc = Arc::clone(&seen_clone);
Box::pin(async move {
let mut guard = sc.lock().unwrap();
if guard.is_none() {
*guard = Some((ctx.target.clone(), ctx.is_channel));
}
Ok(())
})
},
);
let handlers = vec![HandlerEntry {
trigger: Trigger::Cron {
schedule: "* * * * * *".to_string(),
tz: "UTC".to_string(),
target: Some("#chan".to_string()),
},
handler,
}];
let bot = Arc::new(());
let handler_set = ircbot::internal::make_handler_set(handlers);
let bot_task = tokio::spawn(run_bot_internal(bot, state, handler_set));
tokio::time::timeout(Duration::from_secs(5), async {
loop {
if seen_target.lock().unwrap().is_some() {
return;
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
})
.await
.expect("cron handler did not fire within 5 s");
let (target, is_channel) = seen_target.lock().unwrap().clone().unwrap();
assert_eq!(target, "#chan");
assert!(is_channel);
bot_task.abort();
}