@@ -32,7 +32,6 @@ import { FirebaseApp } from './public-types';
32
32
import * as firebaseUtil from '@firebase/util' ;
33
33
import { SinonStub , stub , useFakeTimers } from 'sinon' ;
34
34
import * as indexedDb from './indexeddb' ;
35
- import { base64Encode , isIndexedDBAvailable } from '@firebase/util' ;
36
35
37
36
declare module '@firebase/component' {
38
37
interface NameServiceMapping {
@@ -99,38 +98,37 @@ describe('HeartbeatServiceImpl', () => {
99
98
*/
100
99
it ( `triggerHeartbeat() stores a heartbeat` , async ( ) => {
101
100
await heartbeatService . triggerHeartbeat ( ) ;
102
- expect ( heartbeatService . _heartbeatsCache ?. length ) . to . equal ( 1 ) ;
103
- const heartbeat1 = heartbeatService . _heartbeatsCache ?. [ 0 ] ;
104
- expect ( heartbeat1 ?. userAgent ) . to . equal ( USER_AGENT_STRING_1 ) ;
101
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats . length ) . to . equal ( 1 ) ;
102
+ const heartbeat1 = heartbeatService . _heartbeatsCache ?. heartbeats [ 0 ] ;
103
+ expect ( heartbeat1 ?. agent ) . to . equal ( USER_AGENT_STRING_1 ) ;
105
104
expect ( heartbeat1 ?. date ) . to . equal ( '1970-01-01' ) ;
106
- expect ( writeStub ) . to . be . calledWith ( [ heartbeat1 ] ) ;
105
+ expect ( writeStub ) . to . be . calledWith ( { heartbeats : [ heartbeat1 ] } ) ;
107
106
} ) ;
108
107
it ( `triggerHeartbeat() doesn't store another heartbeat on the same day` , async ( ) => {
109
- expect ( heartbeatService . _heartbeatsCache ?. length ) . to . equal ( 1 ) ;
108
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats . length ) . to . equal ( 1 ) ;
110
109
await heartbeatService . triggerHeartbeat ( ) ;
111
- expect ( heartbeatService . _heartbeatsCache ?. length ) . to . equal ( 1 ) ;
110
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats . length ) . to . equal ( 1 ) ;
112
111
} ) ;
113
112
it ( `triggerHeartbeat() does store another heartbeat on a different day` , async ( ) => {
114
- expect ( heartbeatService . _heartbeatsCache ?. length ) . to . equal ( 1 ) ;
113
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats . length ) . to . equal ( 1 ) ;
115
114
clock . tick ( 24 * 60 * 60 * 1000 ) ;
116
115
await heartbeatService . triggerHeartbeat ( ) ;
117
- expect ( heartbeatService . _heartbeatsCache ?. length ) . to . equal ( 2 ) ;
118
- expect ( heartbeatService . _heartbeatsCache ?. [ 1 ] . date ) . to . equal (
116
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats . length ) . to . equal ( 2 ) ;
117
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats [ 1 ] . date ) . to . equal (
119
118
'1970-01-02'
120
119
) ;
121
120
} ) ;
122
121
it ( `triggerHeartbeat() stores another entry for a different user agent` , async ( ) => {
123
122
userAgentString = USER_AGENT_STRING_2 ;
124
- expect ( heartbeatService . _heartbeatsCache ?. length ) . to . equal ( 2 ) ;
123
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats . length ) . to . equal ( 2 ) ;
125
124
clock . tick ( 2 * 24 * 60 * 60 * 1000 ) ;
126
125
await heartbeatService . triggerHeartbeat ( ) ;
127
- expect ( heartbeatService . _heartbeatsCache ?. length ) . to . equal ( 3 ) ;
128
- expect ( heartbeatService . _heartbeatsCache ?. [ 2 ] . date ) . to . equal (
126
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats . length ) . to . equal ( 3 ) ;
127
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats [ 2 ] . date ) . to . equal (
129
128
'1970-01-03'
130
129
) ;
131
130
} ) ;
132
131
it ( 'getHeartbeatHeaders() gets stored heartbeats and clears heartbeats' , async ( ) => {
133
- const deleteStub = stub ( heartbeatService . _storage , 'deleteAll' ) ;
134
132
const heartbeatHeaders = firebaseUtil . base64Decode (
135
133
await heartbeatService . getHeartbeatsHeader ( )
136
134
) ;
@@ -140,10 +138,13 @@ describe('HeartbeatServiceImpl', () => {
140
138
expect ( heartbeatHeaders ) . to . include ( '1970-01-02' ) ;
141
139
expect ( heartbeatHeaders ) . to . include ( '1970-01-03' ) ;
142
140
expect ( heartbeatHeaders ) . to . include ( `"version":2` ) ;
143
- expect ( heartbeatService . _heartbeatsCache ) . to . equal ( null ) ;
141
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats ) . to . be . empty ;
142
+ expect ( writeStub ) . to . be . calledWith ( {
143
+ lastSentHeartbeatDate : '1970-01-01' ,
144
+ heartbeats : [ ]
145
+ } ) ;
144
146
const emptyHeaders = await heartbeatService . getHeartbeatsHeader ( ) ;
145
147
expect ( emptyHeaders ) . to . equal ( '' ) ;
146
- expect ( deleteStub ) . to . be . called ;
147
148
} ) ;
148
149
} ) ;
149
150
describe ( 'If IndexedDB has entries' , ( ) => {
@@ -154,11 +155,11 @@ describe('HeartbeatServiceImpl', () => {
154
155
const mockIndexedDBHeartbeats = [
155
156
// Chosen so one will exceed 30 day limit and one will not.
156
157
{
157
- userAgent : 'old-user-agent' ,
158
+ agent : 'old-user-agent' ,
158
159
date : '1969-12-01'
159
160
} ,
160
161
{
161
- userAgent : 'old-user-agent' ,
162
+ agent : 'old-user-agent' ,
162
163
date : '1969-12-31'
163
164
}
164
165
] ;
@@ -197,61 +198,149 @@ describe('HeartbeatServiceImpl', () => {
197
198
*/
198
199
it ( `new heartbeat service reads from indexedDB cache` , async ( ) => {
199
200
const promiseResult = await heartbeatService . _heartbeatsCachePromise ;
200
- if ( isIndexedDBAvailable ( ) ) {
201
- expect ( promiseResult ) . to . deep . equal ( mockIndexedDBHeartbeats ) ;
202
- expect ( heartbeatService . _heartbeatsCache ) . to . deep . equal (
203
- mockIndexedDBHeartbeats
204
- ) ;
201
+ if ( firebaseUtil . isIndexedDBAvailable ( ) ) {
202
+ expect ( promiseResult ) . to . deep . equal ( {
203
+ heartbeats : mockIndexedDBHeartbeats
204
+ } ) ;
205
+ expect ( heartbeatService . _heartbeatsCache ) . to . deep . equal ( {
206
+ heartbeats : mockIndexedDBHeartbeats
207
+ } ) ;
205
208
} else {
206
209
// In Node or other no-indexed-db environments it will fail the
207
210
// `canUseIndexedDb` check and return an empty array.
208
- expect ( promiseResult ) . to . deep . equal ( [ ] ) ;
209
- expect ( heartbeatService . _heartbeatsCache ) . to . deep . equal ( [ ] ) ;
211
+ expect ( promiseResult ) . to . deep . equal ( {
212
+ heartbeats : [ ]
213
+ } ) ;
214
+ expect ( heartbeatService . _heartbeatsCache ) . to . deep . equal ( {
215
+ heartbeats : [ ]
216
+ } ) ;
210
217
}
211
218
} ) ;
212
219
it ( `triggerHeartbeat() writes new heartbeats and retains old ones newer than 30 days` , async ( ) => {
213
220
userAgentString = USER_AGENT_STRING_2 ;
214
221
clock . tick ( 3 * 24 * 60 * 60 * 1000 ) ;
215
222
await heartbeatService . triggerHeartbeat ( ) ;
216
- if ( isIndexedDBAvailable ( ) ) {
217
- expect ( writeStub ) . to . be . calledWith ( [
218
- // The first entry exceeds the 30 day retention limit.
219
- mockIndexedDBHeartbeats [ 1 ] ,
220
- { userAgent : USER_AGENT_STRING_2 , date : '1970-01-04' }
221
- ] ) ;
223
+ if ( firebaseUtil . isIndexedDBAvailable ( ) ) {
224
+ expect ( writeStub ) . to . be . calledWith ( {
225
+ heartbeats : [
226
+ // The first entry exceeds the 30 day retention limit.
227
+ mockIndexedDBHeartbeats [ 1 ] ,
228
+ { agent : USER_AGENT_STRING_2 , date : '1970-01-04' }
229
+ ]
230
+ } ) ;
222
231
} else {
223
- expect ( writeStub ) . to . be . calledWith ( [
224
- { userAgent : USER_AGENT_STRING_2 , date : '1970-01-04' }
225
- ] ) ;
232
+ expect ( writeStub ) . to . be . calledWith ( {
233
+ heartbeats : [ { agent : USER_AGENT_STRING_2 , date : '1970-01-04' } ]
234
+ } ) ;
226
235
}
227
236
} ) ;
228
237
it ( 'getHeartbeatHeaders() gets stored heartbeats and clears heartbeats' , async ( ) => {
229
- const deleteStub = stub ( heartbeatService . _storage , 'deleteAll' ) ;
230
238
const heartbeatHeaders = firebaseUtil . base64Decode (
231
239
await heartbeatService . getHeartbeatsHeader ( )
232
240
) ;
233
- if ( isIndexedDBAvailable ( ) ) {
241
+ if ( firebaseUtil . isIndexedDBAvailable ( ) ) {
234
242
expect ( heartbeatHeaders ) . to . include ( 'old-user-agent' ) ;
235
243
expect ( heartbeatHeaders ) . to . include ( '1969-12-31' ) ;
236
244
}
237
245
expect ( heartbeatHeaders ) . to . include ( USER_AGENT_STRING_2 ) ;
238
246
expect ( heartbeatHeaders ) . to . include ( '1970-01-04' ) ;
239
247
expect ( heartbeatHeaders ) . to . include ( `"version":2` ) ;
240
- expect ( heartbeatService . _heartbeatsCache ) . to . equal ( null ) ;
248
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats ) . to . be . empty ;
249
+ expect ( writeStub ) . to . be . calledWith ( {
250
+ lastSentHeartbeatDate : '1970-01-01' ,
251
+ heartbeats : [ ]
252
+ } ) ;
241
253
const emptyHeaders = await heartbeatService . getHeartbeatsHeader ( ) ;
242
254
expect ( emptyHeaders ) . to . equal ( '' ) ;
243
- expect ( deleteStub ) . to . be . called ;
255
+ } ) ;
256
+ } ) ;
257
+
258
+ describe ( 'If IndexedDB records that a header was sent today' , ( ) => {
259
+ let heartbeatService : HeartbeatServiceImpl ;
260
+ let writeStub : SinonStub ;
261
+ const userAgentString = USER_AGENT_STRING_1 ;
262
+ const mockIndexedDBHeartbeats = [
263
+ // Chosen so one will exceed 30 day limit and one will not.
264
+ {
265
+ agent : 'old-user-agent' ,
266
+ date : '1969-12-01'
267
+ } ,
268
+ {
269
+ agent : 'old-user-agent' ,
270
+ date : '1969-12-31'
271
+ }
272
+ ] ;
273
+ before ( ( ) => {
274
+ const container = new ComponentContainer ( 'heartbeatTestContainer' ) ;
275
+ container . addComponent (
276
+ new Component (
277
+ 'app' ,
278
+ ( ) =>
279
+ ( {
280
+ options : { appId : 'an-app-id' } ,
281
+ name : 'an-app-name'
282
+ } as FirebaseApp ) ,
283
+ ComponentType . VERSION
284
+ )
285
+ ) ;
286
+ container . addComponent (
287
+ new Component (
288
+ 'platform-logger' ,
289
+ ( ) => ( { getPlatformInfoString : ( ) => userAgentString } ) ,
290
+ ComponentType . VERSION
291
+ )
292
+ ) ;
293
+ stub ( indexedDb , 'readHeartbeatsFromIndexedDB' ) . resolves ( {
294
+ lastSentHeartbeatDate : '1970-01-01' ,
295
+ heartbeats : [ ...mockIndexedDBHeartbeats ]
296
+ } ) ;
297
+ heartbeatService = new HeartbeatServiceImpl ( container ) ;
298
+ } ) ;
299
+ beforeEach ( ( ) => {
300
+ useFakeTimers ( ) ;
301
+ writeStub = stub ( heartbeatService . _storage , 'overwrite' ) ;
302
+ } ) ;
303
+ it ( `new heartbeat service reads from indexedDB cache` , async ( ) => {
304
+ const promiseResult = await heartbeatService . _heartbeatsCachePromise ;
305
+ if ( firebaseUtil . isIndexedDBAvailable ( ) ) {
306
+ expect ( promiseResult ) . to . deep . equal ( {
307
+ lastSentHeartbeatDate : '1970-01-01' ,
308
+ heartbeats : mockIndexedDBHeartbeats
309
+ } ) ;
310
+ expect ( heartbeatService . _heartbeatsCache ) . to . deep . equal ( {
311
+ lastSentHeartbeatDate : '1970-01-01' ,
312
+ heartbeats : mockIndexedDBHeartbeats
313
+ } ) ;
314
+ } else {
315
+ // In Node or other no-indexed-db environments it will fail the
316
+ // `canUseIndexedDb` check and return an empty array.
317
+ expect ( promiseResult ) . to . deep . equal ( {
318
+ heartbeats : [ ]
319
+ } ) ;
320
+ expect ( heartbeatService . _heartbeatsCache ) . to . deep . equal ( {
321
+ heartbeats : [ ]
322
+ } ) ;
323
+ }
324
+ } ) ;
325
+ it ( `triggerHeartbeat() will skip storing new data` , async ( ) => {
326
+ await heartbeatService . triggerHeartbeat ( ) ;
327
+ expect ( writeStub ) . to . not . be . called ;
328
+ if ( firebaseUtil . isIndexedDBAvailable ( ) ) {
329
+ expect ( heartbeatService . _heartbeatsCache ?. heartbeats ) . to . deep . equal (
330
+ mockIndexedDBHeartbeats
331
+ ) ;
332
+ }
244
333
} ) ;
245
334
} ) ;
246
335
247
336
describe ( 'countBytes()' , ( ) => {
248
337
it ( 'counts how many bytes there will be in a stringified, encoded header' , ( ) => {
249
338
const heartbeats = [
250
- { userAgent : generateUserAgentString ( 1 ) , dates : generateDates ( 1 ) } ,
251
- { userAgent : generateUserAgentString ( 3 ) , dates : generateDates ( 2 ) }
339
+ { agent : generateUserAgentString ( 1 ) , dates : generateDates ( 1 ) } ,
340
+ { agent : generateUserAgentString ( 3 ) , dates : generateDates ( 2 ) }
252
341
] ;
253
342
let size : number = 0 ;
254
- const headerString = base64Encode (
343
+ const headerString = firebaseUtil . base64urlEncodeWithoutPadding (
255
344
JSON . stringify ( { version : 2 , heartbeats } )
256
345
) ;
257
346
// Use independent methods to validate our byte count method matches.
@@ -272,7 +361,7 @@ describe('HeartbeatServiceImpl', () => {
272
361
describe ( '_extractHeartbeatsForHeader()' , ( ) => {
273
362
it ( 'returns empty heartbeatsToKeep if it cannot get under maxSize' , ( ) => {
274
363
const heartbeats = [
275
- { userAgent : generateUserAgentString ( 1 ) , date : '2022-01-01' }
364
+ { agent : generateUserAgentString ( 1 ) , date : '2022-01-01' }
276
365
] ;
277
366
const { unsentEntries, heartbeatsToSend } = extractHeartbeatsForHeader (
278
367
heartbeats ,
@@ -283,11 +372,11 @@ describe('HeartbeatServiceImpl', () => {
283
372
} ) ;
284
373
it ( 'splits heartbeats array' , ( ) => {
285
374
const heartbeats = [
286
- { userAgent : generateUserAgentString ( 20 ) , date : '2022-01-01' } ,
287
- { userAgent : generateUserAgentString ( 4 ) , date : '2022-01-02' }
375
+ { agent : generateUserAgentString ( 20 ) , date : '2022-01-01' } ,
376
+ { agent : generateUserAgentString ( 4 ) , date : '2022-01-02' }
288
377
] ;
289
378
const sizeWithHeartbeat0Only = countBytes ( [
290
- { userAgent : heartbeats [ 0 ] . userAgent , dates : [ heartbeats [ 0 ] . date ] }
379
+ { agent : heartbeats [ 0 ] . agent , dates : [ heartbeats [ 0 ] . date ] }
291
380
] ) ;
292
381
const { unsentEntries, heartbeatsToSend } = extractHeartbeatsForHeader (
293
382
heartbeats ,
@@ -299,12 +388,12 @@ describe('HeartbeatServiceImpl', () => {
299
388
it ( 'splits the first heartbeat if needed' , ( ) => {
300
389
const uaString = generateUserAgentString ( 20 ) ;
301
390
const heartbeats = [
302
- { userAgent : uaString , date : '2022-01-01' } ,
303
- { userAgent : uaString , date : '2022-01-02' } ,
304
- { userAgent : uaString , date : '2022-01-03' }
391
+ { agent : uaString , date : '2022-01-01' } ,
392
+ { agent : uaString , date : '2022-01-02' } ,
393
+ { agent : uaString , date : '2022-01-03' }
305
394
] ;
306
395
const sizeWithHeartbeat0Only = countBytes ( [
307
- { userAgent : heartbeats [ 0 ] . userAgent , dates : [ heartbeats [ 0 ] . date ] }
396
+ { agent : heartbeats [ 0 ] . agent , dates : [ heartbeats [ 0 ] . date ] }
308
397
] ) ;
309
398
const { unsentEntries, heartbeatsToSend } = extractHeartbeatsForHeader (
310
399
heartbeats ,
0 commit comments