From 68341e72b6d471e5dd40569828576bedcc3c3919 Mon Sep 17 00:00:00 2001 From: Marco Groot <60631182+marcogroot@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:14:06 +1000 Subject: [PATCH] Clarify error message when attempting to delete channel with active participants (#54146) Previous error message when trying to delete channel with active participants in call: image After change (tested locally) image Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #ISSUE https://github.com/zed-industries/zed/issues/53572 Release Notes: - N/A or Added/Fixed/Improved ... Added a clear error message upon trying to delete channel with active participants --- crates/collab/src/db/queries/channels.rs | 11 ++++ .../integration/db_tests/channel_tests.rs | 65 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/crates/collab/src/db/queries/channels.rs b/crates/collab/src/db/queries/channels.rs index 8e783f42a86f3810a811d6d4495b237b5285ded0..7262a5fa40ff7baf530359eb6bb690a8ae100ef2 100644 --- a/crates/collab/src/db/queries/channels.rs +++ b/crates/collab/src/db/queries/channels.rs @@ -261,6 +261,17 @@ impl Database { .chain(Some(channel_id)) .collect::>(); + let channel_has_active_participants = room_participant::Entity::find() + .inner_join(room::Entity) + .filter(room::Column::ChannelId.is_in(channels_to_remove.iter().copied())) + .count(&*tx) + .await? + > 0; + + if channel_has_active_participants { + Err(anyhow!("can't delete channel while a call is in progress"))?; + } + channel::Entity::delete_many() .filter(channel::Column::Id.is_in(channels_to_remove.iter().copied())) .exec(&*tx) diff --git a/crates/collab/tests/integration/db_tests/channel_tests.rs b/crates/collab/tests/integration/db_tests/channel_tests.rs index fc3f8770f839b24c2be34919e883a03c34b14af6..c78fe0f4ef71f79ed4735372874934cbb851df66 100644 --- a/crates/collab/tests/integration/db_tests/channel_tests.rs +++ b/crates/collab/tests/integration/db_tests/channel_tests.rs @@ -976,3 +976,68 @@ fn assert_channel_tree_order(actual: Vec, expected: &[(ChannelId, &[Cha .collect::>(); pretty_assertions::assert_eq!(actual, expected, "wrong channel ids and parent paths"); } + +test_both_dbs!( + test_delete_channel_with_active_call, + test_delete_channel_with_active_call_postgres, + test_delete_channel_with_active_call_sqlite +); + +async fn test_delete_channel_with_active_call(db: &Arc) { + let owner_id = db.create_server("test").await.unwrap().0 as u32; + + let user_1 = new_test_user(db, "user1@example.com").await; + let user_2 = new_test_user(db, "user2@example.com").await; + + let parent_channel_id = db + .create_root_channel("parent_channel", user_1) + .await + .unwrap(); + let nested_channel_id = db + .create_sub_channel("nested_channel", parent_channel_id, user_1) + .await + .unwrap(); + + db.invite_channel_member(parent_channel_id, user_2, user_1, ChannelRole::Member) + .await + .unwrap(); + + db.respond_to_channel_invite(parent_channel_id, user_2, true) + .await + .unwrap(); + + let connection_1 = ConnectionId { owner_id, id: 1 }; + let connection_2 = ConnectionId { owner_id, id: 2 }; + + db.join_channel(parent_channel_id, user_1, connection_1) + .await + .unwrap(); + + db.join_channel(nested_channel_id, user_2, connection_2) + .await + .unwrap(); + + // Delete fails - participants in both parent and nested calls + let err = db + .delete_channel(parent_channel_id, user_1) + .await + .unwrap_err() + .to_string(); + assert!(err.contains("call is in progress"), "{err}"); + + // Delete fails - participants in nested calls + db.leave_room(connection_2).await.unwrap(); + let err = db + .delete_channel(parent_channel_id, user_1) + .await + .unwrap_err() + .to_string(); + assert!(err.contains("call is in progress"), "{err}"); + + // Delete succeeds - no participants in calls + db.leave_room(connection_1).await.unwrap(); + db.delete_channel(parent_channel_id, user_1).await.unwrap(); + + assert!(db.get_channel(parent_channel_id, user_1).await.is_err()); + assert!(db.get_channel(parent_channel_id, user_2).await.is_err()); +}