Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/reaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::fmt;

use anyhow::{Result, bail};
use anyhow::{Result, bail, ensure};
use serde::{Deserialize, Serialize};

use crate::chat::{Chat, ChatId, send_msg};
Expand Down Expand Up @@ -246,9 +246,12 @@ pub async fn send_reaction(context: &Context, msg_id: MsgId, reaction: &str) ->
let chat_id = msg.chat_id;
let chat = Chat::load_from_db(context, chat_id).await?;

if matches!(chat.typ, Chattype::OutBroadcast | Chattype::InBroadcast)
&& !is_allowed_reaction(&reaction)
{
ensure!(
chat.typ != Chattype::OutBroadcast,
"Cannot react to own messages in a channel"
);

if chat.typ == Chattype::InBroadcast && !is_allowed_reaction(&reaction) {
bail!("Reaction not allowed: {}", reaction.as_str());
}

Expand Down
46 changes: 6 additions & 40 deletions src/reaction/broadcast_reactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,33 +721,19 @@ mod tests {
Ok(())
}

/// Tests that channel owner cannot react to own messages.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_broadcast_reaction_resent_to_new_member() -> Result<()> {
async fn test_broadcast_owner_cannot_react_to_self() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;

// Alice creates a broadcast channel, sends a message and reacts to her own message
// Alice creates a broadcast channel, sends a message and tries to react to her own message.
let alice_chat_id = create_broadcast(alice, "Channel".to_string()).await?;
let qr = get_securejoin_qr(alice, Some(alice_chat_id)).await.unwrap();
let alice_msg_id = alice
.send_text(alice_chat_id, "hi channel!")
.await
.sender_msg_id;
send_reaction(alice, alice_msg_id, "👍").await?;
alice.pop_sent_msg().await;
let reactions = get_msg_reactions(alice, alice_msg_id).await?;
assert_eq!(reactions.to_string(), "👍1");

// Bob joins the channel via QR code, receives the resent message, together with the reaction.
let bob_chat_id = tcm.exec_securejoin_qr(bob, alice, &qr).await;
let sent_msg = alice.pop_sent_msg().await;
let bob_msg = bob.recv_msg(&sent_msg).await;
let reactions = get_msg_reactions(bob, bob_msg.id).await?;
assert_eq!(bob_msg.chat_id, bob_chat_id);
assert_eq!(bob_msg.get_text(), "hi channel!");
assert_eq!(reactions.to_string(), "👍1");
assert_eq!(reactions.frequencies.len(), 1);
assert!(send_reaction(alice, alice_msg_id, "👍").await.is_err());

Ok(())
}
Expand All @@ -774,42 +760,22 @@ mod tests {

tcm.section("Alice1 posts, Alice2 and Bob receive the post");
let sent_msg = alice1.send_text(alice1_chat_id, "hi channel!").await;
let alice1_msg_id = sent_msg.load_from_db().await.id;
let alice2_msg = alice2.recv_msg(&sent_msg).await;
alice2.recv_msg(&sent_msg).await;
let bob_msg = bob.recv_msg(&sent_msg).await;

tcm.section("Alice2 reacts to her own post, Alice1 learns about it");
send_reaction(alice2, alice2_msg.id, "👍").await?;
alice1.recv_msg_hidden(&alice2.pop_sent_msg().await).await;
assert_eq!(
get_msg_reactions(alice1, alice1_msg_id).await?.to_string(),
"👍1"
);
broadcast_reactions_for_all_chats(alice2).await?; // bypass timer
let summary = alice2.pop_sent_msg().await;
alice1.recv_msg_hidden(&summary).await;
bob.recv_msg_hidden(&summary).await;

tcm.section("Bob reacts, Alice1 broadcasts the summary");
send_reaction(bob, bob_msg.id, "❤️").await?;
alice1.recv_msg_hidden(&bob.pop_sent_msg().await).await;
broadcast_reactions_for_all_chats(alice1).await?; // bypass timer
let summary = alice1.pop_sent_msg().await;
bob.recv_msg_hidden(&summary).await;
assert_eq!(
get_msg_reactions(bob, bob_msg.id).await?.to_string(),
"❤️1 👍1"
);
assert_eq!(get_msg_reactions(bob, bob_msg.id).await?.to_string(), "❤️1");

tcm.section("Alice2 receives Alice1's summary and must not broadcast it again");
alice2.recv_msg_hidden(&summary).await;
broadcast_reactions_for_all_chats(alice2).await?;
assert!(alice2.pop_sent_msg_opt().await.is_none());

tcm.section("The summary must not remove Alice's own reaction on Alice2");
let reactions = get_msg_reactions(alice2, alice2_msg.id).await?;
assert_eq!(reactions.to_string(), "👍1");

Ok(())
}

Expand Down
Loading