@@ -569,7 +569,10 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
569
569
) ;
570
570
} ) ;
571
571
572
- specTest ( 'Recovers when watch rejection cannot be persisted' , [ ] , ( ) => {
572
+ specTest ( 'Handles rejections that cannot be persisted' , [ ] , ( ) => {
573
+ // This test verifies that the client ignores failures during the
574
+ // 'Release target' transaction.
575
+
573
576
const doc1Query = Query . atPath ( path ( 'collection/key1' ) ) ;
574
577
const doc2Query = Query . atPath ( path ( 'collection/key2' ) ) ;
575
578
const doc1a = doc ( 'collection/key1' , 1000 , { foo : 'a' } ) ;
@@ -593,25 +596,22 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
593
596
doc1Query ,
594
597
new RpcError ( Code . PERMISSION_DENIED , 'Simulated target error' )
595
598
)
596
- // `failDatabaseTransactions()` causes us to go offline.
597
- . expectActiveTargets ( )
598
- . expectEvents ( doc1Query , { fromCache : true } )
599
- . expectEvents ( doc2Query , { fromCache : true } )
599
+ . expectEvents ( doc1Query , { errorCode : Code . PERMISSION_DENIED } )
600
600
. recoverDatabase ( )
601
- . runTimer ( TimerId . AsyncQueueRetry )
602
- . expectActiveTargets (
603
- { query : doc1Query , resumeToken : 'resume-token-1000' } ,
604
- { query : doc2Query , resumeToken : 'resume-token-2000' }
605
- )
606
- . watchAcksFull ( doc1Query , 3000 )
607
- . expectEvents ( doc1Query , { } )
608
601
. watchRemoves (
609
602
doc2Query ,
610
603
new RpcError ( Code . PERMISSION_DENIED , 'Simulated target error' )
611
604
)
612
605
. expectEvents ( doc2Query , { errorCode : Code . PERMISSION_DENIED } )
613
- . watchSends ( { affects : [ doc1Query ] } , doc1b )
614
- . watchSnapshots ( 4000 )
606
+ // Verify that `doc1Query` can be listened to again. Note that the
607
+ // resume token is slightly outdated since we failed to persist the
608
+ // target update during the release.
609
+ . userListens ( doc1Query , 'resume-token-1000' )
610
+ . expectEvents ( doc1Query , {
611
+ added : [ doc1a ] ,
612
+ fromCache : true
613
+ } )
614
+ . watchAcksFull ( doc1Query , 4000 , doc1b )
615
615
. expectEvents ( doc1Query , {
616
616
modified : [ doc1b ]
617
617
} )
@@ -797,4 +797,45 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
797
797
) ;
798
798
}
799
799
) ;
800
+
801
+ specTest ( 'Unlisten succeeds when target release fails' , [ ] , ( ) => {
802
+ const query = Query . atPath ( path ( 'collection' ) ) ;
803
+ const doc1 = doc ( 'collection/key1' , 1 , { foo : 'a' } ) ;
804
+ return spec ( )
805
+ . userListens ( query )
806
+ . watchAcksFull ( query , 1000 , doc1 )
807
+ . expectEvents ( query , {
808
+ added : [ doc1 ]
809
+ } )
810
+ . failDatabaseTransactions ( 'Release target' )
811
+ . userUnlistens ( query )
812
+ . expectActiveTargets ( ) ;
813
+ } ) ;
814
+
815
+ specTest ( 'Can re-listen to query when unlisten fails' , [ ] , ( ) => {
816
+ const query = Query . atPath ( path ( 'collection' ) ) ;
817
+ const doc1 = doc ( 'collection/key1' , 1 , { foo : 'a' } ) ;
818
+ const doc2 = doc ( 'collection/key2' , 2 , { foo : 'b' } ) ;
819
+ return spec ( )
820
+ . withGCEnabled ( false )
821
+ . userListens ( query )
822
+ . watchAcksFull ( query , 1000 , doc1 )
823
+ . expectEvents ( query , {
824
+ added : [ doc1 ]
825
+ } )
826
+ . failDatabaseTransactions ( 'Release target' )
827
+ . userUnlistens ( query )
828
+ . watchRemoves ( query )
829
+ . recoverDatabase ( )
830
+ . userListens ( query , 'resume-token-1000' )
831
+ . expectEvents ( query , {
832
+ added : [ doc1 ] ,
833
+ fromCache : true
834
+ } )
835
+ . watchAcksFull ( query , 2000 , doc2 )
836
+ . expectEvents ( query , {
837
+ added : [ doc2 ]
838
+ } )
839
+ . userUnlistens ( query ) ;
840
+ } ) ;
800
841
} ) ;
0 commit comments