@@ -57,6 +57,36 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
57
57
}
58
58
) ;
59
59
60
+ specTest (
61
+ 'Clients fail to lookup mutations (with recovery)' ,
62
+ [ 'multi-client' ] ,
63
+ ( ) => {
64
+ // Multi-Tab uses a Local Storage notification to inform all tabs about
65
+ // changes to a mutation tab. To act upon these changes, the tabs read
66
+ // the mutated document from IndexedDB. This test verifies that mutations
67
+ // are applied even if the lookup fails temporarily.
68
+ return (
69
+ client ( 0 )
70
+ . expectPrimaryState ( true )
71
+ // All tabs fail to act upon the Local Storage notifications
72
+ . failDatabaseTransactions ( 'Lookup mutation documents' )
73
+ . client ( 1 )
74
+ . expectPrimaryState ( false )
75
+ . userSets ( 'collection/a' , { v : 1 } )
76
+ . failDatabaseTransactions ( 'Lookup mutation documents' )
77
+ // All tabs recover and the notifications are processed
78
+ . client ( 0 )
79
+ . recoverDatabase ( )
80
+ . runTimer ( TimerId . AsyncQueueRetry )
81
+ . writeAcks ( 'collection/a' , 1 , { expectUserCallback : false } )
82
+ . client ( 1 )
83
+ . recoverDatabase ( )
84
+ . runTimer ( TimerId . AsyncQueueRetry )
85
+ . expectUserCallbacks ( { acknowledged : [ 'collection/a' ] } )
86
+ ) ;
87
+ }
88
+ ) ;
89
+
60
90
specTest (
61
91
'Query raises events in secondary client (with recovery)' ,
62
92
[ 'multi-client' ] ,
@@ -114,6 +144,55 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
114
144
}
115
145
) ;
116
146
147
+ specTest (
148
+ 'Ignores intermittent lease refresh failures (with recovery)' ,
149
+ [ 'multi-client' ] ,
150
+ ( ) => {
151
+ // This test verifies that an IndexedDB failure during a lease refresh
152
+ // does not impact client functionality. Lease refresh failures are
153
+ // ignored, as the lease is also verified each time an operation is
154
+ // run.
155
+ return (
156
+ client ( 0 )
157
+ . expectPrimaryState ( true )
158
+ . client ( 1 )
159
+ . expectPrimaryState ( false )
160
+ // Run the initial sequence: The primary client fails its lease refresh
161
+ // before the secondary client.
162
+ . client ( 0 )
163
+ . failDatabaseTransactions ( 'updateClientMetadataAndTryBecomePrimary' )
164
+ . runTimer ( TimerId . ClientMetadataRefresh )
165
+ . client ( 1 )
166
+ . failDatabaseTransactions ( 'updateClientMetadataAndTryBecomePrimary' )
167
+ . runTimer ( TimerId . ClientMetadataRefresh )
168
+ . client ( 0 )
169
+ . recoverDatabase ( )
170
+ . runTimer ( TimerId . ClientMetadataRefresh )
171
+ . expectPrimaryState ( true )
172
+ . client ( 1 )
173
+ . recoverDatabase ( )
174
+ . runTimer ( TimerId . ClientMetadataRefresh )
175
+ . expectPrimaryState ( false )
176
+ // Run the opposite sequence: The secondary client fails its lease
177
+ // refresh before the primary client.
178
+ . client ( 1 )
179
+ . failDatabaseTransactions ( 'updateClientMetadataAndTryBecomePrimary' )
180
+ . runTimer ( TimerId . ClientMetadataRefresh )
181
+ . client ( 0 )
182
+ . failDatabaseTransactions ( 'updateClientMetadataAndTryBecomePrimary' )
183
+ . runTimer ( TimerId . ClientMetadataRefresh )
184
+ . client ( 1 )
185
+ . recoverDatabase ( )
186
+ . runTimer ( TimerId . ClientMetadataRefresh )
187
+ . expectPrimaryState ( false )
188
+ . client ( 0 )
189
+ . recoverDatabase ( )
190
+ . runTimer ( TimerId . ClientMetadataRefresh )
191
+ . expectPrimaryState ( true )
192
+ ) ;
193
+ }
194
+ ) ;
195
+
117
196
specTest ( 'Recovers when write cannot be persisted' , [ ] , ( ) => {
118
197
return (
119
198
spec ( )
0 commit comments