diff --git a/src/chat.rs b/src/chat.rs index f8bfb62b1..163ce9bb9 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -5301,8 +5301,8 @@ mod tests { // Bob receives the add and deletion messages out of order let bob = TestContext::new_bob().await; bob.recv_msg(&add1).await; - bob.recv_msg(&add3).await; - let bob_chat_id = bob.recv_msg(&add2).await.chat_id; + let bob_chat_id = bob.recv_msg(&add3).await.chat_id; + bob.recv_msg_trash(&add2).await; // No-op addition message is trashed. assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 4); bob.recv_msg(&remove2).await; diff --git a/src/receive_imf.rs b/src/receive_imf.rs index d2f8e69f9..894e2ba2a 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -1528,6 +1528,9 @@ async fn add_parts( let mut txt_raw = "".to_string(); let (msg, typ): (&str, Viewtype) = if let Some(better_msg) = &better_msg { + if better_msg.is_empty() && is_partial_download.is_none() { + chat_id = DC_CHAT_ID_TRASH; + } (better_msg, Viewtype::Text) } else { (&part.msg, part.typ) @@ -2197,7 +2200,8 @@ async fn update_chats_contacts_timestamps( /// Apply group member list, name, avatar and protection status changes from the MIME message. /// /// Returns `Vec` of group changes messages and, optionally, a better message to replace the -/// original system message. +/// original system message. If the better message is empty, the original system message +/// should be trashed. /// /// * `to_ids` - contents of the `To` and `Cc` headers. /// * `past_ids` - contents of the `Chat-Group-Past-Members` header. @@ -2394,7 +2398,12 @@ async fn apply_group_changes( } if let Some(added_id) = added_id { - added_ids.remove(&added_id); + if !added_ids.remove(&added_id) && !self_added { + // No-op "Member added" message. + // + // Trash it. + better_msg = Some(String::new()); + } } if let Some(removed_id) = removed_id { removed_ids.remove(&removed_id); diff --git a/src/receive_imf/tests.rs b/src/receive_imf/tests.rs index 60bbeb680..76cef08e6 100644 --- a/src/receive_imf/tests.rs +++ b/src/receive_imf/tests.rs @@ -5049,6 +5049,32 @@ async fn test_unarchive_on_member_removal() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_no_op_member_added_is_trash() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + let alice_chat_id = alice + .create_group_with_members(ProtectionStatus::Unprotected, "foos", &[bob]) + .await; + send_text_msg(alice, alice_chat_id, "populate".to_string()).await?; + let msg = alice.pop_sent_msg().await; + bob.recv_msg(&msg).await; + let bob_chat_id = bob.get_last_msg().await.chat_id; + bob_chat_id.accept(bob).await?; + + let fiona_id = Contact::create(alice, "", "fiona@example.net").await?; + add_contact_to_chat(alice, alice_chat_id, fiona_id).await?; + let msg = alice.pop_sent_msg().await; + + let fiona_id = Contact::create(bob, "", "fiona@example.net").await?; + add_contact_to_chat(bob, bob_chat_id, fiona_id).await?; + bob.recv_msg_trash(&msg).await; + let contacts = get_chat_contacts(bob, bob_chat_id).await?; + assert_eq!(contacts.len(), 3); + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_forged_from() -> Result<()> { let mut tcm = TestContextManager::new();