test: MDN on pre-message has effect if received before sending full message (#8004)

Actually, the problem in #8004 is that a pre-message doesn't "want MDN" if it has no text. Anyway,
the added test reproduces the bug.
This commit is contained in:
iequidoo
2026-03-18 10:46:46 -03:00
parent 8c006caeb2
commit d2f33f57e2
6 changed files with 96 additions and 6 deletions

View File

@@ -427,6 +427,10 @@ pub enum Config {
/// storing the same token multiple times on the server.
EncryptedDeviceToken,
/// Make `TestContext::pop_sent_msg_opt()` and related functions pop messages from the `smtp`
/// head, i.e. make it a queue. For historical reasons the default is a stack.
PopSentMsgFromHead,
/// Enables running test hooks, e.g. see `InnerContext::pre_encrypt_mime_hook`.
/// This way is better than conditional compilation, i.e. `#[cfg(test)]`, because tests not
/// using this still run unmodified code.

View File

@@ -1084,6 +1084,13 @@ impl Context {
.await?
.to_string(),
);
res.insert(
"pop_sent_msg_from_head",
self.sql
.get_raw_config("pop_sent_msg_from_head")
.await?
.unwrap_or_default(),
);
res.insert(
"test_hooks",
self.sql

View File

@@ -1904,9 +1904,10 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
//
// We also don't send read receipts for contact requests.
// Read receipts will not be sent even after accepting the chat.
let wants_mdn = curr_param.get_bool(Param::WantsMdn).unwrap_or_default();
let to_id = if curr_blocked == Blocked::Not
&& !curr_hidden
&& curr_param.get_bool(Param::WantsMdn).unwrap_or_default()
&& wants_mdn
&& curr_param.get_cmd() == SystemMessage::Unknown
&& context.should_send_mdns().await?
{
@@ -1928,6 +1929,11 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
None
};
if let Some(to_id) = to_id {
info!(
context,
"Queuing MDN to {to_id} for {id} from {curr_from_id}, wants_mdn={wants_mdn}, cmd={}.",
curr_param.get_cmd()
);
context
.sql
.execute(

View File

@@ -2501,6 +2501,8 @@ async fn handle_mdn(
let Some((msg_id, chat_id, has_mdns, is_dup)) = context
.sql
.query_row_optional(
// MDN on a pre-message references the post-message, see `receive_imf`. So we can't tell
// which one was seen, but this is on purpose.
"SELECT
m.id AS msg_id,
c.id AS chat_id,

View File

@@ -624,16 +624,25 @@ impl TestContext {
}
pub async fn pop_sent_msg_opt(&self, timeout: Duration) -> Option<SentMessage<'_>> {
let from_head = self
.get_config_bool(Config::PopSentMsgFromHead)
.await
.unwrap();
let order_subst = match from_head {
true => "",
false => " DESC",
};
let start = Instant::now();
let (rowid, msg_id, payload, recipients) = loop {
let row = self
.ctx
.sql
.query_row_optional(
r#"
SELECT id, msg_id, mime, recipients
FROM smtp
ORDER BY id DESC"#,
&format!(
"SELECT id, msg_id, mime, recipients
FROM smtp
ORDER BY id{order_subst}"
),
(),
|row| {
let rowid: i64 = row.get(0)?;

View File

@@ -4,6 +4,7 @@ use pretty_assertions::assert_eq;
use crate::EventType;
use crate::chat;
use crate::config::Config;
use crate::contact;
use crate::download::{DownloadState, PRE_MSG_ATTACHMENT_SIZE_THRESHOLD, PostMsgMetadata};
use crate::message::{Message, MessageState, Viewtype, delete_msgs, markseen_msgs};
@@ -253,6 +254,64 @@ async fn test_lost_pre_msg() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pre_msg_mdn_before_sending_full() -> Result<()> {
pre_msg_mdn_before_sending_full("").await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pre_msg_mdn_before_sending_full_with_text() -> Result<()> {
pre_msg_mdn_before_sending_full("text").await
}
async fn pre_msg_mdn_before_sending_full(text: &str) -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
alice
.set_config_bool(Config::PopSentMsgFromHead, true)
.await?;
let bob = &tcm.bob().await;
let alice_chat_id = alice.create_group_with_members("", &[bob]).await;
let file_bytes = include_bytes!("../../../test-data/image/screenshot.gif");
let mut msg = Message::new(Viewtype::Image);
msg.set_file_from_bytes(alice, "a.jpg", file_bytes, None)?;
msg.set_text(text.to_string());
let pre_msg = alice.send_msg(alice_chat_id, &mut msg).await;
let alice_msg_id = msg.id;
let msg = bob.recv_msg(&pre_msg).await;
assert_eq!(msg.download_state, DownloadState::Available);
assert_eq!(msg.id.get_state(bob).await?, MessageState::InFresh);
assert_eq!(msg.text, text);
assert!(msg.param.get_bool(Param::WantsMdn).unwrap_or_default());
msg.chat_id.accept(bob).await?;
markseen_msgs(bob, vec![msg.id]).await?;
assert_eq!(msg.id.get_state(bob).await?, MessageState::InSeen);
assert_eq!(
bob.sql
.count(
"SELECT COUNT(*) FROM smtp_mdns WHERE from_id=?",
(msg.from_id,)
)
.await?,
1
);
smtp::queue_mdn(bob).await?;
alice.recv_msg_trash(&bob.pop_sent_msg().await).await;
assert_eq!(
alice_msg_id.get_state(alice).await?,
MessageState::OutPending
);
let _full_msg = alice.pop_sent_msg().await;
assert_eq!(
alice_msg_id.get_state(alice).await?,
MessageState::OutMdnRcvd
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_post_msg_bad_sender() -> Result<()> {
let mut tcm = TestContextManager::new();
@@ -539,7 +598,10 @@ async fn test_markseen_pre_msg() -> Result<()> {
assert_eq!(
alice
.sql
.count("SELECT COUNT(*) FROM smtp_mdns", ())
.count(
"SELECT COUNT(*) FROM smtp_mdns WHERE from_id=?",
(msg.from_id,)
)
.await?,
1
);