@@ -2,7 +2,9 @@ import 'package:checks/checks.dart';
2
2
import 'package:flutter_test/flutter_test.dart' ;
3
3
import 'package:zulip/api/model/model.dart' ;
4
4
import 'package:zulip/model/recent_senders.dart' ;
5
+ import 'package:zulip/model/store.dart' ;
5
6
import '../example_data.dart' as eg;
7
+ import 'test_store.dart' ;
6
8
7
9
/// [messages] should be sorted by [id] ascending.
8
10
void checkMatchesMessages (RecentSenders model, List <Message > messages) {
@@ -142,6 +144,126 @@ void main() {
142
144
});
143
145
});
144
146
147
+ group ('RecentSenders.handleUpdateMessageUpdate' , () {
148
+ late PerAccountStore store;
149
+ late RecentSenders model;
150
+
151
+ final origChannel = eg.stream (); final newChannel = eg.stream ();
152
+ final origTopic = 'origTopic' ; final newTopic = 'newTopic' ;
153
+ final userX = eg.user (); final userY = eg.user ();
154
+
155
+ Future <void > prepare (List <Message > messages) async {
156
+ store = eg.store ();
157
+ await store.addMessages (messages);
158
+ await store.addStreams ([origChannel, newChannel]);
159
+ await store.addUsers ([userX, userY]);
160
+ model = store.recentSenders;
161
+ }
162
+
163
+ List <StreamMessage > copyMessagesWith (Iterable <StreamMessage > messages, {
164
+ ZulipStream ? newChannel,
165
+ String ? newTopic,
166
+ }) {
167
+ assert (newChannel != null || newTopic != null );
168
+ return messages.map ((message) => StreamMessage .fromJson (
169
+ message.toJson ()
170
+ ..['stream_id' ] = newChannel? .streamId ?? message.streamId
171
+ // See [StreamMessage.displayRecipient] for why this is needed.
172
+ ..['display_recipient' ] = newChannel? .name ?? message.displayRecipient!
173
+
174
+ ..['subject' ] = newTopic ?? message.topic
175
+ )).toList ();
176
+ }
177
+
178
+ test ('move a conversation exactly' , () async {
179
+ final messages = List .generate (10 , (i) => eg.streamMessage (
180
+ stream: origChannel, topic: origTopic, sender: userX));
181
+ await prepare (messages);
182
+ final streamSenderIdsBefore = model.streamSenders
183
+ [origChannel.streamId]! [userX.userId]! .ids;
184
+ final topicSenderIdsBefore = model.topicSenders
185
+ [origChannel.streamId]! [TopicName (origTopic)]! [userX.userId]! .ids;
186
+
187
+ await store.handleEvent (eg.updateMessageEventMoveFrom (
188
+ origMessages: messages,
189
+ newStreamId: newChannel.streamId,
190
+ newTopicStr: newTopic));
191
+ checkMatchesMessages (model, copyMessagesWith (
192
+ messages, newChannel: newChannel, newTopic: newTopic));
193
+ // Check we avoided creating a new list for the moved message IDs.
194
+ final streamSenderIdsAfter = model.streamSenders
195
+ [newChannel.streamId]! [userX.userId]! .ids;
196
+ final topicSenderIdsAfter = model.topicSenders
197
+ [newChannel.streamId]! [TopicName (newTopic)]! [userX.userId]! .ids;
198
+ check (streamSenderIdsBefore).identicalTo (streamSenderIdsAfter);
199
+ check (topicSenderIdsBefore).identicalTo (topicSenderIdsAfter);
200
+ });
201
+
202
+ test ('move a conversation partially to a different channel' , () async {
203
+ final messages = List .generate (10 , (i) => eg.streamMessage (
204
+ stream: origChannel, topic: origTopic));
205
+ final movedMessages = messages.take (5 ).toList ();
206
+ final otherMessages = messages.skip (5 );
207
+ await prepare (messages);
208
+
209
+ await store.handleEvent (eg.updateMessageEventMoveFrom (
210
+ origMessages: movedMessages,
211
+ newStreamId: newChannel.streamId));
212
+ checkMatchesMessages (model, [
213
+ ...copyMessagesWith (movedMessages, newChannel: newChannel),
214
+ ...otherMessages,
215
+ ]);
216
+ });
217
+
218
+ test ('move a conversation partially to a different topic, within the same channel' , () async {
219
+ final messages = List .generate (10 , (i) => eg.streamMessage (
220
+ stream: origChannel, topic: origTopic, sender: userX));
221
+ final movedMessages = messages.take (5 ).toList ();
222
+ final otherMessages = messages.skip (5 );
223
+ await prepare (messages);
224
+ final streamSenderIdsBefore = model.streamSenders
225
+ [origChannel.streamId]! [userX.userId]! .ids;
226
+
227
+ await store.handleEvent (eg.updateMessageEventMoveFrom (
228
+ origMessages: movedMessages,
229
+ newTopicStr: newTopic));
230
+ checkMatchesMessages (model, [
231
+ ...copyMessagesWith (movedMessages, newTopic: newTopic),
232
+ ...otherMessages,
233
+ ]);
234
+
235
+ // Check that we did not touch stream message IDs tracker
236
+ // when there wasn't a stream move.
237
+ final streamSenderIdsAfter = model.streamSenders
238
+ [origChannel.streamId]! [userX.userId]! .ids;
239
+ check (streamSenderIdsBefore).identicalTo (streamSenderIdsAfter);
240
+ });
241
+
242
+ test ('move a conversation with multiple senders' , () async {
243
+ final messages = [
244
+ eg.streamMessage (stream: origChannel, topic: origTopic, sender: userX),
245
+ eg.streamMessage (stream: origChannel, topic: origTopic, sender: userX),
246
+ eg.streamMessage (stream: origChannel, topic: origTopic, sender: userY),
247
+ ];
248
+ await prepare (messages);
249
+
250
+ await store.handleEvent (eg.updateMessageEventMoveFrom (
251
+ origMessages: messages,
252
+ newStreamId: newChannel.streamId));
253
+ checkMatchesMessages (model, copyMessagesWith (
254
+ messages, newChannel: newChannel));
255
+ });
256
+
257
+ test ('message edit update without move' , () async {
258
+ final messages = List .generate (10 , (i) => eg.streamMessage (
259
+ stream: origChannel, topic: origTopic));
260
+ await prepare (messages);
261
+
262
+ await store.handleEvent (eg.updateMessageEditEvent (messages[0 ]));
263
+ checkMatchesMessages (model, messages);
264
+ });
265
+ });
266
+
145
267
test ('RecentSenders.handleDeleteMessageEvent' , () {
146
268
final model = RecentSenders ();
147
269
final stream = eg.stream ();
0 commit comments