use tokio::sync::mpsc;
use crate::context::{Context, User};
pub struct TestContext {
ctx: Option<Context>,
rx: mpsc::UnboundedReceiver<String>,
}
impl TestContext {
pub fn channel(target: &str, sender_nick: &str, text: &str) -> Self {
TestContextBuilder::new()
.target(target)
.is_channel(true)
.sender_nick(sender_nick)
.text(text)
.build()
}
pub fn private(sender_nick: &str, text: &str) -> Self {
TestContextBuilder::new()
.target(sender_nick)
.is_channel(false)
.sender_nick(sender_nick)
.text(text)
.build()
}
pub fn builder() -> TestContextBuilder {
TestContextBuilder::new()
}
pub fn take_ctx(&mut self) -> Context {
self.ctx
.take()
.expect("take_ctx called twice on the same TestContext")
}
pub fn replies(&mut self) -> Vec<String> {
let mut out = Vec::new();
while let Ok(msg) = self.rx.try_recv() {
out.push(msg);
}
out
}
pub fn next_reply(&mut self) -> Option<String> {
self.rx.try_recv().ok()
}
}
#[derive(Debug)]
pub struct TestContextBuilder {
target: String,
is_channel: bool,
sender_nick: String,
sender_user: String,
sender_host: String,
bot_nick: String,
text: String,
captures: Vec<String>,
}
impl Default for TestContextBuilder {
fn default() -> Self {
TestContextBuilder {
target: "#test".to_string(),
is_channel: true,
sender_nick: "tester".to_string(),
sender_user: "tester".to_string(),
sender_host: "test.host".to_string(),
bot_nick: "testbot".to_string(),
text: String::new(),
captures: Vec::new(),
}
}
}
impl TestContextBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn target(mut self, t: impl Into<String>) -> Self {
self.target = t.into();
self
}
pub fn is_channel(mut self, v: bool) -> Self {
self.is_channel = v;
self
}
pub fn sender_nick(mut self, n: impl Into<String>) -> Self {
self.sender_nick = n.into();
self
}
pub fn sender_user(mut self, u: impl Into<String>) -> Self {
self.sender_user = u.into();
self
}
pub fn sender_host(mut self, h: impl Into<String>) -> Self {
self.sender_host = h.into();
self
}
pub fn bot_nick(mut self, n: impl Into<String>) -> Self {
self.bot_nick = n.into();
self
}
pub fn text(mut self, t: impl Into<String>) -> Self {
self.text = t.into();
self
}
pub fn captures(mut self, caps: Vec<String>) -> Self {
self.captures = caps;
self
}
pub fn build(self) -> TestContext {
let (tx, rx) = mpsc::unbounded_channel();
let raw_line = format!(
":{}!{}@{} PRIVMSG {} :{}",
self.sender_nick, self.sender_user, self.sender_host, self.target, self.text,
);
let raw = raw_line
.parse::<irc_proto::Message>()
.unwrap_or_else(|_| ":test!t@h PRIVMSG #test :test".parse().unwrap());
let ctx = Context {
tx,
target: self.target,
is_channel: self.is_channel,
sender: Some(User {
nick: self.sender_nick,
user: self.sender_user,
host: self.sender_host,
}),
raw,
bot_nick: self.bot_nick,
captures: self.captures,
};
TestContext { ctx: Some(ctx), rx }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn channel_sets_is_channel_true() {
let mut tc = TestContext::channel("#rust", "alice", "hello");
assert!(tc.take_ctx().is_channel);
}
#[test]
fn channel_sets_target() {
let mut tc = TestContext::channel("#rust", "alice", "hello");
assert_eq!(tc.take_ctx().target, "#rust");
}
#[test]
fn channel_sets_sender_nick() {
let mut tc = TestContext::channel("#rust", "alice", "hello");
let ctx = tc.take_ctx();
assert_eq!(ctx.sender.as_ref().unwrap().nick, "alice");
}
#[test]
fn channel_sets_message_text() {
let mut tc = TestContext::channel("#rust", "alice", "hello world");
assert_eq!(tc.take_ctx().message_text(), "hello world");
}
#[test]
fn private_sets_is_channel_false() {
let mut tc = TestContext::private("alice", "hey bot");
assert!(!tc.take_ctx().is_channel);
}
#[test]
fn private_sets_target_to_sender_nick() {
let mut tc = TestContext::private("alice", "hey bot");
assert_eq!(tc.take_ctx().target, "alice");
}
#[test]
fn private_sets_sender_nick() {
let mut tc = TestContext::private("alice", "hey bot");
let ctx = tc.take_ctx();
assert_eq!(ctx.sender.as_ref().unwrap().nick, "alice");
}
#[test]
fn take_ctx_returns_context() {
let mut tc = TestContext::channel("#test", "nick", "msg");
let ctx = tc.take_ctx();
assert_eq!(ctx.target, "#test");
}
#[test]
#[should_panic(expected = "take_ctx called twice")]
fn take_ctx_panics_on_second_call() {
let mut tc = TestContext::channel("#test", "nick", "msg");
let _ = tc.take_ctx();
let _ = tc.take_ctx(); }
#[test]
fn next_reply_returns_none_when_no_messages_sent() {
let mut tc = TestContext::channel("#test", "nick", "msg");
assert_eq!(tc.next_reply(), None);
}
#[test]
fn next_reply_captures_say() {
let mut tc = TestContext::channel("#test", "nick", "msg");
tc.take_ctx().say("hello").unwrap();
assert_eq!(
tc.next_reply(),
Some("PRIVMSG #test :hello\r\n".to_string()),
);
}
#[test]
fn next_reply_returns_messages_in_order() {
let mut tc = TestContext::channel("#test", "nick", "msg");
let ctx = tc.take_ctx();
ctx.say("first").unwrap();
ctx.say("second").unwrap();
assert_eq!(
tc.next_reply(),
Some("PRIVMSG #test :first\r\n".to_string()),
);
assert_eq!(
tc.next_reply(),
Some("PRIVMSG #test :second\r\n".to_string()),
);
assert_eq!(tc.next_reply(), None);
}
#[test]
fn replies_returns_empty_vec_when_nothing_sent() {
let mut tc = TestContext::channel("#test", "nick", "msg");
assert!(tc.replies().is_empty());
}
#[test]
fn replies_drains_all_messages_at_once() {
let mut tc = TestContext::channel("#test", "nick", "msg");
let ctx = tc.take_ctx();
ctx.say("one").unwrap();
ctx.say("two").unwrap();
ctx.say("three").unwrap();
let msgs = tc.replies();
assert_eq!(msgs.len(), 3);
assert_eq!(msgs[0], "PRIVMSG #test :one\r\n");
assert_eq!(msgs[1], "PRIVMSG #test :two\r\n");
assert_eq!(msgs[2], "PRIVMSG #test :three\r\n");
}
#[test]
fn replies_is_empty_after_being_drained() {
let mut tc = TestContext::channel("#test", "nick", "msg");
tc.take_ctx().say("hi").unwrap();
let _ = tc.replies();
assert!(tc.replies().is_empty());
}
#[test]
fn builder_default_target_is_test_channel() {
let mut tc = TestContextBuilder::new().build();
assert_eq!(tc.take_ctx().target, "#test");
}
#[test]
fn builder_default_is_channel_true() {
let mut tc = TestContextBuilder::new().build();
assert!(tc.take_ctx().is_channel);
}
#[test]
fn builder_default_sender_nick_is_tester() {
let mut tc = TestContextBuilder::new().build();
let ctx = tc.take_ctx();
assert_eq!(ctx.sender.as_ref().unwrap().nick, "tester");
}
#[test]
fn builder_default_bot_nick_is_testbot() {
let mut tc = TestContextBuilder::new().build();
assert_eq!(tc.take_ctx().bot_nick, "testbot");
}
#[test]
fn builder_default_captures_empty() {
let mut tc = TestContextBuilder::new().build();
assert!(tc.take_ctx().captures.is_empty());
}
#[test]
fn builder_target_overrides_default() {
let mut tc = TestContextBuilder::new().target("#general").build();
assert_eq!(tc.take_ctx().target, "#general");
}
#[test]
fn builder_is_channel_false_overrides_default() {
let mut tc = TestContextBuilder::new().is_channel(false).build();
assert!(!tc.take_ctx().is_channel);
}
#[test]
fn builder_sender_nick_overrides_default() {
let mut tc = TestContextBuilder::new().sender_nick("bob").build();
let ctx = tc.take_ctx();
assert_eq!(ctx.sender.as_ref().unwrap().nick, "bob");
}
#[test]
fn builder_sender_user_overrides_default() {
let mut tc = TestContextBuilder::new().sender_user("bobident").build();
let ctx = tc.take_ctx();
assert_eq!(ctx.sender.as_ref().unwrap().user, "bobident");
}
#[test]
fn builder_sender_host_overrides_default() {
let mut tc = TestContextBuilder::new().sender_host("example.com").build();
let ctx = tc.take_ctx();
assert_eq!(ctx.sender.as_ref().unwrap().host, "example.com");
}
#[test]
fn builder_bot_nick_overrides_default() {
let mut tc = TestContextBuilder::new().bot_nick("mybot").build();
assert_eq!(tc.take_ctx().bot_nick, "mybot");
}
#[test]
fn builder_text_sets_message_text() {
let mut tc = TestContextBuilder::new().text("hello there").build();
assert_eq!(tc.take_ctx().message_text(), "hello there");
}
#[test]
fn builder_captures_sets_captures_list() {
let caps = vec!["foo".to_string(), "bar".to_string()];
let mut tc = TestContextBuilder::new().captures(caps.clone()).build();
assert_eq!(tc.take_ctx().captures, caps);
}
#[test]
fn reply_in_channel_prefixes_nick() {
let mut tc = TestContext::channel("#test", "alice", "msg");
tc.take_ctx().reply("hi").unwrap();
assert_eq!(
tc.next_reply(),
Some("PRIVMSG #test :alice, hi\r\n".to_string()),
);
}
#[test]
fn reply_in_query_sends_to_sender() {
let mut tc = TestContext::private("alice", "msg");
tc.take_ctx().reply("hi").unwrap();
assert_eq!(tc.next_reply(), Some("PRIVMSG alice :hi\r\n".to_string()),);
}
}