diff --git a/src/download.rs b/src/download.rs index e64c26d4e..3a7113ef0 100644 --- a/src/download.rs +++ b/src/download.rs @@ -80,12 +80,46 @@ impl Context { let placeholder = Message::load_from_db(self, placeholder_msg_id).await?; self.sql .transaction(move |transaction| { - transaction - .execute("DELETE FROM msgs WHERE id=?;", paramsv![placeholder_msg_id])?; + // Move all the data from full message to placeholder. + // `id` stays the same, so foreign key constraints are not violated. + // For example, `reactions.msg_id` foreign key keeps pointing + // to the same message. transaction.execute( - "UPDATE msgs SET id=? WHERE id=?", + "UPDATE msgs + SET + rfc724_mid=full.rfc724_mid, + chat_id=full.chat_id, + from_id=full.from_id, + to_id=full.to_id, + timestamp=full.timestamp, + type=full.type, + state=full.state, + msgrmsg=full.msgrmsg, + bytes=full.bytes, + txt=full.txt, + txt_raw=full.txt_raw, + param=full.param, + starred=full.starred, + timestamp_sent=full.timestamp_sent, + timestamp_rcvd=full.timestamp_rcvd, + hidden=full.hidden, + mime_headers=full.mime_headers, + mime_in_reply_to=full.mime_in_reply_to, + mime_references=full.mime_references, + move_state=full.move_state, + location_id=full.location_id, + error=full.error, + ephemeral_timer=full.ephemeral_timer, + ephemeral_timestamp=full.ephemeral_timestamp, + mime_modified=full.mime_modified, + subject=full.subject, + download_state=full.download_state, + hop_info=full.hop_info + FROM msgs AS full + WHERE msgs.id=?1 AND full.id=?2", paramsv![placeholder_msg_id, full_msg_id], )?; + transaction.execute("DELETE FROM msgs WHERE id=?;", paramsv![full_msg_id])?; Ok(()) }) .await?; diff --git a/src/reaction.rs b/src/reaction.rs index b00824ba9..f463e3f00 100644 --- a/src/reaction.rs +++ b/src/reaction.rs @@ -291,8 +291,9 @@ mod tests { use crate::config::Config; use crate::constants::DC_CHAT_ID_TRASH; use crate::contact::{Contact, Origin}; + use crate::download::DownloadState; use crate::message::MessageState; - use crate::receive_imf::receive_imf; + use crate::receive_imf::{receive_imf, receive_imf_inner}; use crate::test_utils::TestContext; #[test] @@ -478,4 +479,73 @@ Content-Disposition: reaction\n\ Ok(()) } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_partial_download_and_reaction() -> Result<()> { + let alice = TestContext::new_alice().await; + let bob = TestContext::new_bob().await; + + alice + .create_chat_with_contact("Bob", "bob@example.net") + .await; + + let msg_header = "From: Bob \n\ + To: Alice \n\ + Chat-Version: 1.0\n\ + Subject: subject\n\ + Message-ID: \n\ + Date: Sun, 14 Nov 2021 00:10:00 +0000\ + Content-Type: text/plain"; + let msg_full = format!("{}\n\n100k text...", msg_header); + + // Alice downloads message from Bob partially. + let alice_received_message = receive_imf_inner( + &alice, + "first@example.org", + msg_header.as_bytes(), + false, + Some(100000), + false, + ) + .await? + .unwrap(); + let alice_msg_id = *alice_received_message.msg_ids.get(0).unwrap(); + + // Bob downloads own message on the other device. + let bob_received_message = receive_imf(&bob, msg_full.as_bytes(), false) + .await? + .unwrap(); + let bob_msg_id = *bob_received_message.msg_ids.get(0).unwrap(); + + // Bob reacts to own message. + send_reaction(&bob, bob_msg_id, "👍").await.unwrap(); + let bob_reaction_msg = bob.pop_sent_msg().await; + + // Alice receives a reaction. + alice.recv_msg_opt(&bob_reaction_msg).await.unwrap(); + + let reactions = get_msg_reactions(&alice, alice_msg_id).await?; + assert_eq!(reactions.to_string(), "👍1"); + let msg = Message::load_from_db(&alice, alice_msg_id).await?; + assert_eq!(msg.download_state(), DownloadState::Available); + + // Alice downloads full message. + receive_imf_inner( + &alice, + "first@example.org", + msg_full.as_bytes(), + false, + None, + false, + ) + .await?; + + // Check that reaction is still on the message after full download. + let msg = Message::load_from_db(&alice, alice_msg_id).await?; + assert_eq!(msg.download_state(), DownloadState::Done); + let reactions = get_msg_reactions(&alice, alice_msg_id).await?; + assert_eq!(reactions.to_string(), "👍1"); + + Ok(()) + } }