@@ -203,4 +203,261 @@ describe('Parse.Push', () => {
203
203
done ( ) ;
204
204
} ) ;
205
205
} ) ;
206
+
207
+ const successfulAny = function ( body , installations ) {
208
+ const promises = installations . map ( ( device ) => {
209
+ return Promise . resolve ( {
210
+ transmitted : true ,
211
+ device : device ,
212
+ } )
213
+ } ) ;
214
+
215
+ return Promise . all ( promises ) ;
216
+ } ;
217
+
218
+ const provideInstallations = function ( num ) {
219
+ if ( ! num ) {
220
+ num = 2 ;
221
+ }
222
+
223
+ const installations = [ ] ;
224
+ while ( installations . length !== num ) {
225
+ // add Android installations
226
+ const installation = new Parse . Object ( "_Installation" ) ;
227
+ installation . set ( "installationId" , "installation_" + installations . length ) ;
228
+ installation . set ( "deviceToken" , "device_token_" + installations . length ) ;
229
+ installation . set ( "deviceType" , "android" ) ;
230
+ installations . push ( installation ) ;
231
+ }
232
+
233
+ return installations ;
234
+ } ;
235
+
236
+ const losingAdapter = {
237
+ send : function ( body , installations ) {
238
+ // simulate having lost an installation before this was called
239
+ // thus invalidating our 'count' in _PushStatus
240
+ installations . pop ( ) ;
241
+
242
+ return successfulAny ( body , installations ) ;
243
+ } ,
244
+ getValidPushTypes : function ( ) {
245
+ return [ "android" ] ;
246
+ }
247
+ } ;
248
+
249
+ /**
250
+ * Verifies that _PushStatus cannot get stuck in a 'running' state
251
+ * Simulates a simple push where 1 installation is removed between _PushStatus
252
+ * count being set and the pushes being sent
253
+ */
254
+ it ( 'does not get stuck with _PushStatus \'running\' on 1 installation lost' , ( done ) => {
255
+ reconfigureServer ( {
256
+ push : { adapter : losingAdapter }
257
+ } ) . then ( ( ) => {
258
+ return Parse . Object . saveAll ( provideInstallations ( ) ) ;
259
+ } ) . then ( ( ) => {
260
+ return Parse . Push . send (
261
+ {
262
+ data : { alert : "We fixed our status!" } ,
263
+ where : { deviceType : 'android' }
264
+ } ,
265
+ { useMasterKey : true }
266
+ ) ;
267
+ } ) . then ( ( ) => {
268
+ // it is enqueued so it can take time
269
+ return new Promise ( ( resolve ) => {
270
+ setTimeout ( ( ) => {
271
+ resolve ( ) ;
272
+ } , 1000 ) ;
273
+ } ) ;
274
+ } ) . then ( ( ) => {
275
+ // query for push status
276
+ const query = new Parse . Query ( '_PushStatus' ) ;
277
+ return query . find ( { useMasterKey : true } ) ;
278
+ } ) . then ( ( results ) => {
279
+ // verify status is NOT broken
280
+ expect ( results . length ) . toBe ( 1 ) ;
281
+ const result = results [ 0 ] ;
282
+ expect ( result . get ( 'status' ) ) . toEqual ( 'succeeded' ) ;
283
+ expect ( result . get ( 'numSent' ) ) . toEqual ( 1 ) ;
284
+ expect ( result . get ( 'count' ) ) . toEqual ( undefined ) ;
285
+ done ( ) ;
286
+ } ) ;
287
+ } ) ;
288
+
289
+ /**
290
+ * Verifies that _PushStatus cannot get stuck in a 'running' state
291
+ * Simulates a simple push where 1 installation is added between _PushStatus
292
+ * count being set and the pushes being sent
293
+ */
294
+ it ( 'does not get stuck with _PushStatus \'running\' on 1 installation added' , ( done ) => {
295
+ const installations = provideInstallations ( ) ;
296
+
297
+ // add 1 iOS installation which we will omit & add later on
298
+ const iOSInstallation = new Parse . Object ( "_Installation" ) ;
299
+ iOSInstallation . set ( "installationId" , "installation_" + installations . length ) ;
300
+ iOSInstallation . set ( "deviceToken" , "device_token_" + installations . length ) ;
301
+ iOSInstallation . set ( "deviceType" , "ios" ) ;
302
+ installations . push ( iOSInstallation ) ;
303
+
304
+ reconfigureServer ( {
305
+ push : {
306
+ adapter : {
307
+ send : function ( body , installations ) {
308
+ // simulate having added an installation before this was called
309
+ // thus invalidating our 'count' in _PushStatus
310
+ installations . push ( iOSInstallation ) ;
311
+
312
+ return successfulAny ( body , installations ) ;
313
+ } ,
314
+ getValidPushTypes : function ( ) {
315
+ return [ "android" ] ;
316
+ }
317
+ }
318
+ }
319
+ } ) . then ( ( ) => {
320
+ return Parse . Object . saveAll ( installations ) ;
321
+ } ) . then ( ( ) => {
322
+ return Parse . Push . send (
323
+ {
324
+ data : { alert : "We fixed our status!" } ,
325
+ where : { deviceType : { '$ne' : 'random' } }
326
+ } ,
327
+ { useMasterKey : true }
328
+ ) ;
329
+ } ) . then ( ( ) => {
330
+ // it is enqueued so it can take time
331
+ return new Promise ( ( resolve ) => {
332
+ setTimeout ( ( ) => {
333
+ resolve ( ) ;
334
+ } , 1000 ) ;
335
+ } ) ;
336
+ } ) . then ( ( ) => {
337
+ // query for push status
338
+ const query = new Parse . Query ( '_PushStatus' ) ;
339
+ return query . find ( { useMasterKey : true } ) ;
340
+ } ) . then ( ( results ) => {
341
+ // verify status is NOT broken
342
+ expect ( results . length ) . toBe ( 1 ) ;
343
+ const result = results [ 0 ] ;
344
+ expect ( result . get ( 'status' ) ) . toEqual ( 'succeeded' ) ;
345
+ expect ( result . get ( 'numSent' ) ) . toEqual ( 3 ) ;
346
+ expect ( result . get ( 'count' ) ) . toEqual ( undefined ) ;
347
+ done ( ) ;
348
+ } ) ;
349
+ } ) ;
350
+
351
+ /**
352
+ * Verifies that _PushStatus cannot get stuck in a 'running' state
353
+ * Simulates an extended push, where some installations may be removed,
354
+ * resulting in a non-zero count
355
+ */
356
+ it ( 'does not get stuck with _PushStatus \'running\' on many installations removed' , ( done ) => {
357
+ const devices = 1000 ;
358
+ const installations = provideInstallations ( devices ) ;
359
+
360
+ reconfigureServer ( {
361
+ push : { adapter : losingAdapter }
362
+ } ) . then ( ( ) => {
363
+ return Parse . Object . saveAll ( installations ) ;
364
+ } ) . then ( ( ) => {
365
+ return Parse . Push . send (
366
+ {
367
+ data : { alert : "We fixed our status!" } ,
368
+ where : { deviceType : 'android' }
369
+ } ,
370
+ { useMasterKey : true }
371
+ ) ;
372
+ } ) . then ( ( ) => {
373
+ // it is enqueued so it can take time
374
+ return new Promise ( ( resolve ) => {
375
+ setTimeout ( ( ) => {
376
+ resolve ( ) ;
377
+ } , 1000 ) ;
378
+ } ) ;
379
+ } ) . then ( ( ) => {
380
+ // query for push status
381
+ const query = new Parse . Query ( '_PushStatus' ) ;
382
+ return query . find ( { useMasterKey : true } ) ;
383
+ } ) . then ( ( results ) => {
384
+ // verify status is NOT broken
385
+ expect ( results . length ) . toBe ( 1 ) ;
386
+ const result = results [ 0 ] ;
387
+ expect ( result . get ( 'status' ) ) . toEqual ( 'succeeded' ) ;
388
+ // expect # less than # of batches used, assuming each batch is 100 pushes
389
+ expect ( result . get ( 'numSent' ) ) . toEqual ( devices - ( devices / 100 ) ) ;
390
+ expect ( result . get ( 'count' ) ) . toEqual ( undefined ) ;
391
+ done ( ) ;
392
+ } ) ;
393
+ } ) ;
394
+
395
+ /**
396
+ * Verifies that _PushStatus cannot get stuck in a 'running' state
397
+ * Simulates an extended push, where some installations may be added,
398
+ * resulting in a non-zero count
399
+ */
400
+ it ( 'does not get stuck with _PushStatus \'running\' on many installations added' , ( done ) => {
401
+ const devices = 1000 ;
402
+ const installations = provideInstallations ( devices ) ;
403
+
404
+ // add 1 iOS installation which we will omit & add later on
405
+ const iOSInstallations = [ ] ;
406
+
407
+ while ( iOSInstallations . length !== ( devices / 100 ) ) {
408
+ const iOSInstallation = new Parse . Object ( "_Installation" ) ;
409
+ iOSInstallation . set ( "installationId" , "installation_" + installations . length ) ;
410
+ iOSInstallation . set ( "deviceToken" , "device_token_" + installations . length ) ;
411
+ iOSInstallation . set ( "deviceType" , "ios" ) ;
412
+ installations . push ( iOSInstallation ) ;
413
+ iOSInstallations . push ( iOSInstallation ) ;
414
+ }
415
+
416
+ reconfigureServer ( {
417
+ push : {
418
+ adapter : {
419
+ send : function ( body , installations ) {
420
+ // simulate having added an installation before this was called
421
+ // thus invalidating our 'count' in _PushStatus
422
+ installations . push ( iOSInstallations . pop ( ) ) ;
423
+
424
+ return successfulAny ( body , installations ) ;
425
+ } ,
426
+ getValidPushTypes : function ( ) {
427
+ return [ "android" ] ;
428
+ }
429
+ }
430
+ }
431
+ } ) . then ( ( ) => {
432
+ return Parse . Object . saveAll ( installations ) ;
433
+ } ) . then ( ( ) => {
434
+ return Parse . Push . send (
435
+ {
436
+ data : { alert : "We fixed our status!" } ,
437
+ where : { deviceType : { '$ne' : 'random' } }
438
+ } ,
439
+ { useMasterKey : true }
440
+ ) ;
441
+ } ) . then ( ( ) => {
442
+ // it is enqueued so it can take time
443
+ return new Promise ( ( resolve ) => {
444
+ setTimeout ( ( ) => {
445
+ resolve ( ) ;
446
+ } , 1000 ) ;
447
+ } ) ;
448
+ } ) . then ( ( ) => {
449
+ // query for push status
450
+ const query = new Parse . Query ( '_PushStatus' ) ;
451
+ return query . find ( { useMasterKey : true } ) ;
452
+ } ) . then ( ( results ) => {
453
+ // verify status is NOT broken
454
+ expect ( results . length ) . toBe ( 1 ) ;
455
+ const result = results [ 0 ] ;
456
+ expect ( result . get ( 'status' ) ) . toEqual ( 'succeeded' ) ;
457
+ // expect # less than # of batches used, assuming each batch is 100 pushes
458
+ expect ( result . get ( 'numSent' ) ) . toEqual ( devices + ( devices / 100 ) ) ;
459
+ expect ( result . get ( 'count' ) ) . toEqual ( undefined ) ;
460
+ done ( ) ;
461
+ } ) ;
462
+ } ) ;
206
463
} ) ;
0 commit comments