@@ -469,6 +469,192 @@ void main() {
469469        }
470470      }
471471    });
472+ 
473+     group ('moves' , () {
474+       final  origChannel =  eg.stream ();
475+       const  origTopic =  'origTopic' ;
476+       const  newTopic =  'newTopic' ;
477+ 
478+       late  List <StreamMessage > readMessages;
479+       late  List <StreamMessage > unreadMessages;
480+ 
481+       Future <void > prepareStore () async  {
482+         prepare ();
483+         await  channelStore.addStream (origChannel);
484+         await  channelStore.addSubscription (eg.subscription (origChannel));
485+         readMessages  =  List <StreamMessage >.generate (10 ,
486+           (_) =>  eg.streamMessage (stream:  origChannel, topic:  origTopic,
487+                    flags:  [MessageFlag .read]));
488+         unreadMessages =  List <StreamMessage >.generate (10 ,
489+           (_) =>  eg.streamMessage (stream:  origChannel, topic:  origTopic));
490+       }
491+ 
492+       List <StreamMessage > copyMessagesWith (Iterable <StreamMessage > messages, {
493+         ZulipStream ?  newChannel,
494+         String ?  newTopic,
495+       }) {
496+         assert (newChannel !=  null  ||  newTopic !=  null );
497+         return  messages.map ((message) =>  StreamMessage .fromJson (
498+           message.toJson ()
499+             ..['stream_id' ] =  newChannel? .streamId ??  message.streamId
500+             ..['subject' ] =  newTopic ??  message.topic
501+         )).toList ();
502+       }
503+ 
504+       test ('moving a conversation exactly' , () async  {
505+         await  prepareStore ();
506+         final  newChannel =  eg.stream ();
507+         await  channelStore.addStream (newChannel);
508+         await  channelStore.addSubscription (eg.subscription (newChannel));
509+         fillWithMessages (unreadMessages);
510+         final  originalMessageIds = 
511+           model.streams[origChannel.streamId]! [TopicName (origTopic)]! ;
512+ 
513+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
514+           origMessages:  unreadMessages,
515+           newStreamId:  newChannel.streamId,
516+           newTopicStr:  newTopic));
517+         checkNotifiedOnce ();
518+         checkMatchesMessages (copyMessagesWith (unreadMessages,
519+           newChannel:  newChannel, newTopic:  newTopic));
520+         final  newMessageIds = 
521+           model.streams[newChannel.streamId]! [TopicName (newTopic)]! ;
522+         check (originalMessageIds).identicalTo (newMessageIds);
523+       });
524+ 
525+       test ('moving some read messages from a conversation' , () async  {
526+         await  prepareStore ();
527+         final  messagesToMove =  readMessages.take (2 ).toList ();
528+         fillWithMessages (unreadMessages +  readMessages);
529+ 
530+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
531+           origMessages:  messagesToMove,
532+           newTopicStr:  newTopic));
533+         checkNotNotified ();
534+         checkMatchesMessages (unreadMessages);
535+       });
536+ 
537+       test ('moving some unread messages from a conversation' , () async  {
538+         await  prepareStore ();
539+         final  messagesToMove =  unreadMessages.take (2 ).toList ();
540+         fillWithMessages (unreadMessages +  readMessages);
541+ 
542+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
543+           origMessages:  messagesToMove,
544+           newTopicStr:  newTopic));
545+         checkNotifiedOnce ();
546+         checkMatchesMessages ([
547+           ...copyMessagesWith (messagesToMove, newTopic:  newTopic),
548+           ...unreadMessages.skip (2 ),
549+         ]);
550+       });
551+ 
552+       test ('moving some read and unread messages from a conversation' , () async  {
553+         await  prepareStore ();
554+         final  messagesToMove =  [unreadMessages.first, readMessages.first];
555+         fillWithMessages (unreadMessages +  readMessages);
556+ 
557+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
558+           origMessages:  messagesToMove,
559+           newTopicStr:  newTopic));
560+         checkNotifiedOnce ();
561+         checkMatchesMessages ([
562+           ...copyMessagesWith (unreadMessages.take (1 ), newTopic:  newTopic),
563+           ...unreadMessages.skip (1 ),
564+         ]);
565+       });
566+ 
567+       test ('moving some read and all unread messages from a conversation' , () async  {
568+         await  prepareStore ();
569+         final  messagesToMove =  unreadMessages +  readMessages.take (2 ).toList ();
570+         fillWithMessages (unreadMessages +  readMessages);
571+         final  originalMessageIds = 
572+           model.streams[origChannel.streamId]! [TopicName (origTopic)]! ;
573+ 
574+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
575+           origMessages:  messagesToMove,
576+           newTopicStr:  newTopic));
577+         checkNotifiedOnce ();
578+         checkMatchesMessages ([
579+           ...copyMessagesWith (unreadMessages, newTopic:  newTopic),
580+         ]);
581+         final  newMessageIds = 
582+           model.streams[origChannel.streamId]! [TopicName (newTopic)]! ;
583+         check (originalMessageIds).identicalTo (newMessageIds);
584+       });
585+ 
586+       test ('moving all read and unread messages from a conversation' , () async  {
587+         await  prepareStore ();
588+         final  allMessages =  unreadMessages +  readMessages;
589+         fillWithMessages (allMessages);
590+         final  originalMessageIds = 
591+           model.streams[origChannel.streamId]! [TopicName (origTopic)]! ;
592+ 
593+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
594+           origMessages:  allMessages,
595+           newTopicStr:  newTopic));
596+         checkNotifiedOnce ();
597+         checkMatchesMessages (copyMessagesWith (unreadMessages, newTopic:  newTopic));
598+         final  newMessageIds = 
599+           model.streams[origChannel.streamId]! [TopicName (newTopic)]! ;
600+         check (originalMessageIds).identicalTo (newMessageIds);
601+       });
602+ 
603+       test ('moving to unsubscribed channels drops the unreads' , () async  {
604+         await  prepareStore ();
605+         final  unsubscribedChannel =  eg.stream ();
606+         await  channelStore.addStream (unsubscribedChannel);
607+         assert (! channelStore.subscriptions.containsKey (
608+           unsubscribedChannel.streamId));
609+         fillWithMessages (unreadMessages);
610+ 
611+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
612+           origMessages:  unreadMessages,
613+           newStreamId:  unsubscribedChannel.streamId));
614+         checkNotifiedOnce ();
615+         checkMatchesMessages ([]);
616+       });
617+ 
618+       test ('tolerates unsorted messages' , () async  {
619+         await  prepareStore ();
620+         final  unreadMessages =  List .generate (10 , (i) => 
621+           eg.streamMessage (
622+             id:  1000  -  i, stream:  origChannel, topic:  origTopic));
623+         fillWithMessages (unreadMessages);
624+ 
625+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
626+           origMessages:  unreadMessages,
627+           newTopicStr:  newTopic));
628+         checkNotifiedOnce ();
629+         checkMatchesMessages (copyMessagesWith (unreadMessages, newTopic:  newTopic));
630+       });
631+ 
632+       test ('tolerates unreads unknown to the model' , () async  {
633+         await  prepareStore ();
634+         fillWithMessages (unreadMessages);
635+ 
636+         final  unknownChannel =  eg.stream ();
637+         assert (! channelStore.streams.containsKey (unknownChannel.streamId));
638+         final  unknownUnreadMessage =  eg.streamMessage (
639+           stream:  unknownChannel, topic:  origTopic);
640+ 
641+         model.handleUpdateMessageEvent (eg.updateMessageEventMoveFrom (
642+           origMessages:  [unknownUnreadMessage],
643+           newTopicStr:  newTopic));
644+         checkNotNotified ();
645+         checkMatchesMessages (unreadMessages);
646+       });
647+ 
648+       test ('message edit but no move' , () async  {
649+         await  prepareStore ();
650+         fillWithMessages (unreadMessages);
651+ 
652+         model.handleUpdateMessageEvent (eg.updateMessageEditEvent (
653+           unreadMessages.first));
654+         checkNotNotified ();
655+         checkMatchesMessages (unreadMessages);
656+       });
657+     });
472658  });
473659
474660
0 commit comments