Repository: zulip/zulip

Issue:
Add zerver/lib/events / test_events support for moving messages between streams
This is a planned follow-up to #13912; I'm opening it now so we don't lose track of it.  We should add a case to `test_stream_send_message_events` (or similar to the message editing piece of it) that moving a message between streams using the `propagate` feature, and verifies that our `zerver/lib/events.py` code does the right thing.

I'm pretty sure it doesn't today and might be fairly tricky to fix, and it's a very small race, so I'm OK with merging #13912 without a proper fix for it.  


Discussion:
Hello @zulip/server-api, @zulip/server-message-view members, this issue was labeled with the "area: api", "area: message-editing" labels, so you may want to check it out!

<!-- areaLabelAddition -->

Resolving patch:
diff --git a/zerver/lib/events.py b/zerver/lib/events.py
--- a/zerver/lib/events.py
+++ b/zerver/lib/events.py
@@ -672,6 +672,13 @@ def name(sub: Dict[str, Any]) -> str:
         # We don't return messages in /register, so we don't need to
         # do anything for content updates, but we may need to update
         # the unread_msgs data if the topic of an unread message changed.
+        if 'new_stream_id' in event:
+            stream_dict = state['raw_unread_msgs']['stream_dict']
+            stream_id = event['new_stream_id']
+            for message_id in event['message_ids']:
+                if message_id in stream_dict:
+                    stream_dict[message_id]['stream_id'] = stream_id
+
         if TOPIC_NAME in event:
             stream_dict = state['raw_unread_msgs']['stream_dict']
             topic = event[TOPIC_NAME]


Test patch:
diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py
--- a/zerver/tests/test_events.py
+++ b/zerver/tests/test_events.py
@@ -479,6 +479,54 @@ def get_checker(check_gravatar: Validator[Optional[str]]) -> Validator[Dict[str,
         )
         schema_checker('events[0]', events[0])
 
+        # Verify move topic to different stream.
+        schema_checker = check_events_dict([
+            ('type', equals('update_message')),
+            ('flags', check_list(None)),
+            ('edit_timestamp', check_int),
+            ('message_id', check_int),
+            ('message_ids', check_list(check_int)),
+            (ORIG_TOPIC, check_string),
+            ('propagate_mode', check_string),
+            ('stream_id', check_int),
+            ('new_stream_id', check_int),
+            ('stream_name', check_string),
+            (TOPIC_NAME, check_string),
+            (TOPIC_LINKS, check_list(None)),
+            ('user_id', check_int),
+        ])
+
+        # Send 2 messages in "test" topic.
+        self.send_stream_message(self.user_profile, "Verona")
+        message_id = self.send_stream_message(self.user_profile, "Verona")
+        message = Message.objects.get(id=message_id)
+        topic = 'new_topic'
+        stream = get_stream("Denmark", self.user_profile.realm)
+        propagate_mode = 'change_all'
+        prior_mention_user_ids = set()
+
+        events = self.verify_action(
+            lambda: do_update_message(
+                self.user_profile,
+                message,
+                stream,
+                topic,
+                propagate_mode,
+                True,
+                True,
+                None,
+                None,
+                set(),
+                set(),
+                None),
+            state_change_expected=True,
+            # There are 3 events generated for this action
+            # * update_message: For updating existing messages
+            # * 2 new message events: Breadcrumb messages in the new and old topics.
+            num_events=3,
+        )
+        schema_checker('events[0]', events[0])
+
     def test_update_message_flags(self) -> None:
         # Test message flag update events
         schema_checker = check_events_dict([