Add unit tests
This commit is contained in:
parent
dd7e681285
commit
025049096c
@ -5,6 +5,7 @@ use crate::{Context, Error};
|
||||
|
||||
/// Configure whether other users can set reminders to your direct messages
|
||||
#[poise::command(slash_command, rename = "dm")]
|
||||
#[cfg(not(test))]
|
||||
pub async fn allowed_dm(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ impl Recordable for Options {
|
||||
|
||||
/// Allow other users to set reminders in your direct messages
|
||||
#[poise::command(slash_command, rename = "allow", identifying_name = "set_allowed_dm")]
|
||||
#[cfg(not(test))]
|
||||
pub async fn set(ctx: Context<'_>) -> Result<(), Error> {
|
||||
(Options {}).run(ctx).await
|
||||
}
|
||||
|
@ -31,6 +31,48 @@ impl Recordable for Options {
|
||||
|
||||
/// Get the link to the web dashboard
|
||||
#[poise::command(slash_command, rename = "dashboard", identifying_name = "dashboard")]
|
||||
#[cfg(not(test))]
|
||||
pub async fn command(ctx: Context<'_>) -> Result<(), Error> {
|
||||
(Options {}).run(ctx).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::env;
|
||||
|
||||
use sqlx::Pool;
|
||||
use tokio::sync::{broadcast, Mutex};
|
||||
|
||||
use crate::{
|
||||
commands::dashboard::Options,
|
||||
test::{MockCache, TestContext, TestData},
|
||||
utils::Recordable,
|
||||
Data,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn dashboard_command() {
|
||||
let (tx, _rx) = broadcast::channel(16);
|
||||
let database = Pool::connect(&env::var("DATABASE_URL").expect("No database URL provided"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let ctx = TestContext {
|
||||
data: &Data { database, popular_timezones: vec![], _broadcast: tx },
|
||||
cache: &MockCache {},
|
||||
test_data: &Mutex::new(TestData { replies: vec![] }),
|
||||
shard_id: 0,
|
||||
};
|
||||
|
||||
let res = (Options {}).run(ctx).await;
|
||||
|
||||
assert!(res.is_ok(), "command OK");
|
||||
assert_eq!(ctx.test_data.lock().await.replies.len(), 1, "one message sent");
|
||||
assert!(
|
||||
ctx.sent_content()
|
||||
.await
|
||||
.contains(&String::from("**https://beta.reminder-bot.com/dashboard**")),
|
||||
"content correct"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,41 @@
|
||||
#[cfg(not(test))]
|
||||
pub mod allowed_dm;
|
||||
#[cfg(not(test))]
|
||||
mod autocomplete;
|
||||
#[cfg(not(test))]
|
||||
pub mod clock;
|
||||
#[cfg(not(test))]
|
||||
pub mod clock_context_menu;
|
||||
#[cfg(not(test))]
|
||||
pub mod command_macro;
|
||||
pub mod dashboard;
|
||||
#[cfg(not(test))]
|
||||
pub mod delete;
|
||||
#[cfg(not(test))]
|
||||
pub mod donate;
|
||||
#[cfg(not(test))]
|
||||
pub mod help;
|
||||
#[cfg(not(test))]
|
||||
pub mod info;
|
||||
#[cfg(not(test))]
|
||||
pub mod look;
|
||||
#[cfg(not(test))]
|
||||
pub mod multiline;
|
||||
#[cfg(not(test))]
|
||||
pub mod nudge;
|
||||
#[cfg(not(test))]
|
||||
pub mod offset;
|
||||
#[cfg(not(test))]
|
||||
pub mod pause;
|
||||
#[cfg(not(test))]
|
||||
pub mod remind;
|
||||
#[cfg(not(test))]
|
||||
pub mod settings;
|
||||
#[cfg(not(test))]
|
||||
pub mod timer;
|
||||
#[cfg(not(test))]
|
||||
pub mod timezone;
|
||||
#[cfg(not(test))]
|
||||
pub mod todo;
|
||||
#[cfg(not(test))]
|
||||
pub mod webhook;
|
||||
|
18
src/main.rs
18
src/main.rs
@ -4,12 +4,18 @@
|
||||
extern crate lazy_static;
|
||||
|
||||
mod commands;
|
||||
#[cfg(not(test))]
|
||||
mod component_models;
|
||||
mod consts;
|
||||
#[cfg(not(test))]
|
||||
mod event_handlers;
|
||||
#[cfg(not(test))]
|
||||
mod hooks;
|
||||
mod interval_parser;
|
||||
#[cfg(not(test))]
|
||||
mod models;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod time_parser;
|
||||
mod utils;
|
||||
|
||||
@ -33,6 +39,9 @@ use poise::serenity_prelude::{
|
||||
use sqlx::{MySql, Pool};
|
||||
use tokio::sync::{broadcast, broadcast::Sender, RwLock};
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::test::TestContext;
|
||||
#[cfg(not(test))]
|
||||
use crate::{
|
||||
commands::{
|
||||
allowed_dm, clock, clock_context_menu::clock_context_menu, command_macro, dashboard,
|
||||
@ -48,11 +57,18 @@ use crate::{
|
||||
type Database = MySql;
|
||||
|
||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||
|
||||
#[cfg(test)]
|
||||
type Context<'a> = TestContext<'a>;
|
||||
|
||||
#[cfg(not(test))]
|
||||
type Context<'a> = poise::Context<'a, Data, Error>;
|
||||
|
||||
type ApplicationContext<'a> = poise::ApplicationContext<'a, Data, Error>;
|
||||
|
||||
pub struct Data {
|
||||
database: Pool<Database>,
|
||||
#[cfg(not(test))]
|
||||
recording_macros: RwLock<HashMap<(GuildId, UserId), CommandMacro>>,
|
||||
popular_timezones: Vec<Tz>,
|
||||
_broadcast: Sender<()>,
|
||||
@ -81,6 +97,7 @@ impl Display for Ended {
|
||||
impl StdError for Ended {}
|
||||
|
||||
#[tokio::main(flavor = "multi_thread")]
|
||||
#[cfg(not(test))]
|
||||
async fn main() -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||
let (tx, mut rx) = broadcast::channel(16);
|
||||
|
||||
@ -90,6 +107,7 @@ async fn main() -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||
env_logger::init();
|
||||
|
||||
|
95
src/test/mod.rs
Normal file
95
src/test/mod.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use poise::{
|
||||
serenity_prelude::{GuildId, UserId},
|
||||
CreateReply,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{Data, Error};
|
||||
|
||||
pub(crate) struct TestData {
|
||||
pub(crate) replies: Vec<CreateReply>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct TestContext<'a> {
|
||||
pub(crate) data: &'a Data,
|
||||
pub(crate) cache: &'a MockCache,
|
||||
pub(crate) test_data: &'a Mutex<TestData>,
|
||||
pub(crate) shard_id: usize,
|
||||
}
|
||||
|
||||
pub(crate) struct MockUser {
|
||||
pub(crate) id: UserId,
|
||||
}
|
||||
|
||||
pub(crate) struct MockCache {}
|
||||
|
||||
impl<'a> TestContext<'a> {
|
||||
pub async fn say(&self, message: impl Into<String>) -> Result<(), Error> {
|
||||
self.test_data.lock().await.replies.push(CreateReply::default().content(message));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send(&self, reply: CreateReply) -> Result<(), Error> {
|
||||
self.test_data.lock().await.replies.push(reply.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn guild_id(&self) -> Option<GuildId> {
|
||||
Some(GuildId::new(1))
|
||||
}
|
||||
|
||||
pub async fn defer_ephemeral(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn author(&self) -> MockUser {
|
||||
MockUser { id: UserId::new(1) }
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &Data {
|
||||
return &self.data;
|
||||
}
|
||||
|
||||
pub fn serenity_context(&self) -> &Self {
|
||||
return &self;
|
||||
}
|
||||
|
||||
pub async fn sent_content(&self) -> Vec<String> {
|
||||
let data = self.test_data.lock().await;
|
||||
|
||||
data.replies
|
||||
.iter()
|
||||
.map(|r| {
|
||||
let reply = r.clone();
|
||||
let content = reply.content.unwrap_or(String::new());
|
||||
let embed_content = reply
|
||||
.embeds
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let map = serde_json::to_value(e).unwrap();
|
||||
let description =
|
||||
map.get("description").cloned().unwrap_or(Value::String(String::new()));
|
||||
return format!("{}", description.as_str().unwrap());
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
return if content.is_empty() {
|
||||
embed_content
|
||||
} else {
|
||||
format!("{}\n{}", content, embed_content)
|
||||
};
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl MockCache {
|
||||
pub fn shard_count(&self) -> usize {
|
||||
return 1;
|
||||
}
|
||||
}
|
3753
web/Cargo.lock
generated
Normal file
3753
web/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user