feat: implement bots including tests

this starts work on #78
This commit is contained in:
Paul Makles
2023-04-22 14:25:02 +01:00
parent 56ead0f894
commit 6b10385c0d
11 changed files with 311 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
mod model;
// mod ops;
mod ops;
pub use model::*;
// pub use ops::*;
pub use ops::*;

View File

@@ -1,3 +1,5 @@
use revolt_result::Result;
use crate::Database;
auto_derived_partial!(
@@ -66,9 +68,59 @@ impl Bot {
}
/// Delete this bot
pub async fn delete(&self, db: &Database) -> Result<(), ()> {
pub async fn delete(&self, db: &Database) -> Result<()> {
// db.fetch_user(&self.id).await?.mark_deleted(db).await?;
// db.delete_bot(&self.id).await
Ok(())
db.delete_bot(&self.id).await
}
}
#[cfg(test)]
mod tests {
use crate::{Bot, FieldsBot};
#[async_std::test]
async fn crud() {
let db = database_test!("bot_crud");
let bot_id = "bot";
let user_id = "user";
let token = "my_token";
let bot = Bot {
id: bot_id.to_string(),
owner: user_id.to_string(),
token: token.to_string(),
interactions_url: Some("some url".to_string()),
..Default::default()
};
db.insert_bot(&bot).await.unwrap();
db.update_bot(
bot_id,
&crate::PartialBot {
public: Some(true),
..Default::default()
},
vec![FieldsBot::Token, FieldsBot::InteractionsURL],
)
.await
.unwrap();
let fetched_bot1 = db.fetch_bot(bot_id).await.unwrap();
let fetched_bot2 = db.fetch_bot_by_token(&fetched_bot1.token).await.unwrap();
let fetched_bots = db.fetch_bots_by_user(user_id).await.unwrap();
assert!(!bot.public);
assert!(fetched_bot1.public);
assert!(bot.interactions_url.is_some());
assert!(fetched_bot1.interactions_url.is_none());
assert_ne!(bot.token, fetched_bot1.token);
assert_eq!(fetched_bot1, fetched_bot2);
assert_eq!(fetched_bot1, fetched_bots[0]);
assert_eq!(1, db.get_number_of_bots_by_user(user_id).await.unwrap());
bot.delete(&db).await.unwrap();
assert!(db.fetch_bot(bot_id).await.is_err());
assert_eq!(0, db.get_number_of_bots_by_user(user_id).await.unwrap());
}
}

View File

@@ -1,5 +1,9 @@
mod dummy;
use revolt_result::Result;
use crate::{Bot, FieldsBot, PartialBot};
mod mongodb;
mod reference;
#[async_trait]
pub trait AbstractBots: Sync + Send {
@@ -13,7 +17,12 @@ pub trait AbstractBots: Sync + Send {
async fn insert_bot(&self, bot: &Bot) -> Result<()>;
/// Update bot with new information
async fn update_bot(&self, id: &str, bot: &PartialBot, remove: Vec<FieldsBot>) -> Result<()>;
async fn update_bot(
&self,
id: &str,
partial: &PartialBot,
remove: Vec<FieldsBot>,
) -> Result<()>;
/// Delete a bot from the database
async fn delete_bot(&self, id: &str) -> Result<()>;

View File

@@ -1,9 +1,102 @@
use revolt_database::MongoDb;
use ::mongodb::bson::doc;
use revolt_result::Result;
use crate::{Bot, FieldsBot, PartialBot};
use crate::{IntoDocumentPath, MongoDb};
use super::AbstractBots;
mod init;
mod scripts;
static COL: &str = "bots";
#[async_trait]
impl AbstractBots for MongoDb {}
impl AbstractBots for MongoDb {
/// Fetch a bot by its id
async fn fetch_bot(&self, id: &str) -> Result<Bot> {
self.find_one_by_id(COL, id)
.await
.map_err(|_| create_database_error!("find_one", COL))?
.ok_or_else(|| create_error!(NotFound))
}
/// Fetch a bot by its token
async fn fetch_bot_by_token(&self, token: &str) -> Result<Bot> {
self.find_one(
COL,
doc! {
"token": token
},
)
.await
.map_err(|_| create_database_error!("find_one", COL))?
.ok_or_else(|| create_error!(NotFound))
}
/// Insert new bot into the database
async fn insert_bot(&self, bot: &Bot) -> Result<()> {
self.insert_one(COL, &bot)
.await
.map(|_| ())
.map_err(|_| create_database_error!("insert_one", COL))
}
/// Update bot with new information
async fn update_bot(
&self,
id: &str,
partial: &PartialBot,
remove: Vec<FieldsBot>,
) -> Result<()> {
self.update_one_by_id(
COL,
id,
partial,
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
None,
)
.await
.map(|_| ())
.map_err(|_| create_database_error!("update_one", COL))
}
/// Delete a bot from the database
async fn delete_bot(&self, id: &str) -> Result<()> {
self.delete_one_by_id(COL, id)
.await
.map(|_| ())
.map_err(|_| create_database_error!("delete_one", COL))
}
/// Fetch bots owned by a user
async fn fetch_bots_by_user(&self, user_id: &str) -> Result<Vec<Bot>> {
self.find(
COL,
doc! {
"owner": user_id
},
)
.await
.map_err(|_| create_database_error!("find", COL))
}
/// Get the number of bots owned by a user
async fn get_number_of_bots_by_user(&self, user_id: &str) -> Result<usize> {
self.count_documents(
COL,
doc! {
"owner": user_id
},
)
.await
.map(|v| v as usize)
.map_err(|_| create_database_error!("count", COL))
}
}
impl IntoDocumentPath for FieldsBot {
fn as_path(&self) -> Option<&'static str> {
match self {
FieldsBot::InteractionsURL => Some("interactions_url"),
FieldsBot::Token => None,
}
}
}

View File

@@ -1,6 +1,81 @@
use revolt_database::DummyDb;
use revolt_result::Result;
use crate::ReferenceDb;
use crate::{Bot, FieldsBot, PartialBot};
use super::AbstractBots;
#[async_trait]
impl AbstractBots for DummyDb {}
impl AbstractBots for ReferenceDb {
/// Fetch a bot by its id
async fn fetch_bot(&self, id: &str) -> Result<Bot> {
let bots = self.bots.lock().await;
bots.get(id).cloned().ok_or_else(|| create_error!(NotFound))
}
/// Fetch a bot by its token
async fn fetch_bot_by_token(&self, token: &str) -> Result<Bot> {
let bots = self.bots.lock().await;
bots.values()
.find(|bot| bot.token == token)
.cloned()
.ok_or_else(|| create_error!(NotFound))
}
/// Insert new bot into the database
async fn insert_bot(&self, bot: &Bot) -> Result<()> {
let mut bots = self.bots.lock().await;
if bots.contains_key(&bot.id) {
Err(create_database_error!("insert", "bot"))
} else {
bots.insert(bot.id.to_string(), bot.clone());
Ok(())
}
}
/// Update bot with new information
async fn update_bot(
&self,
id: &str,
partial: &PartialBot,
remove: Vec<FieldsBot>,
) -> Result<()> {
let mut bots = self.bots.lock().await;
if let Some(bot) = bots.get_mut(id) {
for field in remove {
bot.remove(&field);
}
bot.apply_options(partial.clone());
Ok(())
} else {
Err(create_error!(NotFound))
}
}
/// Delete a bot from the database
async fn delete_bot(&self, id: &str) -> Result<()> {
let mut bots = self.bots.lock().await;
if bots.remove(id).is_some() {
Ok(())
} else {
Err(create_error!(NotFound))
}
}
/// Fetch bots owned by a user
async fn fetch_bots_by_user(&self, user_id: &str) -> Result<Vec<Bot>> {
let bots = self.bots.lock().await;
Ok(bots
.values()
.filter(|bot| bot.owner == user_id)
.cloned()
.collect())
}
/// Get the number of bots owned by a user
async fn get_number_of_bots_by_user(&self, user_id: &str) -> Result<usize> {
let bots = self.bots.lock().await;
Ok(bots.values().filter(|bot| bot.owner == user_id).count())
}
}

View File

@@ -6,7 +6,11 @@ pub use bots::*;
use crate::{Database, MongoDb, ReferenceDb};
pub trait AbstractDatabase: Sync + Send + admin_migrations::AbstractMigrations {}
pub trait AbstractDatabase:
Sync + Send + admin_migrations::AbstractMigrations + bots::AbstractBots
{
}
impl AbstractDatabase for ReferenceDb {}
impl AbstractDatabase for MongoDb {}