Add modlog entries for bulk removals. (#5050)

* Add modlog entries for bulk removals.

- Added unit tests for removal / restore to api_common/utils.
- Fixes #4699

* Address PR comments.

* Combining remove and restore functions.

* Trigger build.

* Trigger build 2.

* Changing allow to expect.
This commit is contained in:
Dessalines 2024-10-01 10:59:52 -04:00 committed by GitHub
parent 338344dbc5
commit e3edc317be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 424 additions and 176 deletions

View file

@ -92,8 +92,10 @@ pub async fn ban_from_community(
let remove_data = data.ban; let remove_data = data.ban;
remove_or_restore_user_data_in_community( remove_or_restore_user_data_in_community(
data.community_id, data.community_id,
local_user_view.person.id,
banned_person_id, banned_person_id,
remove_data, remove_data,
&data.reason,
&mut context.pool(), &mut context.pool(),
) )
.await?; .await?;

View file

@ -5,7 +5,7 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
person::{BanPerson, BanPersonResponse}, person::{BanPerson, BanPersonResponse},
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
utils::{check_expire_time, is_admin, remove_user_data, restore_user_data}, utils::{check_expire_time, is_admin, remove_or_restore_user_data},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -66,11 +66,15 @@ pub async fn ban_from_site(
// Remove their data if that's desired // Remove their data if that's desired
if data.remove_or_restore_data.unwrap_or(false) { if data.remove_or_restore_data.unwrap_or(false) {
if data.ban { let removed = data.ban;
remove_user_data(person.id, &context).await?; remove_or_restore_user_data(
} else { local_user_view.person.id,
restore_user_data(person.id, &context).await?; person.id,
} removed,
&data.reason,
&context,
)
.await?;
}; };
// Mod tables // Mod tables

View file

@ -11,7 +11,7 @@ use chrono::{DateTime, Days, Local, TimeZone, Utc};
use enum_map::{enum_map, EnumMap}; use enum_map::{enum_map, EnumMap};
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm}, aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
newtypes::{CommunityId, DbUrl, InstanceId, PersonId, PostId}, newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId},
source::{ source::{
comment::{Comment, CommentUpdateForm}, comment::{Comment, CommentUpdateForm},
community::{Community, CommunityModerator, CommunityUpdateForm}, community::{Community, CommunityModerator, CommunityUpdateForm},
@ -23,6 +23,7 @@ use lemmy_db_schema::{
local_site::LocalSite, local_site::LocalSite,
local_site_rate_limit::LocalSiteRateLimit, local_site_rate_limit::LocalSiteRateLimit,
local_site_url_blocklist::LocalSiteUrlBlocklist, local_site_url_blocklist::LocalSiteUrlBlocklist,
moderator::{ModRemoveComment, ModRemoveCommentForm, ModRemovePost, ModRemovePostForm},
oauth_account::OAuthAccount, oauth_account::OAuthAccount,
password_reset_request::PasswordResetRequest, password_reset_request::PasswordResetRequest,
person::{Person, PersonUpdateForm}, person::{Person, PersonUpdateForm},
@ -667,112 +668,179 @@ pub async fn purge_image_posts_for_community(
Ok(()) Ok(())
} }
pub async fn remove_user_data( /// Removes or restores user data.
pub async fn remove_or_restore_user_data(
mod_person_id: PersonId,
banned_person_id: PersonId, banned_person_id: PersonId,
removed: bool,
reason: &Option<String>,
context: &LemmyContext, context: &LemmyContext,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let pool = &mut context.pool(); let pool = &mut context.pool();
// Purge user images
let person = Person::read(pool, banned_person_id).await?; // Only these actions are possible when removing, not restoring
if let Some(avatar) = person.avatar { if removed {
purge_image_from_pictrs(&avatar, context).await.ok(); // Purge user images
} let person = Person::read(pool, banned_person_id).await?;
if let Some(banner) = person.banner { if let Some(avatar) = person.avatar {
purge_image_from_pictrs(&banner, context).await.ok(); purge_image_from_pictrs(&avatar, context).await.ok();
}
if let Some(banner) = person.banner {
purge_image_from_pictrs(&banner, context).await.ok();
}
// Update the fields to None
Person::update(
pool,
banned_person_id,
&PersonUpdateForm {
avatar: Some(None),
banner: Some(None),
bio: Some(None),
..Default::default()
},
)
.await?;
// Purge image posts
purge_image_posts_for_person(banned_person_id, context).await?;
// Communities
// Remove all communities where they're the top mod
// for now, remove the communities manually
let first_mod_communities = CommunityModeratorView::get_community_first_mods(pool).await?;
// Filter to only this banned users top communities
let banned_user_first_communities: Vec<CommunityModeratorView> = first_mod_communities
.into_iter()
.filter(|fmc| fmc.moderator.id == banned_person_id)
.collect();
for first_mod_community in banned_user_first_communities {
let community_id = first_mod_community.community.id;
Community::update(
pool,
community_id,
&CommunityUpdateForm {
removed: Some(removed),
..Default::default()
},
)
.await?;
// Delete the community images
if let Some(icon) = first_mod_community.community.icon {
purge_image_from_pictrs(&icon, context).await.ok();
}
if let Some(banner) = first_mod_community.community.banner {
purge_image_from_pictrs(&banner, context).await.ok();
}
// Update the fields to None
Community::update(
pool,
community_id,
&CommunityUpdateForm {
icon: Some(None),
banner: Some(None),
..Default::default()
},
)
.await?;
}
} }
// Update the fields to None // Posts
Person::update( let removed_or_restored_posts =
Post::update_removed_for_creator(pool, banned_person_id, None, removed).await?;
create_modlog_entries_for_removed_or_restored_posts(
pool, pool,
banned_person_id, mod_person_id,
&PersonUpdateForm { removed_or_restored_posts.iter().map(|r| r.id).collect(),
avatar: Some(None), removed,
banner: Some(None), reason,
bio: Some(None),
..Default::default()
},
) )
.await?; .await?;
// Posts
Post::update_removed_for_creator(pool, banned_person_id, None, true).await?;
// Purge image posts
purge_image_posts_for_person(banned_person_id, context).await?;
// Communities
// Remove all communities where they're the top mod
// for now, remove the communities manually
let first_mod_communities = CommunityModeratorView::get_community_first_mods(pool).await?;
// Filter to only this banned users top communities
let banned_user_first_communities: Vec<CommunityModeratorView> = first_mod_communities
.into_iter()
.filter(|fmc| fmc.moderator.id == banned_person_id)
.collect();
for first_mod_community in banned_user_first_communities {
let community_id = first_mod_community.community.id;
Community::update(
pool,
community_id,
&CommunityUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await?;
// Delete the community images
if let Some(icon) = first_mod_community.community.icon {
purge_image_from_pictrs(&icon, context).await.ok();
}
if let Some(banner) = first_mod_community.community.banner {
purge_image_from_pictrs(&banner, context).await.ok();
}
// Update the fields to None
Community::update(
pool,
community_id,
&CommunityUpdateForm {
icon: Some(None),
banner: Some(None),
..Default::default()
},
)
.await?;
}
// Comments // Comments
Comment::update_removed_for_creator(pool, banned_person_id, true).await?; let removed_or_restored_comments =
Comment::update_removed_for_creator(pool, banned_person_id, removed).await?;
create_modlog_entries_for_removed_or_restored_comments(
pool,
mod_person_id,
removed_or_restored_comments.iter().map(|r| r.id).collect(),
removed,
reason,
)
.await?;
Ok(()) Ok(())
} }
/// We can't restore their images, but we can unremove their posts and comments async fn create_modlog_entries_for_removed_or_restored_posts(
pub async fn restore_user_data( pool: &mut DbPool<'_>,
banned_person_id: PersonId, mod_person_id: PersonId,
context: &LemmyContext, post_ids: Vec<PostId>,
removed: bool,
reason: &Option<String>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let pool = &mut context.pool(); // Build the forms
let forms = post_ids
.iter()
.map(|&post_id| ModRemovePostForm {
mod_person_id,
post_id,
removed: Some(removed),
reason: reason.clone(),
})
.collect();
// Posts ModRemovePost::create_multiple(pool, &forms).await?;
Post::update_removed_for_creator(pool, banned_person_id, None, false).await?;
// Comments Ok(())
Comment::update_removed_for_creator(pool, banned_person_id, false).await?; }
async fn create_modlog_entries_for_removed_or_restored_comments(
pool: &mut DbPool<'_>,
mod_person_id: PersonId,
comment_ids: Vec<CommentId>,
removed: bool,
reason: &Option<String>,
) -> LemmyResult<()> {
// Build the forms
let forms = comment_ids
.iter()
.map(|&comment_id| ModRemoveCommentForm {
mod_person_id,
comment_id,
removed: Some(removed),
reason: reason.clone(),
})
.collect();
ModRemoveComment::create_multiple(pool, &forms).await?;
Ok(()) Ok(())
} }
pub async fn remove_or_restore_user_data_in_community( pub async fn remove_or_restore_user_data_in_community(
community_id: CommunityId, community_id: CommunityId,
mod_person_id: PersonId,
banned_person_id: PersonId, banned_person_id: PersonId,
remove: bool, remove: bool,
reason: &Option<String>,
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
// Posts // Posts
Post::update_removed_for_creator(pool, banned_person_id, Some(community_id), remove).await?; let posts =
Post::update_removed_for_creator(pool, banned_person_id, Some(community_id), remove).await?;
create_modlog_entries_for_removed_or_restored_posts(
pool,
mod_person_id,
posts.iter().map(|r| r.id).collect(),
remove,
reason,
)
.await?;
// Comments // Comments
// TODO Diesel doesn't allow updates with joins, so this has to be a loop // TODO Diesel doesn't allow updates with joins, so this has to be a loop
@ -798,6 +866,15 @@ pub async fn remove_or_restore_user_data_in_community(
.await?; .await?;
} }
create_modlog_entries_for_removed_or_restored_comments(
pool,
mod_person_id,
comments.iter().map(|r| r.comment.id).collect(),
remove,
reason,
)
.await?;
Ok(()) Ok(())
} }
@ -1067,10 +1144,20 @@ fn build_proxied_image_url(
} }
#[cfg(test)] #[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests { mod tests {
use super::*; use super::*;
use lemmy_db_schema::source::{
comment::CommentInsertForm,
community::CommunityInsertForm,
person::PersonInsertForm,
post::PostInsertForm,
};
use lemmy_db_views_moderator::structs::{
ModRemoveCommentView,
ModRemovePostView,
ModlogListParams,
};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
@ -1092,48 +1179,42 @@ mod tests {
} }
#[test] #[test]
fn test_limit_ban_term() { fn test_limit_ban_term() -> LemmyResult<()> {
// Ban expires in past, should throw error // Ban expires in past, should throw error
assert!(limit_expire_time(Utc::now() - Days::new(5)).is_err()); assert!(limit_expire_time(Utc::now() - Days::new(5)).is_err());
// Legitimate ban term, return same value // Legitimate ban term, return same value
let fourteen_days = Utc::now() + Days::new(14); let fourteen_days = Utc::now() + Days::new(14);
assert_eq!( assert_eq!(limit_expire_time(fourteen_days)?, Some(fourteen_days));
limit_expire_time(fourteen_days).unwrap(),
Some(fourteen_days)
);
let nine_years = Utc::now() + Days::new(365 * 9); let nine_years = Utc::now() + Days::new(365 * 9);
assert_eq!(limit_expire_time(nine_years).unwrap(), Some(nine_years)); assert_eq!(limit_expire_time(nine_years)?, Some(nine_years));
// Too long ban term, changes to None (permanent ban) // Too long ban term, changes to None (permanent ban)
assert_eq!( assert_eq!(limit_expire_time(Utc::now() + Days::new(365 * 11))?, None);
limit_expire_time(Utc::now() + Days::new(365 * 11)).unwrap(),
None Ok(())
);
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_proxy_image_link() { async fn test_proxy_image_link() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await; let context = LemmyContext::init_test_context().await;
// image from local domain is unchanged // image from local domain is unchanged
let local_url = Url::parse("http://lemmy-alpha/image.png").unwrap(); let local_url = Url::parse("http://lemmy-alpha/image.png")?;
let proxied = let proxied =
proxy_image_link_internal(local_url.clone(), PictrsImageMode::ProxyAllImages, &context) proxy_image_link_internal(local_url.clone(), PictrsImageMode::ProxyAllImages, &context)
.await .await?;
.unwrap();
assert_eq!(&local_url, proxied.inner()); assert_eq!(&local_url, proxied.inner());
// image from remote domain is proxied // image from remote domain is proxied
let remote_image = Url::parse("http://lemmy-beta/image.png").unwrap(); let remote_image = Url::parse("http://lemmy-beta/image.png")?;
let proxied = proxy_image_link_internal( let proxied = proxy_image_link_internal(
remote_image.clone(), remote_image.clone(),
PictrsImageMode::ProxyAllImages, PictrsImageMode::ProxyAllImages,
&context, &context,
) )
.await .await?;
.unwrap();
assert_eq!( assert_eq!(
"https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png", "https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png",
proxied.as_str() proxied.as_str()
@ -1146,5 +1227,159 @@ mod tests {
.await .await
.is_ok() .is_ok()
); );
Ok(())
}
#[tokio::test]
#[serial]
async fn test_mod_remove_or_restore_data() -> LemmyResult<()> {
let context = LemmyContext::init_test_context().await;
let pool = &mut context.pool();
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
let new_mod = PersonInsertForm::test_form(inserted_instance.id, "modder");
let inserted_mod = Person::create(pool, &new_mod).await?;
let new_person = PersonInsertForm::test_form(inserted_instance.id, "chrimbus");
let inserted_person = Person::create(pool, &new_person).await?;
let new_community = CommunityInsertForm::new(
inserted_instance.id,
"mod_community crepes".to_string(),
"nada".to_owned(),
"pubkey".to_string(),
);
let inserted_community = Community::create(pool, &new_community).await?;
let post_form_1 = PostInsertForm::new(
"A test post tubular".into(),
inserted_person.id,
inserted_community.id,
);
let inserted_post_1 = Post::create(pool, &post_form_1).await?;
let post_form_2 = PostInsertForm::new(
"A test post radical".into(),
inserted_person.id,
inserted_community.id,
);
let inserted_post_2 = Post::create(pool, &post_form_2).await?;
let comment_form_1 = CommentInsertForm::new(
inserted_person.id,
inserted_post_1.id,
"A test comment tubular".into(),
);
let _inserted_comment_1 = Comment::create(pool, &comment_form_1, None).await?;
let comment_form_2 = CommentInsertForm::new(
inserted_person.id,
inserted_post_2.id,
"A test comment radical".into(),
);
let _inserted_comment_2 = Comment::create(pool, &comment_form_2, None).await?;
// Remove the user data
remove_or_restore_user_data(
inserted_mod.id,
inserted_person.id,
true,
&Some("a remove reason".to_string()),
&context,
)
.await?;
// Verify that their posts and comments are removed.
let params = ModlogListParams {
community_id: None,
mod_person_id: None,
other_person_id: None,
post_id: None,
comment_id: None,
page: None,
limit: None,
hide_modlog_names: false,
};
// Posts
let post_modlog = ModRemovePostView::list(pool, params).await?;
assert_eq!(2, post_modlog.len());
let mod_removed_posts = post_modlog
.iter()
.map(|p| p.mod_remove_post.removed)
.collect::<Vec<bool>>();
assert_eq!(vec![true, true], mod_removed_posts);
let removed_posts = post_modlog
.iter()
.map(|p| p.post.removed)
.collect::<Vec<bool>>();
assert_eq!(vec![true, true], removed_posts);
// Comments
let comment_modlog = ModRemoveCommentView::list(pool, params).await?;
assert_eq!(2, comment_modlog.len());
let mod_removed_comments = comment_modlog
.iter()
.map(|p| p.mod_remove_comment.removed)
.collect::<Vec<bool>>();
assert_eq!(vec![true, true], mod_removed_comments);
let removed_comments = comment_modlog
.iter()
.map(|p| p.comment.removed)
.collect::<Vec<bool>>();
assert_eq!(vec![true, true], removed_comments);
// Now restore the content, and make sure it got appended
remove_or_restore_user_data(
inserted_mod.id,
inserted_person.id,
false,
&Some("a restore reason".to_string()),
&context,
)
.await?;
// Posts
let post_modlog = ModRemovePostView::list(pool, params).await?;
assert_eq!(4, post_modlog.len());
let mod_restored_posts = post_modlog
.iter()
.map(|p| p.mod_remove_post.removed)
.collect::<Vec<bool>>();
assert_eq!(vec![false, false, true, true], mod_restored_posts);
let restored_posts = post_modlog
.iter()
.map(|p| p.post.removed)
.collect::<Vec<bool>>();
// All of these will be false, cause its the current state of the post
assert_eq!(vec![false, false, false, false], restored_posts);
// Comments
let comment_modlog = ModRemoveCommentView::list(pool, params).await?;
assert_eq!(4, comment_modlog.len());
let mod_restored_comments = comment_modlog
.iter()
.map(|p| p.mod_remove_comment.removed)
.collect::<Vec<bool>>();
assert_eq!(vec![false, false, true, true], mod_restored_comments);
let restored_comments = comment_modlog
.iter()
.map(|p| p.comment.removed)
.collect::<Vec<bool>>();
assert_eq!(vec![false, false, false, false], restored_comments);
Instance::delete(pool, inserted_instance.id).await?;
Ok(())
} }
} }

