1
1
import 'dart:convert' ;
2
+ import 'dart:io' ;
2
3
3
4
import 'package:checks/checks.dart' ;
5
+ import 'package:http/http.dart' as http;
4
6
import 'package:test/scaffolding.dart' ;
5
7
import 'package:zulip/api/model/events.dart' ;
6
8
import 'package:zulip/api/model/model.dart' ;
7
9
import 'package:zulip/api/model/submessage.dart' ;
10
+ import 'package:zulip/api/route/messages.dart' ;
8
11
import 'package:zulip/model/message_list.dart' ;
9
12
import 'package:zulip/model/narrow.dart' ;
10
13
import 'package:zulip/model/store.dart' ;
@@ -13,6 +16,7 @@ import '../api/fake_api.dart';
13
16
import '../api/model/model_checks.dart' ;
14
17
import '../api/model/submessage_checks.dart' ;
15
18
import '../example_data.dart' as eg;
19
+ import '../fake_async.dart' ;
16
20
import '../stdlib_checks.dart' ;
17
21
import 'message_list_test.dart' ;
18
22
import 'store_checks.dart' ;
@@ -123,6 +127,179 @@ void main() {
123
127
});
124
128
});
125
129
130
+ group ('edit-message methods' , () {
131
+ late PerAccountStore store;
132
+ late FakeApiConnection connection;
133
+ late StreamMessage message;
134
+
135
+ Future <void > prepare () async {
136
+ store = eg.store ();
137
+ connection = store.connection as FakeApiConnection ;
138
+ message = eg.streamMessage ();
139
+ await store.addMessage (message);
140
+ }
141
+
142
+ test ('smoke' , () => awaitFakeAsync ((async ) async {
143
+ await prepare ();
144
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
145
+
146
+ connection.prepare (
147
+ json: UpdateMessageResult ().toJson (), delay: Duration (seconds: 1 ));
148
+ store.editMessage (messageId: message.id, content: 'new content' );
149
+ check (connection.takeRequests ()).single.isA< http.Request > ()
150
+ ..method.equals ('PATCH' )
151
+ ..url.path.equals ('/api/v1/messages/${message .id }' )
152
+ ..bodyFields.deepEquals ({
153
+ 'content' : 'new content' ,
154
+ });
155
+
156
+ async .elapse (Duration (milliseconds: 500 ));
157
+ // Mid-request
158
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isFalse ();
159
+
160
+ async .elapse (Duration (milliseconds: 500 ));
161
+ // Request has succeeded; event hasn't arrived
162
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isFalse ();
163
+
164
+ await store.handleEvent (eg.updateMessageEditEvent (message));
165
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
166
+ }));
167
+
168
+ test ('request fails' , () => awaitFakeAsync ((async ) async {
169
+ await prepare ();
170
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
171
+
172
+ connection.prepare (apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
173
+ store.editMessage (messageId: message.id, content: 'new content' );
174
+ async .elapse (Duration (seconds: 1 ));
175
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
176
+ }));
177
+
178
+ test ('request fails; take failed edit' , () => awaitFakeAsync ((async ) async {
179
+ await prepare ();
180
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
181
+
182
+ connection.prepare (apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
183
+ store.editMessage (messageId: message.id, content: 'new content' );
184
+ async .elapse (Duration (seconds: 1 ));
185
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
186
+
187
+ check (store.takeFailedMessageEdit (message.id)).equals ('new content' );
188
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
189
+ }));
190
+
191
+ test ('takeFailedMessageEdit throws StateError when nothing to take' , () => awaitFakeAsync ((async ) async {
192
+ await prepare ();
193
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
194
+ check (() => store.takeFailedMessageEdit (message.id)).throws <StateError >();
195
+ }));
196
+
197
+ test ('request failure after event arrival' , () => awaitFakeAsync ((async ) async {
198
+ // This can happen with network issues.
199
+
200
+ await prepare ();
201
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
202
+
203
+ connection.prepare (
204
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
205
+ store.editMessage (messageId: message.id, content: 'new content' );
206
+
207
+ async .elapse (Duration (milliseconds: 500 ));
208
+ await store.handleEvent (eg.updateMessageEditEvent (message));
209
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
210
+
211
+ async .elapse (Duration (milliseconds: 500 ));
212
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
213
+ }));
214
+
215
+ test ('request failure before event arrival' , () => awaitFakeAsync ((async ) async {
216
+ // This can happen with network issues.
217
+
218
+ await prepare ();
219
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
220
+
221
+ connection.prepare (
222
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
223
+ store.editMessage (messageId: message.id, content: 'new content' );
224
+
225
+ async .elapse (Duration (seconds: 1 ));
226
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
227
+
228
+ await store.handleEvent (eg.updateMessageEditEvent (message));
229
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
230
+ }));
231
+
232
+ test ('request failure before event arrival; take failed edit in between' , () => awaitFakeAsync ((async ) async {
233
+ // This can happen with network issues.
234
+
235
+ await prepare ();
236
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
237
+
238
+ connection.prepare (
239
+ httpException: const SocketException ('failed' ), delay: Duration (seconds: 1 ));
240
+ store.editMessage (messageId: message.id, content: 'new content' );
241
+
242
+ async .elapse (Duration (seconds: 1 ));
243
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
244
+ check (store.takeFailedMessageEdit (message.id)).equals ('new content' );
245
+
246
+ await store.handleEvent (eg.updateMessageEditEvent (message)); // no error
247
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
248
+ }));
249
+
250
+ test ('request fails, then message deleted' , () => awaitFakeAsync ((async ) async {
251
+ await prepare ();
252
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
253
+
254
+ connection.prepare (apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
255
+ store.editMessage (messageId: message.id, content: 'new content' );
256
+ async .elapse (Duration (seconds: 1 ));
257
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isTrue ();
258
+
259
+ await store.handleEvent (eg.deleteMessageEvent ([message])); // no error
260
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
261
+ }));
262
+
263
+ test ('message deleted while request in progress; we get failure response' , () => awaitFakeAsync ((async ) async {
264
+ await prepare ();
265
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
266
+
267
+ connection.prepare (apiException: eg.apiBadRequest (), delay: Duration (seconds: 1 ));
268
+ store.editMessage (messageId: message.id, content: 'new content' );
269
+
270
+ async .elapse (Duration (milliseconds: 500 ));
271
+ // Mid-request
272
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isFalse ();
273
+
274
+ await store.handleEvent (eg.deleteMessageEvent ([message]));
275
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
276
+
277
+ async .elapse (Duration (milliseconds: 500 ));
278
+ // Request failure, but status has already been cleared
279
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
280
+ }));
281
+
282
+ test ('message deleted while request in progress but we get success response' , () => awaitFakeAsync ((async ) async {
283
+ await prepare ();
284
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
285
+
286
+ connection.prepare (
287
+ json: UpdateMessageResult ().toJson (), delay: Duration (seconds: 1 ));
288
+ store.editMessage (messageId: message.id, content: 'new content' );
289
+
290
+ async .elapse (Duration (milliseconds: 500 ));
291
+ // Mid-request
292
+ check (store.getEditMessageErrorStatus (message.id)).isNotNull ().isFalse ();
293
+
294
+ await store.handleEvent (eg.deleteMessageEvent ([message]));
295
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
296
+
297
+ async .elapse (Duration (milliseconds: 500 ));
298
+ // Request success
299
+ check (store.getEditMessageErrorStatus (message.id)).isNull ();
300
+ }));
301
+ });
302
+
126
303
group ('handleMessageEvent' , () {
127
304
test ('from empty' , () async {
128
305
await prepare ();
0 commit comments