diff --git a/src/chat/chat_tests.rs b/src/chat/chat_tests.rs index b3b6fcbefd..813d290124 100644 --- a/src/chat/chat_tests.rs +++ b/src/chat/chat_tests.rs @@ -3049,6 +3049,31 @@ async fn test_broadcast_resend_to_new_member() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_broadcast_resend_failed_msg_to_new_member() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + let fiona = &tcm.fiona().await; + let alice_bc_id = create_broadcast(alice, "bc".to_string()).await?; + let qr = get_securejoin_qr(alice, Some(alice_bc_id)).await.unwrap(); + + tcm.exec_securejoin_qr(bob, alice, &qr).await; + let alice_msg_id = alice.send_text(alice_bc_id, "text").await.sender_msg_id; + let mut msg = Message::load_from_db(alice, alice_msg_id).await?; + message::set_msg_failed(alice, &mut msg, "error").await?; + let fiona_bc_id = tcm.exec_securejoin_qr(fiona, alice, &qr).await; + let resent_msg = alice.pop_sent_msg().await; + let fiona_msg = fiona.recv_msg(&resent_msg).await; + assert_eq!(fiona_msg.chat_id, fiona_bc_id); + assert_eq!(fiona_msg.text, "text"); + assert_eq!( + alice_msg_id.get_state(alice).await?, + MessageState::OutFailed + ); + Ok(()) +} + /// - Alice has multiple devices /// - Alice creates a broadcast and sends a message into it /// - Alice's second device sees the broadcast diff --git a/src/message.rs b/src/message.rs index 9ece2cd2e4..684ed30ee1 100644 --- a/src/message.rs +++ b/src/message.rs @@ -140,8 +140,20 @@ SELECT ?1, rfc724_mid, pre_rfc724_mid, timestamp, ?, ? FROM msgs WHERE id=?1 Ok(()) } - pub(crate) async fn set_delivered(self, context: &Context) -> Result<()> { - update_msg_state(context, self, MessageState::OutDelivered).await?; + /// Returns whether the message state is updated to `OutDelivered`. + pub(crate) async fn set_delivered(self, context: &Context) -> Result { + if context + .sql + .execute( + // Only update `OutPending` i.e. if the message is (re-)sent to all chat members. + "UPDATE msgs SET state=?, error='' WHERE id=? AND state=?", + (MessageState::OutDelivered, self, MessageState::OutPending), + ) + .await? + == 0 + { + return Ok(false); + } let chat_id: Option = context .sql .query_get_value("SELECT chat_id FROM msgs WHERE id=?", (self,)) @@ -153,7 +165,7 @@ SELECT ?1, rfc724_mid, pre_rfc724_mid, timestamp, ?, ? FROM msgs WHERE id=?1 if let Some(chat_id) = chat_id { chatlist_events::emit_chatlist_item_changed(context, chat_id); } - Ok(()) + Ok(true) } /// Bad evil escape hatch. @@ -1414,6 +1426,9 @@ pub enum MessageState { /// The user has pressed the "send" button but the message is not /// yet sent and is pending in some way. Maybe we're offline (no /// checkmark). + /// + /// This state means that the message is being (re-)sent to all chat members. It shalln't be + /// used e.g. for resending only to a new broadcast member. OutPending = 20, /// *Unrecoverable* error (*recoverable* errors result in pending diff --git a/src/test_utils.rs b/src/test_utils.rs index 20d8864861..28c4082c9f 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -38,7 +38,7 @@ use crate::events::{Event, EventEmitter, EventType, Events}; use crate::key::{self, DcKey, self_fingerprint}; use crate::log::warn; use crate::login_param::EnteredLoginParam; -use crate::message::{Message, MessageState, MsgId, update_msg_state}; +use crate::message::{Message, MessageState, MsgId}; use crate::mimeparser::{MimeMessage, SystemMessage}; use crate::pgp::SeipdVersion; use crate::receive_imf::{ReceivedMsg, receive_imf}; @@ -692,10 +692,11 @@ ORDER BY id" if !msg_has_pending_smtp_job(self, msg_id) .await .expect("Failed to check for more jobs") - { - update_msg_state(&self.ctx, msg_id, MessageState::OutDelivered) + && msg_id + .set_delivered(self) .await - .expect("failed to update message state"); + .expect("MsgId::set_delivered") + { self.sql .execute( "UPDATE msgs SET timestamp_sent=? WHERE id=?", @@ -772,16 +773,19 @@ ORDER BY id" .execute("DELETE FROM smtp WHERE msg_id=?", (msg_id,)) .await .expect("Delete smtp jobs"); - update_msg_state(&self.ctx, msg_id, MessageState::OutDelivered) + if msg_id + .set_delivered(self) .await - .expect("Update message state"); - self.sql - .execute( - "UPDATE msgs SET timestamp_sent=? WHERE id=?", - (time(), msg_id), - ) - .await - .expect("Update timestamp_sent"); + .expect("MsgId::set_delivered") + { + self.sql + .execute( + "UPDATE msgs SET timestamp_sent=? WHERE id=?", + (time(), msg_id), + ) + .await + .expect("Update timestamp_sent"); + } sent_msgs }