View file

@ -23,7 +23,7 @@ use anyhow::anyhow;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
utils::{remove_or_restore_user_data_in_community, remove_user_data}, utils::{remove_or_restore_user_data, remove_or_restore_user_data_in_community},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -160,6 +160,7 @@ impl ActivityHandler for BlockUser {
let mod_person = self.actor.dereference(context).await?; let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.dereference(context).await?; let blocked_person = self.object.dereference(context).await?;
let target = self.target.dereference(context).await?; let target = self.target.dereference(context).await?;
let reason = self.summary;
match target { match target {
SiteOrCommunity::Site(_site) => { SiteOrCommunity::Site(_site) => {
let blocked_person = Person::update( let blocked_person = Person::update(
@ -173,14 +174,15 @@ impl ActivityHandler for BlockUser {
) )
.await?; .await?;
if self.remove_data.unwrap_or(false) { if self.remove_data.unwrap_or(false) {
remove_user_data(blocked_person.id, context).await?; remove_or_restore_user_data(mod_person.id, blocked_person.id, true, &reason, context)
.await?;
} }
// write mod log // write mod log
let form = ModBanForm { let form = ModBanForm {
mod_person_id: mod_person.id, mod_person_id: mod_person.id,
other_person_id: blocked_person.id, other_person_id: blocked_person.id,
reason: self.summary, reason,
banned: Some(true), banned: Some(true),
expires, expires,
}; };
@ -207,8 +209,10 @@ impl ActivityHandler for BlockUser {
if self.remove_data.unwrap_or(false) { if self.remove_data.unwrap_or(false) {
remove_or_restore_user_data_in_community( remove_or_restore_user_data_in_community(
community.id, community.id,
mod_person.id,
blocked_person.id, blocked_person.id,
true, true,
&reason,
&mut context.pool(), &mut context.pool(),
) )
.await?; .await?;
@ -219,7 +223,7 @@ impl ActivityHandler for BlockUser {
mod_person_id: mod_person.id, mod_person_id: mod_person.id,
other_person_id: blocked_person.id, other_person_id: blocked_person.id,
community_id: community.id, community_id: community.id,
reason: self.summary, reason,
banned: Some(true), banned: Some(true),
expires, expires,
}; };

View file

@ -19,7 +19,7 @@ use activitypub_federation::{
}; };
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
utils::{remove_or_restore_user_data_in_community, restore_user_data}, utils::{remove_or_restore_user_data, remove_or_restore_user_data_in_community},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -120,7 +120,8 @@ impl ActivityHandler for UndoBlockUser {
.await?; .await?;
if self.restore_data.unwrap_or(false) { if self.restore_data.unwrap_or(false) {
restore_user_data(blocked_person.id, context).await?; remove_or_restore_user_data(mod_person.id, blocked_person.id, false, &None, context)
.await?;
} }
// write mod log // write mod log
@ -144,8 +145,10 @@ impl ActivityHandler for UndoBlockUser {
if self.restore_data.unwrap_or(false) { if self.restore_data.unwrap_or(false) {
remove_or_restore_user_data_in_community( remove_or_restore_user_data_in_community(
community.id, community.id,
mod_person.id,
blocked_person.id, blocked_person.id,
false, false,
&None,
&mut context.pool(), &mut context.pool(),
) )
.await?; .await?;

View file

@ -40,12 +40,12 @@ impl Comment {
pub async fn update_removed_for_creator( pub async fn update_removed_for_creator(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_creator_id: PersonId, for_creator_id: PersonId,
new_removed: bool, removed: bool,
) -> Result<Vec<Self>, Error> { ) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::update(comment::table.filter(comment::creator_id.eq(for_creator_id))) diesel::update(comment::table.filter(comment::creator_id.eq(for_creator_id)))
.set(( .set((
comment::removed.eq(new_removed), comment::removed.eq(removed),
comment::updated.eq(naive_now()), comment::updated.eq(naive_now()),
)) ))
.get_results::<Self>(conn) .get_results::<Self>(conn)

View file

@ -66,6 +66,20 @@ impl Crud for ModRemovePost {
} }
} }
impl ModRemovePost {
pub async fn create_multiple(
pool: &mut DbPool<'_>,
forms: &Vec<ModRemovePostForm>,
) -> Result<usize, Error> {
use crate::schema::mod_remove_post::dsl::mod_remove_post;
let conn = &mut get_conn(pool).await?;
insert_into(mod_remove_post)
.values(forms)
.execute(conn)
.await
}
}
#[async_trait] #[async_trait]
impl Crud for ModLockPost { impl Crud for ModLockPost {
type InsertForm = ModLockPostForm; type InsertForm = ModLockPostForm;
@ -153,6 +167,20 @@ impl Crud for ModRemoveComment {
} }
} }
impl ModRemoveComment {
pub async fn create_multiple(
pool: &mut DbPool<'_>,
forms: &Vec<ModRemoveCommentForm>,
) -> Result<usize, Error> {
use crate::schema::mod_remove_comment::dsl::mod_remove_comment;
let conn = &mut get_conn(pool).await?;
insert_into(mod_remove_comment)
.values(forms)
.execute(conn)
.await
}
}
#[async_trait] #[async_trait]
impl Crud for ModRemoveCommunity { impl Crud for ModRemoveCommunity {
type InsertForm = ModRemoveCommunityForm; type InsertForm = ModRemoveCommunityForm;
@ -465,7 +493,6 @@ impl Crud for AdminPurgeComment {
} }
#[cfg(test)] #[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests { mod tests {
use crate::{ use crate::{
@ -499,26 +526,25 @@ mod tests {
traits::Crud, traits::Crud,
utils::build_db_pool_for_tests, utils::build_db_pool_for_tests,
}; };
use diesel::result::Error;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_crud() { async fn test_crud() -> Result<(), Error> {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
.await
.unwrap();
let new_mod = PersonInsertForm::test_form(inserted_instance.id, "the mod"); let new_mod = PersonInsertForm::test_form(inserted_instance.id, "the mod");
let inserted_mod = Person::create(pool, &new_mod).await.unwrap(); let inserted_mod = Person::create(pool, &new_mod).await?;
let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim2"); let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim2");
let inserted_person = Person::create(pool, &new_person).await.unwrap(); let inserted_person = Person::create(pool, &new_person).await?;
let new_community = CommunityInsertForm::new( let new_community = CommunityInsertForm::new(
inserted_instance.id, inserted_instance.id,
@ -527,21 +553,21 @@ mod tests {
"pubkey".to_string(), "pubkey".to_string(),
); );
let inserted_community = Community::create(pool, &new_community).await.unwrap(); let inserted_community = Community::create(pool, &new_community).await?;
let new_post = PostInsertForm::new( let new_post = PostInsertForm::new(
"A test post thweep".into(), "A test post thweep".into(),
inserted_person.id, inserted_person.id,
inserted_community.id, inserted_community.id,
); );
let inserted_post = Post::create(pool, &new_post).await.unwrap(); let inserted_post = Post::create(pool, &new_post).await?;
let comment_form = CommentInsertForm::new( let comment_form = CommentInsertForm::new(
inserted_person.id, inserted_person.id,
inserted_post.id, inserted_post.id,
"A test comment".into(), "A test comment".into(),
); );
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap(); let inserted_comment = Comment::create(pool, &comment_form, None).await?;
// Now the actual tests // Now the actual tests
@ -552,12 +578,8 @@ mod tests {
reason: None, reason: None,
removed: None, removed: None,
}; };
let inserted_mod_remove_post = ModRemovePost::create(pool, &mod_remove_post_form) let inserted_mod_remove_post = ModRemovePost::create(pool, &mod_remove_post_form).await?;
.await let read_mod_remove_post = ModRemovePost::read(pool, inserted_mod_remove_post.id).await?;
.unwrap();
let read_mod_remove_post = ModRemovePost::read(pool, inserted_mod_remove_post.id)
.await
.unwrap();
let expected_mod_remove_post = ModRemovePost { let expected_mod_remove_post = ModRemovePost {
id: inserted_mod_remove_post.id, id: inserted_mod_remove_post.id,
post_id: inserted_post.id, post_id: inserted_post.id,
@ -574,12 +596,8 @@ mod tests {
post_id: inserted_post.id, post_id: inserted_post.id,
locked: None, locked: None,
}; };
let inserted_mod_lock_post = ModLockPost::create(pool, &mod_lock_post_form) let inserted_mod_lock_post = ModLockPost::create(pool, &mod_lock_post_form).await?;
.await let read_mod_lock_post = ModLockPost::read(pool, inserted_mod_lock_post.id).await?;
.unwrap();
let read_mod_lock_post = ModLockPost::read(pool, inserted_mod_lock_post.id)
.await
.unwrap();
let expected_mod_lock_post = ModLockPost { let expected_mod_lock_post = ModLockPost {
id: inserted_mod_lock_post.id, id: inserted_mod_lock_post.id,
post_id: inserted_post.id, post_id: inserted_post.id,
@ -596,12 +614,8 @@ mod tests {
featured: false, featured: false,
is_featured_community: true, is_featured_community: true,
}; };
let inserted_mod_feature_post = ModFeaturePost::create(pool, &mod_feature_post_form) let inserted_mod_feature_post = ModFeaturePost::create(pool, &mod_feature_post_form).await?;
.await let read_mod_feature_post = ModFeaturePost::read(pool, inserted_mod_feature_post.id).await?;
.unwrap();
let read_mod_feature_post = ModFeaturePost::read(pool, inserted_mod_feature_post.id)
.await
.unwrap();
let expected_mod_feature_post = ModFeaturePost { let expected_mod_feature_post = ModFeaturePost {
id: inserted_mod_feature_post.id, id: inserted_mod_feature_post.id,
post_id: inserted_post.id, post_id: inserted_post.id,
@ -619,12 +633,10 @@ mod tests {
reason: None, reason: None,
removed: None, removed: None,
}; };
let inserted_mod_remove_comment = ModRemoveComment::create(pool, &mod_remove_comment_form) let inserted_mod_remove_comment =
.await ModRemoveComment::create(pool, &mod_remove_comment_form).await?;
.unwrap(); let read_mod_remove_comment =
let read_mod_remove_comment = ModRemoveComment::read(pool, inserted_mod_remove_comment.id) ModRemoveComment::read(pool, inserted_mod_remove_comment.id).await?;
.await
.unwrap();
let expected_mod_remove_comment = ModRemoveComment { let expected_mod_remove_comment = ModRemoveComment {
id: inserted_mod_remove_comment.id, id: inserted_mod_remove_comment.id,
comment_id: inserted_comment.id, comment_id: inserted_comment.id,
@ -643,13 +655,9 @@ mod tests {
removed: None, removed: None,
}; };
let inserted_mod_remove_community = let inserted_mod_remove_community =
ModRemoveCommunity::create(pool, &mod_remove_community_form) ModRemoveCommunity::create(pool, &mod_remove_community_form).await?;
.await
.unwrap();
let read_mod_remove_community = let read_mod_remove_community =
ModRemoveCommunity::read(pool, inserted_mod_remove_community.id) ModRemoveCommunity::read(pool, inserted_mod_remove_community.id).await?;
.await
.unwrap();
let expected_mod_remove_community = ModRemoveCommunity { let expected_mod_remove_community = ModRemoveCommunity {
id: inserted_mod_remove_community.id, id: inserted_mod_remove_community.id,
community_id: inserted_community.id, community_id: inserted_community.id,
@ -670,13 +678,9 @@ mod tests {
expires: None, expires: None,
}; };
let inserted_mod_ban_from_community = let inserted_mod_ban_from_community =
ModBanFromCommunity::create(pool, &mod_ban_from_community_form) ModBanFromCommunity::create(pool, &mod_ban_from_community_form).await?;
.await
.unwrap();
let read_mod_ban_from_community = let read_mod_ban_from_community =
ModBanFromCommunity::read(pool, inserted_mod_ban_from_community.id) ModBanFromCommunity::read(pool, inserted_mod_ban_from_community.id).await?;
.await
.unwrap();
let expected_mod_ban_from_community = ModBanFromCommunity { let expected_mod_ban_from_community = ModBanFromCommunity {
id: inserted_mod_ban_from_community.id, id: inserted_mod_ban_from_community.id,
community_id: inserted_community.id, community_id: inserted_community.id,
@ -697,8 +701,8 @@ mod tests {
banned: None, banned: None,
expires: None, expires: None,
}; };
let inserted_mod_ban = ModBan::create(pool, &mod_ban_form).await.unwrap(); let inserted_mod_ban = ModBan::create(pool, &mod_ban_form).await?;
let read_mod_ban = ModBan::read(pool, inserted_mod_ban.id).await.unwrap(); let read_mod_ban = ModBan::read(pool, inserted_mod_ban.id).await?;
let expected_mod_ban = ModBan { let expected_mod_ban = ModBan {
id: inserted_mod_ban.id, id: inserted_mod_ban.id,
mod_person_id: inserted_mod.id, mod_person_id: inserted_mod.id,
@ -717,12 +721,8 @@ mod tests {
community_id: inserted_community.id, community_id: inserted_community.id,
removed: None, removed: None,
}; };
let inserted_mod_add_community = ModAddCommunity::create(pool, &mod_add_community_form) let inserted_mod_add_community = ModAddCommunity::create(pool, &mod_add_community_form).await?;
.await let read_mod_add_community = ModAddCommunity::read(pool, inserted_mod_add_community.id).await?;
.unwrap();
let read_mod_add_community = ModAddCommunity::read(pool, inserted_mod_add_community.id)
.await
.unwrap();
let expected_mod_add_community = ModAddCommunity { let expected_mod_add_community = ModAddCommunity {
id: inserted_mod_add_community.id, id: inserted_mod_add_community.id,
community_id: inserted_community.id, community_id: inserted_community.id,
@ -739,8 +739,8 @@ mod tests {
other_person_id: inserted_person.id, other_person_id: inserted_person.id,
removed: None, removed: None,
}; };
let inserted_mod_add = ModAdd::create(pool, &mod_add_form).await.unwrap(); let inserted_mod_add = ModAdd::create(pool, &mod_add_form).await?;
let read_mod_add = ModAdd::read(pool, inserted_mod_add.id).await.unwrap(); let read_mod_add = ModAdd::read(pool, inserted_mod_add.id).await?;
let expected_mod_add = ModAdd { let expected_mod_add = ModAdd {
id: inserted_mod_add.id, id: inserted_mod_add.id,
mod_person_id: inserted_mod.id, mod_person_id: inserted_mod.id,
@ -749,14 +749,12 @@ mod tests {
when_: inserted_mod_add.when_, when_: inserted_mod_add.when_,
}; };
Comment::delete(pool, inserted_comment.id).await.unwrap(); Comment::delete(pool, inserted_comment.id).await?;
Post::delete(pool, inserted_post.id).await.unwrap(); Post::delete(pool, inserted_post.id).await?;
Community::delete(pool, inserted_community.id) Community::delete(pool, inserted_community.id).await?;
.await Person::delete(pool, inserted_person.id).await?;
.unwrap(); Person::delete(pool, inserted_mod.id).await?;
Person::delete(pool, inserted_person.id).await.unwrap(); Instance::delete(pool, inserted_instance.id).await?;
Person::delete(pool, inserted_mod.id).await.unwrap();
Instance::delete(pool, inserted_instance.id).await.unwrap();
assert_eq!(expected_mod_remove_post, read_mod_remove_post); assert_eq!(expected_mod_remove_post, read_mod_remove_post);
assert_eq!(expected_mod_lock_post, read_mod_lock_post); assert_eq!(expected_mod_lock_post, read_mod_lock_post);
@ -767,5 +765,7 @@ mod tests {
assert_eq!(expected_mod_ban, read_mod_ban); assert_eq!(expected_mod_ban, read_mod_ban);
assert_eq!(expected_mod_add_community, read_mod_add_community); assert_eq!(expected_mod_add_community, read_mod_add_community);
assert_eq!(expected_mod_add, read_mod_add); assert_eq!(expected_mod_add, read_mod_add);
Ok(())
} }
} }

View file

@ -145,7 +145,7 @@ impl Post {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_creator_id: PersonId, for_creator_id: PersonId,
for_community_id: Option<CommunityId>, for_community_id: Option<CommunityId>,
new_removed: bool, removed: bool,
) -> Result<Vec<Self>, Error> { ) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
@ -157,7 +157,7 @@ impl Post {
} }
update update
.set((post::removed.eq(new_removed), post::updated.eq(naive_now()))) .set((post::removed.eq(removed), post::updated.eq(naive_now())))
.get_results::<Self>(conn) .get_results::<Self>(conn)
.await .await
} }