use assert_matches::assert_matches;
use assert_matches2::assert_let;
use eyeball_im::VectorDiff;
use imbl::vector;
use matrix_sdk_test::{ALICE, BOB, async_test};
use ruma::{
event_id,
events::{
StateEventContentChange, reaction::RedactedReactionEventContent,
room::message::OriginalSyncRoomMessageEvent,
},
owned_event_id,
};
use stream_assert::{assert_next_matches, assert_pending};
use super::TestTimeline;
use crate::timeline::{
AnyOtherStateEventContentChange, TimelineDetails, TimelineItemContent,
event_item::{EventTimelineItemKind, RemoteEventOrigin, RemoteEventTimelineItem},
};
#[async_test]
async fn test_redact_state_event() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe_events().await;
let f = &timeline.factory;
timeline.handle_live_event(f.room_name("Fancy room name").sender(&ALICE)).await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert_let!(TimelineItemContent::OtherState(state) = item.content());
assert_matches!(
state.content,
AnyOtherStateEventContentChange::RoomName(StateEventContentChange::Original { .. })
);
timeline.handle_live_event(f.redaction(item.event_id().unwrap()).sender(&ALICE)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert_let!(TimelineItemContent::OtherState(state) = item.content());
assert_matches!(
state.content,
AnyOtherStateEventContentChange::RoomName(StateEventContentChange::Redacted(_))
);
}
#[async_test]
async fn test_redact_replied_to_event() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe_events().await;
let f = &timeline.factory;
timeline.handle_live_event(f.text_msg("Hello, world!").sender(&ALICE)).await;
let first_item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert!(first_item.content().is_message());
let first_event: OriginalSyncRoomMessageEvent =
first_item.original_json().unwrap().deserialize_as_unchecked().unwrap();
timeline
.handle_live_event(f.text_msg("Hello, alice.").sender(&BOB).reply_to(&first_event.event_id))
.await;
let second_item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
let msglike = second_item.content().as_msglike().unwrap();
let in_reply_to = msglike.in_reply_to.clone().unwrap();
assert_let!(TimelineDetails::Ready(replied_to_event) = &in_reply_to.event);
assert!(replied_to_event.content.is_message());
timeline.handle_live_event(f.redaction(first_item.event_id().unwrap()).sender(&ALICE)).await;
let first_item_again =
assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert!(first_item_again.content().is_redacted());
assert_matches!(first_item_again.original_json(), None);
let second_item_again =
assert_next_matches!(stream, VectorDiff::Set { index: 1, value } => value);
let msglike = second_item_again.content().as_msglike().unwrap();
let in_reply_to = msglike.in_reply_to.clone().unwrap();
assert_let!(TimelineDetails::Ready(replied_to_event) = &in_reply_to.event);
assert!(replied_to_event.content.is_redacted());
}
#[async_test]
async fn test_redaction_before_event() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe_events().await;
let f = &timeline.factory;
let target_event_id = event_id!("$target");
timeline.handle_live_event(f.reaction(target_event_id, "😀").sender(&ALICE)).await;
timeline.handle_live_event(f.redaction(target_event_id).sender(&ALICE)).await;
timeline.handle_live_event(f.reaction(target_event_id, "👍").sender(&ALICE)).await;
assert_pending!(stream);
timeline
.handle_live_event(f.text_msg("Hello, world!").event_id(target_event_id).sender(&ALICE))
.await;
let first_item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert!(first_item.content().is_redacted());
assert_matches!(first_item.original_json(), None);
assert_matches!(first_item.latest_edit_json(), None);
assert!(first_item.content().reactions().cloned().unwrap_or_default().is_empty());
}
#[async_test]
async fn test_reaction_redaction() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe_events().await;
let f = &timeline.factory;
timeline.handle_live_event(f.text_msg("hi!").sender(&ALICE)).await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert_eq!(item.content().reactions().cloned().unwrap_or_default().len(), 0);
let msg_event_id = item.event_id().unwrap();
timeline.handle_live_event(f.reaction(msg_event_id, "+1").sender(&BOB)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert_eq!(item.content().reactions().cloned().unwrap_or_default().len(), 1);
let reaction_event_id = item.event_id().unwrap();
timeline.handle_live_event(f.redaction(reaction_event_id).sender(&BOB)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert_eq!(item.content().reactions().cloned().unwrap_or_default().len(), 0);
}
#[async_test]
async fn test_reaction_redaction_timeline_filter() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe_events().await;
let f = &timeline.factory;
timeline
.controller
.handle_remote_events_with_diffs(
vec![VectorDiff::Append {
values: vector![
f.redacted(*ALICE, RedactedReactionEventContent::new()).into_event()
],
}],
RemoteEventOrigin::Sync,
)
.await;
assert_eq!(timeline.controller.items().await.len(), 0);
timeline.handle_live_event(f.redacted(&ALICE, RedactedReactionEventContent::new())).await;
assert_eq!(timeline.controller.items().await.len(), 0);
timeline.handle_live_event(f.text_msg("hi!").sender(&ALICE)).await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert_eq!(timeline.controller.items().await.len(), 2);
timeline.handle_live_event(f.reaction(item.event_id().unwrap(), "+1").sender(&BOB)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
let reaction_event_id = item.event_id().unwrap();
assert_eq!(timeline.controller.items().await.len(), 2);
timeline.handle_live_event(f.redaction(reaction_event_id).sender(&BOB)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert_eq!(item.content().reactions().cloned().unwrap_or_default().len(), 0);
assert_eq!(timeline.controller.items().await.len(), 2);
}
#[async_test]
async fn test_local_and_remote_echo_of_redaction() {
let timeline = TestTimeline::new();
let mut stream = timeline.subscribe_events().await;
let f = &timeline.factory;
let event_id = owned_event_id!("$1");
timeline
.handle_live_event(f.text_msg("Hello, world!").sender(&ALICE).event_id(&event_id))
.await;
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert!(!item.content().is_redacted());
assert!(item.unredacted_item.is_none());
assert_let!(
EventTimelineItemKind::Remote(RemoteEventTimelineItem { original_json, .. }) = item.kind
);
assert!(original_json.is_some());
timeline.handle_local_redaction(event_id.clone()).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert!(item.content().is_redacted());
assert_let!(Some(unredacted_item) = item.unredacted_item);
assert!(unredacted_item.original_json.is_some());
assert_let!(
EventTimelineItemKind::Remote(RemoteEventTimelineItem { original_json, .. }) = item.kind
);
assert!(original_json.is_none());
timeline.handle_live_event(f.redaction(&event_id).sender(&ALICE)).await;
let item = assert_next_matches!(stream, VectorDiff::Set { index: 0, value } => value);
assert!(item.content().is_redacted());
assert!(item.unredacted_item.is_none());
assert_let!(
EventTimelineItemKind::Remote(RemoteEventTimelineItem { original_json, .. }) = item.kind
);
assert!(original_json.is_none());
}