1use ruma::{
18 EventId, UserId,
19 events::{
20 AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncTimelineEvent, Mentions,
21 SyncMessageLikeEvent,
22 poll::unstable_start::{
23 ReplacementUnstablePollStartEventContent, UnstablePollStartContentBlock,
24 UnstablePollStartEventContent,
25 },
26 room::message::{
27 FormattedBody, MessageType, Relation, ReplacementMetadata, RoomMessageEventContent,
28 RoomMessageEventContentWithoutRelation,
29 },
30 },
31};
32use thiserror::Error;
33use tracing::instrument;
34
35use super::EventSource;
36use crate::Room;
37
38pub enum EditedContent {
40 RoomMessage(RoomMessageEventContentWithoutRelation),
42
43 MediaCaption {
45 caption: Option<String>,
49
50 formatted_caption: Option<FormattedBody>,
54
55 mentions: Option<Mentions>,
58 },
59
60 PollStart {
62 fallback_text: String,
64 new_content: UnstablePollStartContentBlock,
66 },
67}
68
69#[cfg(not(tarpaulin_include))]
70impl std::fmt::Debug for EditedContent {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 match self {
73 Self::RoomMessage(_) => f.debug_tuple("RoomMessage").finish(),
74 Self::MediaCaption { .. } => f.debug_tuple("MediaCaption").finish(),
75 Self::PollStart { .. } => f.debug_tuple("PollStart").finish(),
76 }
77 }
78}
79
80#[derive(Debug, Error)]
82pub enum EditError {
83 #[error("State events can't be edited")]
85 StateEvent,
86
87 #[error("You're not the author of the event you'd like to edit.")]
90 NotAuthor,
91
92 #[error("Couldn't fetch the remote event: {0}")]
94 Fetch(Box<crate::Error>),
95
96 #[error(transparent)]
98 Deserialize(#[from] serde_json::Error),
99
100 #[error(
102 "The original event type ({target}) isn't the same as \
103 the parameter's new content type ({new_content})"
104 )]
105 IncompatibleEditType {
106 target: String,
108 new_content: &'static str,
110 },
111}
112
113impl Room {
114 #[instrument(skip(self, new_content), fields(room = %self.room_id()))]
119 pub async fn make_edit_event(
120 &self,
121 event_id: &EventId,
122 new_content: EditedContent,
123 ) -> Result<AnyMessageLikeEventContent, EditError> {
124 make_edit_event(self, self.own_user_id(), event_id, new_content).await
125 }
126}
127
128async fn make_edit_event<S: EventSource>(
129 source: S,
130 own_user_id: &UserId,
131 event_id: &EventId,
132 new_content: EditedContent,
133) -> Result<AnyMessageLikeEventContent, EditError> {
134 let target = source.get_event(event_id).await.map_err(|err| EditError::Fetch(Box::new(err)))?;
135
136 let event = target.raw().deserialize().map_err(EditError::Deserialize)?;
137
138 let AnySyncTimelineEvent::MessageLike(message_like_event) = event else {
140 return Err(EditError::StateEvent);
141 };
142
143 if message_like_event.sender() != own_user_id {
145 return Err(EditError::NotAuthor);
146 }
147
148 match new_content {
149 EditedContent::RoomMessage(new_content) => {
150 let AnySyncMessageLikeEvent::RoomMessage(SyncMessageLikeEvent::Original(original)) =
152 message_like_event
153 else {
154 return Err(EditError::IncompatibleEditType {
155 target: message_like_event.event_type().to_string(),
156 new_content: "room message",
157 });
158 };
159
160 let mentions = original.content.mentions;
161
162 let replacement = new_content
163 .make_replacement(ReplacementMetadata::new(event_id.to_owned(), mentions));
164
165 Ok(replacement.into())
166 }
167
168 EditedContent::MediaCaption { caption, formatted_caption, mentions } => {
169 let AnySyncMessageLikeEvent::RoomMessage(SyncMessageLikeEvent::Original(original)) =
171 message_like_event
172 else {
173 return Err(EditError::IncompatibleEditType {
174 target: message_like_event.event_type().to_string(),
175 new_content: "caption for a media room message",
176 });
177 };
178
179 let original_mentions = original.content.mentions.clone();
180
181 let mut prev_content = original.content;
182
183 if !update_media_caption(&mut prev_content, caption, formatted_caption, mentions) {
184 return Err(EditError::IncompatibleEditType {
185 target: prev_content.msgtype.msgtype().to_owned(),
186 new_content: "caption for a media room message",
187 });
188 }
189
190 let replacement = prev_content
191 .make_replacement(ReplacementMetadata::new(event_id.to_owned(), original_mentions));
192
193 Ok(replacement.into())
194 }
195
196 EditedContent::PollStart { fallback_text, new_content } => {
197 if !matches!(
198 message_like_event,
199 AnySyncMessageLikeEvent::UnstablePollStart(SyncMessageLikeEvent::Original(_))
200 ) {
201 return Err(EditError::IncompatibleEditType {
202 target: message_like_event.event_type().to_string(),
203 new_content: "poll start",
204 });
205 }
206
207 let replacement = UnstablePollStartEventContent::Replacement(
208 ReplacementUnstablePollStartEventContent::plain_text(
209 fallback_text,
210 new_content,
211 event_id.to_owned(),
212 ),
213 );
214
215 Ok(replacement.into())
216 }
217 }
218}
219
220pub(crate) async fn validate_attachment_edit<S: EventSource>(
227 source: S,
228 own_user_id: &UserId,
229 event_id: &EventId,
230) -> Result<Option<Mentions>, EditError> {
231 let target = source.get_event(event_id).await.map_err(|err| EditError::Fetch(Box::new(err)))?;
232
233 let event = target.raw().deserialize().map_err(EditError::Deserialize)?;
234
235 let AnySyncTimelineEvent::MessageLike(message_like_event) = event else {
237 return Err(EditError::StateEvent);
238 };
239
240 if message_like_event.sender() != own_user_id {
242 return Err(EditError::NotAuthor);
243 }
244
245 let AnySyncMessageLikeEvent::RoomMessage(SyncMessageLikeEvent::Original(original)) =
246 message_like_event
247 else {
248 return Err(EditError::IncompatibleEditType {
249 target: message_like_event.event_type().to_string(),
250 new_content: "room message",
251 });
252 };
253
254 Ok(original.content.mentions)
255}
256
257macro_rules! set_caption {
263 ($event:expr, $caption:expr) => {
264 let filename = $event.filename().to_owned();
265 if let Some(caption) = $caption {
271 $event.filename = Some(filename);
272 $event.body = caption;
273 } else {
274 $event.filename = None;
275 $event.body = filename;
276 }
277 };
278}
279
280pub(crate) fn update_media_caption(
285 content: &mut RoomMessageEventContent,
286 caption: Option<String>,
287 formatted_caption: Option<FormattedBody>,
288 mentions: Option<Mentions>,
289) -> bool {
290 if let Some(Relation::Replacement(replacement)) = &content.relates_to {
294 let edited_event_id = replacement.event_id.clone();
295 let original_mentions = mentions_of_edited_event(
296 replacement.new_content.mentions.as_ref(),
297 content.mentions.as_ref(),
298 );
299
300 let mut new_content = replacement.new_content.clone();
301 if !update_media_msgtype_caption(&mut new_content.msgtype, caption, formatted_caption) {
302 return false;
303 }
304 new_content.mentions = mentions;
305
306 *content = new_content
307 .make_replacement(ReplacementMetadata::new(edited_event_id, original_mentions));
308 return true;
309 }
310
311 content.mentions = mentions;
312 update_media_msgtype_caption(&mut content.msgtype, caption, formatted_caption)
313}
314
315fn mentions_of_edited_event(
319 canonical: Option<&Mentions>,
320 notified: Option<&Mentions>,
321) -> Option<Mentions> {
322 let (canonical, notified) = (canonical?, notified?);
323
324 let mut mentions = Mentions::new();
325 mentions.user_ids =
326 canonical.user_ids.iter().filter(|u| !notified.user_ids.contains(*u)).cloned().collect();
327 mentions.room = canonical.room && !notified.room;
328 Some(mentions)
329}
330
331fn update_media_msgtype_caption(
335 msgtype: &mut MessageType,
336 caption: Option<String>,
337 formatted_caption: Option<FormattedBody>,
338) -> bool {
339 match msgtype {
340 MessageType::Audio(event) => {
341 set_caption!(event, caption);
342 event.formatted = formatted_caption;
343 true
344 }
345 MessageType::File(event) => {
346 set_caption!(event, caption);
347 event.formatted = formatted_caption;
348 true
349 }
350 #[cfg(feature = "unstable-msc4274")]
351 MessageType::Gallery(event) => {
352 event.body = caption.unwrap_or_default();
353 event.formatted = formatted_caption;
354 true
355 }
356 MessageType::Image(event) => {
357 set_caption!(event, caption);
358 event.formatted = formatted_caption;
359 true
360 }
361 MessageType::Video(event) => {
362 set_caption!(event, caption);
363 event.formatted = formatted_caption;
364 true
365 }
366 _ => false,
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use std::{assert_matches, collections::BTreeMap};
373
374 use matrix_sdk_base::deserialized_responses::TimelineEvent;
375 use matrix_sdk_test::{async_test, event_factory::EventFactory};
376 use ruma::{
377 EventId, OwnedEventId, event_id,
378 events::{
379 AnyMessageLikeEventContent, AnySyncTimelineEvent, Mentions,
380 room::message::{
381 ImageMessageEventContent, MessageType, Relation, ReplacementMetadata,
382 RoomMessageEventContentWithoutRelation,
383 },
384 },
385 owned_mxc_uri, owned_user_id, user_id,
386 };
387 use strass::assert_let;
388
389 use super::{
390 EditError, EventSource, make_edit_event, update_media_caption, validate_attachment_edit,
391 };
392 use crate::{Error, room::edit::EditedContent};
393
394 #[derive(Default)]
395 struct TestEventCache {
396 events: BTreeMap<OwnedEventId, TimelineEvent>,
397 }
398
399 impl EventSource for TestEventCache {
400 async fn get_event(&self, event_id: &EventId) -> Result<TimelineEvent, Error> {
401 Ok(self.events.get(event_id).unwrap().clone())
402 }
403 }
404
405 #[async_test]
406 async fn test_edit_state_event() {
407 let event_id = event_id!("$1");
408 let own_user_id = user_id!("@me:saucisse.bzh");
409
410 let mut cache = TestEventCache::default();
411 let f = EventFactory::new();
412 cache.events.insert(
413 event_id.to_owned(),
414 f.room_name("The room name").event_id(event_id).sender(own_user_id).into(),
415 );
416
417 let new_content = RoomMessageEventContentWithoutRelation::text_plain("the edit");
418
419 assert_matches!(
420 make_edit_event(cache, own_user_id, event_id, EditedContent::RoomMessage(new_content),)
421 .await,
422 Err(EditError::StateEvent)
423 );
424 }
425
426 #[async_test]
427 async fn test_edit_event_other_user() {
428 let event_id = event_id!("$1");
429 let f = EventFactory::new();
430
431 let mut cache = TestEventCache::default();
432
433 cache.events.insert(
434 event_id.to_owned(),
435 f.text_msg("hi").event_id(event_id).sender(user_id!("@other:saucisse.bzh")).into(),
436 );
437
438 let own_user_id = user_id!("@me:saucisse.bzh");
439 let new_content = RoomMessageEventContentWithoutRelation::text_plain("the edit");
440
441 assert_matches!(
442 make_edit_event(cache, own_user_id, event_id, EditedContent::RoomMessage(new_content),)
443 .await,
444 Err(EditError::NotAuthor)
445 );
446 }
447
448 #[async_test]
449 async fn test_make_edit_event_success() {
450 let event_id = event_id!("$1");
451 let own_user_id = user_id!("@me:saucisse.bzh");
452
453 let mut cache = TestEventCache::default();
454 let f = EventFactory::new();
455 cache.events.insert(
456 event_id.to_owned(),
457 f.text_msg("hi").event_id(event_id).sender(own_user_id).into(),
458 );
459
460 let new_content = RoomMessageEventContentWithoutRelation::text_plain("the edit");
461
462 let edit_event =
463 make_edit_event(cache, own_user_id, event_id, EditedContent::RoomMessage(new_content))
464 .await
465 .unwrap();
466
467 assert_let!(AnyMessageLikeEventContent::RoomMessage(msg) = &edit_event);
468 assert_eq!(msg.body(), "* the edit");
470 assert_let!(Some(Relation::Replacement(repl)) = &msg.relates_to);
471
472 assert_eq!(repl.event_id, event_id);
473 assert_eq!(repl.new_content.msgtype.body(), "the edit");
474 }
475
476 #[async_test]
477 async fn test_make_edit_caption_for_non_media_room_message() {
478 let event_id = event_id!("$1");
479 let own_user_id = user_id!("@me:saucisse.bzh");
480
481 let mut cache = TestEventCache::default();
482 let f = EventFactory::new();
483 cache.events.insert(
484 event_id.to_owned(),
485 f.text_msg("hello world").event_id(event_id).sender(own_user_id).into(),
486 );
487
488 let err = make_edit_event(
489 cache,
490 own_user_id,
491 event_id,
492 EditedContent::MediaCaption {
493 caption: Some("yo".to_owned()),
494 formatted_caption: None,
495 mentions: None,
496 },
497 )
498 .await
499 .unwrap_err();
500
501 assert_let!(EditError::IncompatibleEditType { target, new_content } = err);
502 assert_eq!(target, "m.text");
503 assert_eq!(new_content, "caption for a media room message");
504 }
505
506 #[async_test]
507 async fn test_add_caption_for_media() {
508 let event_id = event_id!("$1");
509 let own_user_id = user_id!("@me:saucisse.bzh");
510
511 let filename = "rickroll.gif";
512
513 let mut cache = TestEventCache::default();
514 let f = EventFactory::new();
515 cache.events.insert(
516 event_id.to_owned(),
517 f.image(filename.to_owned(), owned_mxc_uri!("mxc://sdk.rs/rickroll"))
518 .event_id(event_id)
519 .sender(own_user_id)
520 .into(),
521 );
522
523 let edit_event = make_edit_event(
524 cache,
525 own_user_id,
526 event_id,
527 EditedContent::MediaCaption {
528 caption: Some("Best joke ever".to_owned()),
529 formatted_caption: None,
530 mentions: None,
531 },
532 )
533 .await
534 .unwrap();
535
536 assert_let!(AnyMessageLikeEventContent::RoomMessage(msg) = edit_event);
537 assert_let!(MessageType::Image(image) = msg.msgtype);
538
539 assert_eq!(image.filename(), filename);
540 assert_eq!(image.caption(), Some("* Best joke ever")); assert!(image.formatted_caption().is_none());
542
543 assert_let!(Some(Relation::Replacement(repl)) = msg.relates_to);
544 assert_let!(MessageType::Image(new_image) = repl.new_content.msgtype);
545 assert_eq!(new_image.filename(), filename);
546 assert_eq!(new_image.caption(), Some("Best joke ever"));
547 assert!(new_image.formatted_caption().is_none());
548 }
549
550 #[async_test]
551 async fn test_remove_caption_for_media() {
552 let event_id = event_id!("$1");
553 let own_user_id = user_id!("@me:saucisse.bzh");
554
555 let filename = "rickroll.gif";
556
557 let mut cache = TestEventCache::default();
558 let f = EventFactory::new();
559
560 let event = f
561 .image(filename.to_owned(), owned_mxc_uri!("mxc://sdk.rs/rickroll"))
562 .caption(Some("caption".to_owned()), None)
563 .event_id(event_id)
564 .sender(own_user_id)
565 .into_event();
566
567 {
568 let event = event.raw().deserialize().unwrap();
570 assert_let!(AnySyncTimelineEvent::MessageLike(event) = event);
571 assert_let!(
572 AnyMessageLikeEventContent::RoomMessage(msg) = event.original_content().unwrap()
573 );
574 assert_let!(MessageType::Image(image) = msg.msgtype);
575 assert_eq!(image.filename(), filename);
576 assert_eq!(image.caption(), Some("caption"));
577 assert!(image.formatted_caption().is_none());
578 }
579
580 cache.events.insert(event_id.to_owned(), event);
581
582 let edit_event = make_edit_event(
583 cache,
584 own_user_id,
585 event_id,
586 EditedContent::MediaCaption { caption: None, formatted_caption: None, mentions: None },
588 )
589 .await
590 .unwrap();
591
592 assert_let!(AnyMessageLikeEventContent::RoomMessage(msg) = edit_event);
593 assert_let!(MessageType::Image(image) = msg.msgtype);
594
595 assert_eq!(image.filename(), "* rickroll.gif"); assert!(image.caption().is_none());
597 assert!(image.formatted_caption().is_none());
598
599 assert_let!(Some(Relation::Replacement(repl)) = msg.relates_to);
600 assert_let!(MessageType::Image(new_image) = repl.new_content.msgtype);
601 assert_eq!(new_image.filename(), "rickroll.gif");
602 assert!(new_image.caption().is_none());
603 assert!(new_image.formatted_caption().is_none());
604 }
605
606 #[async_test]
607 async fn test_add_media_caption_mention() {
608 let event_id = event_id!("$1");
609 let own_user_id = user_id!("@me:saucisse.bzh");
610
611 let filename = "rickroll.gif";
612
613 let mut cache = TestEventCache::default();
614 let f = EventFactory::new();
615
616 let event = f
618 .image(filename.to_owned(), owned_mxc_uri!("mxc://sdk.rs/rickroll"))
619 .event_id(event_id)
620 .sender(own_user_id)
621 .into_event();
622
623 {
624 let event = event.raw().deserialize().unwrap();
626 assert_let!(AnySyncTimelineEvent::MessageLike(event) = event);
627 assert_let!(
628 AnyMessageLikeEventContent::RoomMessage(msg) = event.original_content().unwrap()
629 );
630 assert_matches!(msg.mentions, None);
631 }
632
633 cache.events.insert(event_id.to_owned(), event);
634
635 let mentioned_user_id = owned_user_id!("@crepe:saucisse.bzh");
637 let edit_event = {
638 let mentions = Mentions::with_user_ids([mentioned_user_id.clone()]);
639 make_edit_event(
640 cache,
641 own_user_id,
642 event_id,
643 EditedContent::MediaCaption {
644 caption: None,
645 formatted_caption: None,
646 mentions: Some(mentions),
647 },
648 )
649 .await
650 .unwrap()
651 };
652
653 assert_let!(AnyMessageLikeEventContent::RoomMessage(msg) = edit_event);
654 assert_let!(MessageType::Image(image) = msg.msgtype);
655
656 assert!(image.caption().is_none());
657 assert!(image.formatted_caption().is_none());
658
659 assert_let!(Some(mentions) = msg.mentions);
661 assert!(!mentions.room);
662 assert_eq!(
663 mentions.user_ids.into_iter().collect::<Vec<_>>(),
664 vec![mentioned_user_id.clone()]
665 );
666
667 assert_let!(Some(Relation::Replacement(repl)) = msg.relates_to);
668 assert_let!(MessageType::Image(new_image) = repl.new_content.msgtype);
669 assert!(new_image.caption().is_none());
670 assert!(new_image.formatted_caption().is_none());
671
672 assert_let!(Some(mentions) = repl.new_content.mentions);
674 assert!(!mentions.room);
675 assert_eq!(mentions.user_ids.into_iter().collect::<Vec<_>>(), vec![mentioned_user_id]);
676 }
677
678 #[test]
679 fn test_update_media_caption_of_a_replacement() {
680 let crepe = owned_user_id!("@crepe:saucisse.bzh");
681 let galette = owned_user_id!("@galette:saucisse.bzh");
682
683 let image = MessageType::Image(ImageMessageEventContent::plain(
685 "rickroll.gif".to_owned(),
686 owned_mxc_uri!("mxc://sdk.rs/rickroll"),
687 ));
688 let mut content = RoomMessageEventContentWithoutRelation::new(image)
689 .add_mentions(Mentions::with_user_ids([crepe.clone()]))
690 .make_replacement(ReplacementMetadata::new(
691 event_id!("$1").to_owned(),
692 Some(Mentions::with_user_ids([crepe.clone()])),
693 ));
694
695 assert!(update_media_caption(
696 &mut content,
697 Some("Best joke ever".to_owned()),
698 None,
699 Some(Mentions::with_user_ids([crepe, galette.clone()]))
700 ));
701
702 assert_let!(MessageType::Image(image) = &content.msgtype);
705 assert_eq!(image.filename(), "rickroll.gif");
706 assert_eq!(image.caption(), Some("* Best joke ever"));
707 assert_let!(Some(notified) = &content.mentions);
708 assert_eq!(notified.user_ids.iter().collect::<Vec<_>>(), [&galette]);
709
710 assert_let!(Some(Relation::Replacement(repl)) = &content.relates_to);
712 assert_eq!(repl.event_id, event_id!("$1"));
713 assert_let!(MessageType::Image(new_image) = &repl.new_content.msgtype);
714 assert_eq!(new_image.filename(), "rickroll.gif");
715 assert_eq!(new_image.caption(), Some("Best joke ever"));
716 assert_let!(Some(mentions) = &repl.new_content.mentions);
717 assert_eq!(mentions.user_ids.len(), 2);
718 }
719
720 #[async_test]
721 async fn test_make_edit_event_success_with_response() {
722 let event_id = event_id!("$1");
723 let resp_event_id = event_id!("$resp");
724 let own_user_id = user_id!("@me:saucisse.bzh");
725
726 let mut cache = TestEventCache::default();
727 let f = EventFactory::new();
728
729 cache.events.insert(
730 event_id.to_owned(),
731 f.text_msg("hi").event_id(event_id).sender(user_id!("@steb:saucisse.bzh")).into(),
732 );
733
734 cache.events.insert(
735 resp_event_id.to_owned(),
736 f.text_msg("you're the hi")
737 .event_id(resp_event_id)
738 .sender(own_user_id)
739 .reply_to(event_id)
740 .into(),
741 );
742
743 let new_content = RoomMessageEventContentWithoutRelation::text_plain("uh i mean hi too");
744
745 let edit_event = make_edit_event(
746 cache,
747 own_user_id,
748 resp_event_id,
749 EditedContent::RoomMessage(new_content),
750 )
751 .await
752 .unwrap();
753
754 assert_let!(AnyMessageLikeEventContent::RoomMessage(msg) = &edit_event);
755 assert_eq!(msg.body(), "* uh i mean hi too");
757 assert_let!(Some(Relation::Replacement(repl)) = &msg.relates_to);
758
759 assert_eq!(repl.event_id, resp_event_id);
760 assert_eq!(repl.new_content.msgtype.body(), "uh i mean hi too");
761 }
762
763 fn image_event_cache(event_id: &EventId, sender: &ruma::UserId) -> TestEventCache {
764 let mut cache = TestEventCache::default();
765 let f = EventFactory::new();
766 cache.events.insert(
767 event_id.to_owned(),
768 f.image("rickroll.gif".to_owned(), owned_mxc_uri!("mxc://sdk.rs/rickroll"))
769 .event_id(event_id)
770 .sender(sender)
771 .into(),
772 );
773 cache
774 }
775
776 #[async_test]
777 async fn test_validate_attachment_edit() {
778 let event_id = event_id!("$1");
779 let own_user_id = user_id!("@me:saucisse.bzh");
780
781 let cache = image_event_cache(event_id, own_user_id);
782
783 assert_matches!(validate_attachment_edit(cache, own_user_id, event_id).await, Ok(None));
784 }
785
786 #[async_test]
787 async fn test_validate_attachment_edit_of_a_text_message() {
788 let event_id = event_id!("$1");
789 let own_user_id = user_id!("@me:saucisse.bzh");
790
791 let mut cache = TestEventCache::default();
792 let f = EventFactory::new();
793 cache.events.insert(
794 event_id.to_owned(),
795 f.text_msg("this is not a media").event_id(event_id).sender(own_user_id).into(),
796 );
797
798 assert_matches!(validate_attachment_edit(cache, own_user_id, event_id).await, Ok(None));
799 }
800
801 #[async_test]
802 async fn test_validate_attachment_edit_state_event() {
803 let event_id = event_id!("$1");
804 let own_user_id = user_id!("@me:saucisse.bzh");
805
806 let mut cache = TestEventCache::default();
807 let f = EventFactory::new();
808 cache.events.insert(
809 event_id.to_owned(),
810 f.room_name("The room name").event_id(event_id).sender(own_user_id).into(),
811 );
812
813 assert_matches!(
814 validate_attachment_edit(cache, own_user_id, event_id).await,
815 Err(EditError::StateEvent)
816 );
817 }
818
819 #[async_test]
820 async fn test_validate_attachment_edit_not_a_room_message() {
821 let event_id = event_id!("$1");
822 let own_user_id = user_id!("@me:saucisse.bzh");
823
824 let mut cache = TestEventCache::default();
825 let f = EventFactory::new();
826 cache.events.insert(
827 event_id.to_owned(),
828 f.poll_start("poll", "question", vec!["a", "b"])
829 .event_id(event_id)
830 .sender(own_user_id)
831 .into(),
832 );
833
834 assert_matches!(
835 validate_attachment_edit(cache, own_user_id, event_id).await,
836 Err(EditError::IncompatibleEditType { .. })
837 );
838 }
839
840 #[async_test]
841 async fn test_validate_attachment_edit_other_user() {
842 let event_id = event_id!("$1");
843 let own_user_id = user_id!("@me:saucisse.bzh");
844
845 let cache = image_event_cache(event_id, user_id!("@other:saucisse.bzh"));
846
847 assert_matches!(
848 validate_attachment_edit(cache, own_user_id, event_id).await,
849 Err(EditError::NotAuthor)
850 );
851 }
852}