@@ -562,6 +562,225 @@ describe("SlidingSync", () => {
562
562
} ) ;
563
563
} ) ;
564
564
565
+ describe ( "transaction IDs" , ( ) => {
566
+ beforeAll ( setupClient ) ;
567
+ afterAll ( teardownClient ) ;
568
+ const roomId = "!foo:bar" ;
569
+
570
+ let slidingSync : SlidingSync ;
571
+
572
+ // really this applies to them all but it's easier to just test one
573
+ it ( "should resolve modifyRoomSubscriptions after SlidingSync.start() is called" , async ( ) => {
574
+ const roomSubInfo = {
575
+ timeline_limit : 1 ,
576
+ required_state : [
577
+ [ "m.room.name" , "" ] ,
578
+ ] ,
579
+ } ;
580
+ // add the subscription
581
+ slidingSync = new SlidingSync ( proxyBaseUrl , [ ] , roomSubInfo , client , 1 ) ;
582
+ // modification before SlidingSync.start()
583
+ const subscribePromise = slidingSync . modifyRoomSubscriptions ( new Set ( [ roomId ] ) ) ;
584
+ let txnId ;
585
+ httpBackend . when ( "POST" , syncUrl ) . check ( function ( req ) {
586
+ const body = req . data ;
587
+ logger . debug ( "got " , body ) ;
588
+ expect ( body . room_subscriptions ) . toBeTruthy ( ) ;
589
+ expect ( body . room_subscriptions [ roomId ] ) . toEqual ( roomSubInfo ) ;
590
+ expect ( body . txn_id ) . toBeTruthy ( ) ;
591
+ txnId = body . txn_id ;
592
+ } ) . respond ( 200 , function ( ) {
593
+ return {
594
+ pos : "aaa" ,
595
+ txn_id : txnId ,
596
+ lists : [ ] ,
597
+ extensions : { } ,
598
+ rooms : {
599
+ [ roomId ] : {
600
+ name : "foo bar" ,
601
+ required_state : [ ] ,
602
+ timeline : [ ] ,
603
+ } ,
604
+ } ,
605
+ } ;
606
+ } ) ;
607
+ slidingSync . start ( ) ;
608
+ await httpBackend . flushAllExpected ( ) ;
609
+ await subscribePromise ;
610
+ } ) ;
611
+ it ( "should resolve setList during a connection" , async ( ) => {
612
+ const newList = {
613
+ ranges : [ [ 0 , 20 ] ] ,
614
+ } ;
615
+ const promise = slidingSync . setList ( 0 , newList ) ;
616
+ let txnId ;
617
+ httpBackend . when ( "POST" , syncUrl ) . check ( function ( req ) {
618
+ const body = req . data ;
619
+ logger . debug ( "got " , body ) ;
620
+ expect ( body . room_subscriptions ) . toBeFalsy ( ) ;
621
+ expect ( body . lists [ 0 ] ) . toEqual ( newList ) ;
622
+ expect ( body . txn_id ) . toBeTruthy ( ) ;
623
+ txnId = body . txn_id ;
624
+ } ) . respond ( 200 , function ( ) {
625
+ return {
626
+ pos : "bbb" ,
627
+ txn_id : txnId ,
628
+ lists : [ { count : 5 } ] ,
629
+ extensions : { } ,
630
+ } ;
631
+ } ) ;
632
+ await httpBackend . flushAllExpected ( ) ;
633
+ await promise ;
634
+ expect ( txnId ) . toBeDefined ( ) ;
635
+ } ) ;
636
+ it ( "should resolve setListRanges during a connection" , async ( ) => {
637
+ const promise = slidingSync . setListRanges ( 0 , [ [ 20 , 40 ] ] ) ;
638
+ let txnId ;
639
+ httpBackend . when ( "POST" , syncUrl ) . check ( function ( req ) {
640
+ const body = req . data ;
641
+ logger . debug ( "got " , body ) ;
642
+ expect ( body . room_subscriptions ) . toBeFalsy ( ) ;
643
+ expect ( body . lists [ 0 ] ) . toEqual ( {
644
+ ranges : [ [ 20 , 40 ] ] ,
645
+ } ) ;
646
+ expect ( body . txn_id ) . toBeTruthy ( ) ;
647
+ txnId = body . txn_id ;
648
+ } ) . respond ( 200 , function ( ) {
649
+ return {
650
+ pos : "ccc" ,
651
+ txn_id : txnId ,
652
+ lists : [ { count : 5 } ] ,
653
+ extensions : { } ,
654
+ } ;
655
+ } ) ;
656
+ await httpBackend . flushAllExpected ( ) ;
657
+ await promise ;
658
+ expect ( txnId ) . toBeDefined ( ) ;
659
+ } ) ;
660
+ it ( "should resolve modifyRoomSubscriptionInfo during a connection" , async ( ) => {
661
+ const promise = slidingSync . modifyRoomSubscriptionInfo ( {
662
+ timeline_limit : 99 ,
663
+ } ) ;
664
+ let txnId ;
665
+ httpBackend . when ( "POST" , syncUrl ) . check ( function ( req ) {
666
+ const body = req . data ;
667
+ logger . debug ( "got " , body ) ;
668
+ expect ( body . room_subscriptions ) . toBeTruthy ( ) ;
669
+ expect ( body . room_subscriptions [ roomId ] ) . toEqual ( {
670
+ timeline_limit : 99 ,
671
+ } ) ;
672
+ expect ( body . txn_id ) . toBeTruthy ( ) ;
673
+ txnId = body . txn_id ;
674
+ } ) . respond ( 200 , function ( ) {
675
+ return {
676
+ pos : "ddd" ,
677
+ txn_id : txnId ,
678
+ extensions : { } ,
679
+ } ;
680
+ } ) ;
681
+ await httpBackend . flushAllExpected ( ) ;
682
+ await promise ;
683
+ expect ( txnId ) . toBeDefined ( ) ;
684
+ } ) ;
685
+ it ( "should reject earlier pending promises if a later transaction is acknowledged" , async ( ) => {
686
+ // i.e if we have [A,B,C] and see txn_id=C then A,B should be rejected.
687
+ const gotTxnIds = [ ] ;
688
+ const pushTxn = function ( req ) {
689
+ gotTxnIds . push ( req . data . txn_id ) ;
690
+ } ;
691
+ const failPromise = slidingSync . setListRanges ( 0 , [ [ 20 , 40 ] ] ) ;
692
+ httpBackend . when ( "POST" , syncUrl ) . check ( pushTxn ) . respond ( 200 , { pos : "e" } ) ; // missing txn_id
693
+ await httpBackend . flushAllExpected ( ) ;
694
+ const failPromise2 = slidingSync . setListRanges ( 0 , [ [ 60 , 70 ] ] ) ;
695
+ httpBackend . when ( "POST" , syncUrl ) . check ( pushTxn ) . respond ( 200 , { pos : "f" } ) ; // missing txn_id
696
+ await httpBackend . flushAllExpected ( ) ;
697
+
698
+ // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection
699
+ // which is a fail.
700
+ expect ( failPromise ) . rejects . toEqual ( gotTxnIds [ 0 ] ) ;
701
+ expect ( failPromise2 ) . rejects . toEqual ( gotTxnIds [ 1 ] ) ;
702
+
703
+ const okPromise = slidingSync . setListRanges ( 0 , [ [ 0 , 20 ] ] ) ;
704
+ let txnId ;
705
+ httpBackend . when ( "POST" , syncUrl ) . check ( ( req ) => {
706
+ txnId = req . data . txn_id ;
707
+ } ) . respond ( 200 , ( ) => {
708
+ // include the txn_id, earlier requests should now be reject()ed.
709
+ return {
710
+ pos : "g" ,
711
+ txn_id : txnId ,
712
+ } ;
713
+ } ) ;
714
+ await httpBackend . flushAllExpected ( ) ;
715
+ await okPromise ;
716
+
717
+ expect ( txnId ) . toBeDefined ( ) ;
718
+ } ) ;
719
+ it ( "should not reject later pending promises if an earlier transaction is acknowledged" , async ( ) => {
720
+ // i.e if we have [A,B,C] and see txn_id=B then C should not be rejected but A should.
721
+ const gotTxnIds = [ ] ;
722
+ const pushTxn = function ( req ) {
723
+ gotTxnIds . push ( req . data . txn_id ) ;
724
+ } ;
725
+ const A = slidingSync . setListRanges ( 0 , [ [ 20 , 40 ] ] ) ;
726
+ httpBackend . when ( "POST" , syncUrl ) . check ( pushTxn ) . respond ( 200 , { pos : "A" } ) ;
727
+ await httpBackend . flushAllExpected ( ) ;
728
+ const B = slidingSync . setListRanges ( 0 , [ [ 60 , 70 ] ] ) ;
729
+ httpBackend . when ( "POST" , syncUrl ) . check ( pushTxn ) . respond ( 200 , { pos : "B" } ) ; // missing txn_id
730
+ await httpBackend . flushAllExpected ( ) ;
731
+
732
+ // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection
733
+ // which is a fail.
734
+ expect ( A ) . rejects . toEqual ( gotTxnIds [ 0 ] ) ;
735
+
736
+ const C = slidingSync . setListRanges ( 0 , [ [ 0 , 20 ] ] ) ;
737
+ let pendingC = true ;
738
+ C . finally ( ( ) => {
739
+ pendingC = false ;
740
+ } ) ;
741
+ httpBackend . when ( "POST" , syncUrl ) . check ( pushTxn ) . respond ( 200 , ( ) => {
742
+ // include the txn_id for B, so C's promise is outstanding
743
+ return {
744
+ pos : "C" ,
745
+ txn_id : gotTxnIds [ 1 ] ,
746
+ } ;
747
+ } ) ;
748
+ await httpBackend . flushAllExpected ( ) ;
749
+ // A is rejected, see above
750
+ expect ( B ) . resolves . toEqual ( gotTxnIds [ 1 ] ) ; // B is resolved
751
+ expect ( pendingC ) . toBe ( true ) ; // C is pending still
752
+ } ) ;
753
+ it ( "should do nothing for unknown txn_ids" , async ( ) => {
754
+ const promise = slidingSync . setListRanges ( 0 , [ [ 20 , 40 ] ] ) ;
755
+ let pending = true ;
756
+ promise . finally ( ( ) => {
757
+ pending = false ;
758
+ } ) ;
759
+ let txnId ;
760
+ httpBackend . when ( "POST" , syncUrl ) . check ( function ( req ) {
761
+ const body = req . data ;
762
+ logger . debug ( "got " , body ) ;
763
+ expect ( body . room_subscriptions ) . toBeFalsy ( ) ;
764
+ expect ( body . lists [ 0 ] ) . toEqual ( {
765
+ ranges : [ [ 20 , 40 ] ] ,
766
+ } ) ;
767
+ expect ( body . txn_id ) . toBeTruthy ( ) ;
768
+ txnId = body . txn_id ;
769
+ } ) . respond ( 200 , function ( ) {
770
+ return {
771
+ pos : "ccc" ,
772
+ txn_id : "bogus transaction id" ,
773
+ lists : [ { count : 5 } ] ,
774
+ extensions : { } ,
775
+ } ;
776
+ } ) ;
777
+ await httpBackend . flushAllExpected ( ) ;
778
+ expect ( txnId ) . toBeDefined ( ) ;
779
+ expect ( pending ) . toBe ( true ) ;
780
+ slidingSync . stop ( ) ;
781
+ } ) ;
782
+ } ) ;
783
+
565
784
describe ( "extensions" , ( ) => {
566
785
beforeAll ( setupClient ) ;
567
786
afterAll ( teardownClient ) ;
0 commit comments