@@ -562,6 +562,225 @@ describe("SlidingSync", () => {
562562 } ) ;
563563 } ) ;
564564
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+
565784 describe ( "extensions" , ( ) => {
566785 beforeAll ( setupClient ) ;
567786 afterAll ( teardownClient ) ;
0 commit comments