@@ -18,6 +18,7 @@ import HttpPollingDatafileManager from '../src/httpPollingDatafileManager'
18
18
import { Headers , AbortableRequest , Response } from '../src/http'
19
19
import { DatafileManagerConfig } from '../src/datafileManager' ;
20
20
import { advanceTimersByTime , getTimerCount } from './testUtils'
21
+ import PersistentKeyValueCache from '../src/persistentKeyValueCache'
21
22
22
23
jest . mock ( '../src/backoffController' , ( ) => {
23
24
return jest . fn ( ) . mockImplementation ( ( ) => {
@@ -39,6 +40,8 @@ class TestDatafileManager extends HttpPollingDatafileManager {
39
40
40
41
responsePromises : Promise < Response > [ ] = [ ]
41
42
43
+ simulateResponseDelay : boolean = false
44
+
42
45
makeGetRequest ( url : string , headers : Headers ) : AbortableRequest {
43
46
const nextResponse : Error | Response | undefined = this . queuedResponses . pop ( )
44
47
let responsePromise : Promise < Response >
@@ -47,7 +50,12 @@ class TestDatafileManager extends HttpPollingDatafileManager {
47
50
} else if ( nextResponse instanceof Error ) {
48
51
responsePromise = Promise . reject ( nextResponse )
49
52
} else {
50
- responsePromise = Promise . resolve ( nextResponse )
53
+ if ( this . simulateResponseDelay ) {
54
+ // Actual response will have some delay. This is required to get expected behavior for caching.
55
+ responsePromise = new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( nextResponse ) , 50 ) )
56
+ } else {
57
+ responsePromise = Promise . resolve ( nextResponse )
58
+ }
51
59
}
52
60
this . responsePromises . push ( responsePromise )
53
61
return { responsePromise, abort : jest . fn ( ) }
@@ -58,6 +66,30 @@ class TestDatafileManager extends HttpPollingDatafileManager {
58
66
}
59
67
}
60
68
69
+ const testCache : PersistentKeyValueCache = {
70
+ get ( key : string ) : Promise < any | null > {
71
+ let val = null
72
+ switch ( key ) {
73
+ case 'opt-datafile-keyThatExists' :
74
+ val = { name : 'keyThatExists' }
75
+ break
76
+ }
77
+ return Promise . resolve ( val )
78
+ } ,
79
+
80
+ set ( key : string , val : any ) : Promise < void > {
81
+ return Promise . resolve ( )
82
+ } ,
83
+
84
+ contains ( key : string ) : Promise < Boolean > {
85
+ return Promise . resolve ( false )
86
+ } ,
87
+
88
+ remove ( key : string ) : Promise < void > {
89
+ return Promise . resolve ( )
90
+ }
91
+ }
92
+
61
93
describe ( 'httpPollingDatafileManager' , ( ) => {
62
94
beforeEach ( ( ) => {
63
95
jest . useFakeTimers ( )
@@ -82,13 +114,7 @@ describe('httpPollingDatafileManager', () => {
82
114
expect ( manager . get ( ) ) . toEqual ( { foo : 'abcd' } )
83
115
} )
84
116
85
- it ( 'resolves onReady immediately' , async ( ) => {
86
- manager . start ( )
87
- await manager . onReady ( )
88
- expect ( manager . get ( ) ) . toEqual ( { foo : 'abcd' } )
89
- } )
90
-
91
- it ( 'after being started, fetches the datafile, updates itself, emits an update event, and updates itself again after a timeout' , async ( ) => {
117
+ it ( 'after being started, fetches the datafile, updates itself, and updates itself again after a timeout' , async ( ) => {
92
118
manager . queuedResponses . push (
93
119
{
94
120
statusCode : 200 ,
@@ -106,10 +132,6 @@ describe('httpPollingDatafileManager', () => {
106
132
manager . start ( )
107
133
expect ( manager . responsePromises . length ) . toBe ( 1 )
108
134
await manager . responsePromises [ 0 ]
109
- expect ( updateFn ) . toBeCalledTimes ( 1 )
110
- expect ( updateFn ) . toBeCalledWith ( {
111
- datafile : { foo : 'bar' }
112
- } )
113
135
expect ( manager . get ( ) ) . toEqual ( { foo : 'bar' } )
114
136
updateFn . mockReset ( )
115
137
@@ -134,27 +156,15 @@ describe('httpPollingDatafileManager', () => {
134
156
expect ( manager . get ( ) ) . toEqual ( { foo : 'abcd' } )
135
157
} )
136
158
137
- it ( 'after being started, resolves onReady immediately' , async ( ) => {
138
- manager . start ( )
139
- await manager . onReady ( )
140
- expect ( manager . get ( ) ) . toEqual ( { foo : 'abcd' } )
141
- } )
142
-
143
- it ( 'after being started, fetches the datafile, updates itself once, and emits an update event, but does not schedule a future update' , async ( ) => {
159
+ it ( 'after being started, fetches the datafile, updates itself once, but does not schedule a future update' , async ( ) => {
144
160
manager . queuedResponses . push ( {
145
161
statusCode : 200 ,
146
162
body : '{"foo": "bar"}' ,
147
163
headers : { }
148
164
} )
149
- const updateFn = jest . fn ( )
150
- manager . on ( 'update' , updateFn )
151
165
manager . start ( )
152
166
expect ( manager . responsePromises . length ) . toBe ( 1 )
153
167
await manager . responsePromises [ 0 ]
154
- expect ( updateFn ) . toBeCalledTimes ( 1 )
155
- expect ( updateFn ) . toBeCalledWith ( {
156
- datafile : { foo : 'bar' }
157
- } )
158
168
expect ( manager . get ( ) ) . toEqual ( { foo : 'bar' } )
159
169
expect ( getTimerCount ( ) ) . toBe ( 0 )
160
170
} )
@@ -634,4 +644,88 @@ describe('httpPollingDatafileManager', () => {
634
644
expect ( makeGetRequestSpy ) . toBeCalledTimes ( 2 )
635
645
} )
636
646
} )
647
+
648
+ describe ( 'when constructed with a cache implementation having an already cached datafile' , ( ) => {
649
+ beforeEach ( ( ) => {
650
+ manager = new TestDatafileManager ( {
651
+ sdkKey : 'keyThatExists' ,
652
+ updateInterval : 500 ,
653
+ autoUpdate : true ,
654
+ cache : testCache ,
655
+ } )
656
+ manager . simulateResponseDelay = true
657
+ } )
658
+
659
+ it ( 'uses cached version of datafile first and resolves the promise while network throws error and no update event is triggered' , async ( ) => {
660
+ manager . queuedResponses . push ( new Error ( 'Connection Error' ) )
661
+ const updateFn = jest . fn ( )
662
+ manager . on ( 'update' , updateFn )
663
+ manager . start ( )
664
+ await manager . onReady ( )
665
+ expect ( manager . get ( ) ) . toEqual ( { name : 'keyThatExists' } )
666
+ await advanceTimersByTime ( 50 )
667
+ expect ( manager . get ( ) ) . toEqual ( { name : 'keyThatExists' } )
668
+ expect ( updateFn ) . toBeCalledTimes ( 0 )
669
+ } )
670
+
671
+ it ( 'uses cached datafile, resolves ready promise, fetches new datafile from network and triggers update event' , async ( ) => {
672
+ manager . queuedResponses . push ( {
673
+ statusCode : 200 ,
674
+ body : '{"foo": "bar"}' ,
675
+ headers : { }
676
+ } )
677
+
678
+ const updateFn = jest . fn ( )
679
+ manager . on ( 'update' , updateFn )
680
+ manager . start ( )
681
+ await manager . onReady ( )
682
+ expect ( manager . get ( ) ) . toEqual ( { name : 'keyThatExists' } )
683
+ expect ( updateFn ) . toBeCalledTimes ( 0 )
684
+ await advanceTimersByTime ( 50 )
685
+ expect ( manager . get ( ) ) . toEqual ( { foo : 'bar' } )
686
+ expect ( updateFn ) . toBeCalledTimes ( 1 )
687
+ } )
688
+
689
+ it ( 'sets newly recieved datafile in to cache' , async ( ) => {
690
+ const cacheSetSpy = jest . spyOn ( testCache , 'set' )
691
+ manager . queuedResponses . push ( {
692
+ statusCode : 200 ,
693
+ body : '{"foo": "bar"}' ,
694
+ headers : { }
695
+ } )
696
+ manager . start ( )
697
+ await manager . onReady ( )
698
+ await advanceTimersByTime ( 50 )
699
+ expect ( manager . get ( ) ) . toEqual ( { foo : 'bar' } )
700
+ expect ( cacheSetSpy ) . toBeCalledWith ( 'opt-datafile-keyThatExists' , { "foo" : "bar" } )
701
+ } )
702
+ } )
703
+
704
+ describe ( 'when constructed with a cache implementation without an already cached datafile' , ( ) => {
705
+ beforeEach ( ( ) => {
706
+ manager = new TestDatafileManager ( {
707
+ sdkKey : 'keyThatDoesExists' ,
708
+ updateInterval : 500 ,
709
+ autoUpdate : true ,
710
+ cache : testCache ,
711
+ } )
712
+ manager . simulateResponseDelay = true
713
+ } )
714
+
715
+ it ( 'does not find cached datafile, fetches new datafile from network, resolves promise and does not trigger update event' , async ( ) => {
716
+ manager . queuedResponses . push ( {
717
+ statusCode : 200 ,
718
+ body : '{"foo": "bar"}' ,
719
+ headers : { }
720
+ } )
721
+
722
+ const updateFn = jest . fn ( )
723
+ manager . on ( 'update' , updateFn )
724
+ manager . start ( )
725
+ await advanceTimersByTime ( 50 )
726
+ await manager . onReady ( )
727
+ expect ( manager . get ( ) ) . toEqual ( { foo : 'bar' } )
728
+ expect ( updateFn ) . toBeCalledTimes ( 0 )
729
+ } )
730
+ } )
637
731
} )
0 commit comments