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
|
/// Configure whether other users can set reminders to your direct messages
|
||||||
#[poise::command(slash_command, rename = "dm")]
|
#[poise::command(slash_command, rename = "dm")]
|
||||||
|
#[cfg(not(test))]
|
||||||
pub async fn allowed_dm(_ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn allowed_dm(_ctx: Context<'_>) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ impl Recordable for Options {
|
|||||||
|
|
||||||
/// Allow other users to set reminders in your direct messages
|
/// Allow other users to set reminders in your direct messages
|
||||||
#[poise::command(slash_command, rename = "allow", identifying_name = "set_allowed_dm")]
|
#[poise::command(slash_command, rename = "allow", identifying_name = "set_allowed_dm")]
|
||||||
|
#[cfg(not(test))]
|
||||||
pub async fn set(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn set(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
(Options {}).run(ctx).await
|
(Options {}).run(ctx).await
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,48 @@ impl Recordable for Options {
|
|||||||
|
|
||||||
/// Get the link to the web dashboard
|
/// Get the link to the web dashboard
|
||||||
#[poise::command(slash_command, rename = "dashboard", identifying_name = "dashboard")]
|
#[poise::command(slash_command, rename = "dashboard", identifying_name = "dashboard")]
|
||||||
|
#[cfg(not(test))]
|
||||||
pub async fn command(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn command(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
(Options {}).run(ctx).await
|
(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;
|
pub mod allowed_dm;
|
||||||
|
#[cfg(not(test))]
|
||||||
mod autocomplete;
|
mod autocomplete;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod clock_context_menu;
|
pub mod clock_context_menu;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod command_macro;
|
pub mod command_macro;
|
||||||
pub mod dashboard;
|
pub mod dashboard;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod donate;
|
pub mod donate;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod help;
|
pub mod help;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod info;
|
pub mod info;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod look;
|
pub mod look;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod multiline;
|
pub mod multiline;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod nudge;
|
pub mod nudge;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod offset;
|
pub mod offset;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod pause;
|
pub mod pause;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod remind;
|
pub mod remind;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod timezone;
|
pub mod timezone;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod todo;
|
pub mod todo;
|
||||||
|
#[cfg(not(test))]
|
||||||
pub mod webhook;
|
pub mod webhook;
|
||||||
|
18
src/main.rs
18
src/main.rs
@ -4,12 +4,18 @@
|
|||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
|
#[cfg(not(test))]
|
||||||
mod component_models;
|
mod component_models;
|
||||||
mod consts;
|
mod consts;
|
||||||
|
#[cfg(not(test))]
|
||||||
mod event_handlers;
|
mod event_handlers;
|
||||||
|
#[cfg(not(test))]
|
||||||
mod hooks;
|
mod hooks;
|
||||||
mod interval_parser;
|
mod interval_parser;
|
||||||
|
#[cfg(not(test))]
|
||||||
mod models;
|
mod models;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
mod time_parser;
|
mod time_parser;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
@ -33,6 +39,9 @@ use poise::serenity_prelude::{
|
|||||||
use sqlx::{MySql, Pool};
|
use sqlx::{MySql, Pool};
|
||||||
use tokio::sync::{broadcast, broadcast::Sender, RwLock};
|
use tokio::sync::{broadcast, broadcast::Sender, RwLock};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::test::TestContext;
|
||||||
|
#[cfg(not(test))]
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{
|
commands::{
|
||||||
allowed_dm, clock, clock_context_menu::clock_context_menu, command_macro, dashboard,
|
allowed_dm, clock, clock_context_menu::clock_context_menu, command_macro, dashboard,
|
||||||
@ -48,11 +57,18 @@ use crate::{
|
|||||||
type Database = MySql;
|
type Database = MySql;
|
||||||
|
|
||||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
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 Context<'a> = poise::Context<'a, Data, Error>;
|
||||||
|
|
||||||
type ApplicationContext<'a> = poise::ApplicationContext<'a, Data, Error>;
|
type ApplicationContext<'a> = poise::ApplicationContext<'a, Data, Error>;
|
||||||
|
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
database: Pool<Database>,
|
database: Pool<Database>,
|
||||||
|
#[cfg(not(test))]
|
||||||
recording_macros: RwLock<HashMap<(GuildId, UserId), CommandMacro>>,
|
recording_macros: RwLock<HashMap<(GuildId, UserId), CommandMacro>>,
|
||||||
popular_timezones: Vec<Tz>,
|
popular_timezones: Vec<Tz>,
|
||||||
_broadcast: Sender<()>,
|
_broadcast: Sender<()>,
|
||||||
@ -81,6 +97,7 @@ impl Display for Ended {
|
|||||||
impl StdError for Ended {}
|
impl StdError for Ended {}
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
|
#[cfg(not(test))]
|
||||||
async fn main() -> Result<(), Box<dyn StdError + Send + Sync>> {
|
async fn main() -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||||
let (tx, mut rx) = broadcast::channel(16);
|
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>> {
|
async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||||
env_logger::init();
|
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