@@ -23,6 +23,15 @@ function addSetting(key: string, value: any) {
2323 mockedKVs . push ( createMockedKeyValue ( { key, value } ) ) ;
2424}
2525
26+ let listKvRequestCount = 0 ;
27+ const listKvCallback = ( ) => {
28+ listKvRequestCount ++ ;
29+ } ;
30+ let getKvRequestCount = 0 ;
31+ const getKvCallback = ( ) => {
32+ getKvRequestCount ++ ;
33+ } ;
34+
2635describe ( "dynamic refresh" , function ( ) {
2736 this . timeout ( 10000 ) ;
2837
@@ -32,12 +41,14 @@ describe("dynamic refresh", function () {
3241 { value : "40" , key : "app.settings.fontSize" } ,
3342 { value : "30" , key : "app.settings.fontSize" , label : "prod" }
3443 ] . map ( createMockedKeyValue ) ;
35- mockAppConfigurationClientListConfigurationSettings ( mockedKVs ) ;
36- mockAppConfigurationClientGetConfigurationSetting ( mockedKVs ) ;
44+ mockAppConfigurationClientListConfigurationSettings ( [ mockedKVs ] , listKvCallback ) ;
45+ mockAppConfigurationClientGetConfigurationSetting ( mockedKVs , getKvCallback ) ;
3746 } ) ;
3847
3948 afterEach ( ( ) => {
4049 restoreMocks ( ) ;
50+ listKvRequestCount = 0 ;
51+ getKvRequestCount = 0 ;
4152 } ) ;
4253
4354 it ( "should throw error when refresh is not enabled but refresh is called" , async ( ) => {
@@ -120,6 +131,8 @@ describe("dynamic refresh", function () {
120131 ]
121132 }
122133 } ) ;
134+ expect ( listKvRequestCount ) . eq ( 1 ) ;
135+ expect ( getKvRequestCount ) . eq ( 0 ) ;
123136 expect ( settings ) . not . undefined ;
124137 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
125138 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
@@ -130,10 +143,14 @@ describe("dynamic refresh", function () {
130143 // within refreshInterval, should not really refresh
131144 await settings . refresh ( ) ;
132145 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
146+ expect ( listKvRequestCount ) . eq ( 1 ) ; // no more request should be sent during the refresh interval
147+ expect ( getKvRequestCount ) . eq ( 0 ) ; // no more request should be sent during the refresh interval
133148
134149 // after refreshInterval, should really refresh
135150 await sleepInMs ( 2 * 1000 + 1 ) ;
136151 await settings . refresh ( ) ;
152+ expect ( listKvRequestCount ) . eq ( 2 ) ;
153+ expect ( getKvRequestCount ) . eq ( 1 ) ;
137154 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "blue" ) ;
138155 } ) ;
139156
@@ -148,18 +165,22 @@ describe("dynamic refresh", function () {
148165 ]
149166 }
150167 } ) ;
168+ expect ( listKvRequestCount ) . eq ( 1 ) ;
169+ expect ( getKvRequestCount ) . eq ( 0 ) ;
151170 expect ( settings ) . not . undefined ;
152171 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
153172 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
154173
155174 // delete setting 'app.settings.fontColor'
156175 const newMockedKVs = mockedKVs . filter ( elem => elem . key !== "app.settings.fontColor" ) ;
157176 restoreMocks ( ) ;
158- mockAppConfigurationClientListConfigurationSettings ( newMockedKVs ) ;
159- mockAppConfigurationClientGetConfigurationSetting ( newMockedKVs ) ;
177+ mockAppConfigurationClientListConfigurationSettings ( [ newMockedKVs ] , listKvCallback ) ;
178+ mockAppConfigurationClientGetConfigurationSetting ( newMockedKVs , getKvCallback ) ;
160179
161180 await sleepInMs ( 2 * 1000 + 1 ) ;
162181 await settings . refresh ( ) ;
182+ expect ( listKvRequestCount ) . eq ( 2 ) ;
183+ expect ( getKvRequestCount ) . eq ( 2 ) ; // one conditional request to detect change and one request as part of loading all kvs (because app.settings.fontColor doesn't exist in the response of listKv request)
163184 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( undefined ) ;
164185 } ) ;
165186
@@ -174,13 +195,17 @@ describe("dynamic refresh", function () {
174195 ]
175196 }
176197 } ) ;
198+ expect ( listKvRequestCount ) . eq ( 1 ) ;
199+ expect ( getKvRequestCount ) . eq ( 0 ) ;
177200 expect ( settings ) . not . undefined ;
178201 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
179202 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
180203
181204 updateSetting ( "app.settings.fontSize" , "50" ) ; // unwatched setting
182205 await sleepInMs ( 2 * 1000 + 1 ) ;
183206 await settings . refresh ( ) ;
207+ expect ( listKvRequestCount ) . eq ( 1 ) ;
208+ expect ( getKvRequestCount ) . eq ( 1 ) ;
184209 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
185210 } ) ;
186211
@@ -196,6 +221,8 @@ describe("dynamic refresh", function () {
196221 ]
197222 }
198223 } ) ;
224+ expect ( listKvRequestCount ) . eq ( 1 ) ;
225+ expect ( getKvRequestCount ) . eq ( 0 ) ;
199226 expect ( settings ) . not . undefined ;
200227 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
201228 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
@@ -205,6 +232,8 @@ describe("dynamic refresh", function () {
205232 updateSetting ( "app.settings.fontSize" , "50" ) ;
206233 await sleepInMs ( 2 * 1000 + 1 ) ;
207234 await settings . refresh ( ) ;
235+ expect ( listKvRequestCount ) . eq ( 2 ) ;
236+ expect ( getKvRequestCount ) . eq ( 2 ) ; // two getKv request for two watched settings
208237 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "50" ) ;
209238 expect ( settings . get ( "app.settings.bgColor" ) ) . eq ( "white" ) ;
210239 } ) ;
@@ -290,6 +319,8 @@ describe("dynamic refresh", function () {
290319 ]
291320 }
292321 } ) ;
322+ expect ( listKvRequestCount ) . eq ( 1 ) ;
323+ expect ( getKvRequestCount ) . eq ( 1 ) ; // app.settings.bgColor doesn't exist in the response of listKv request, so an additional getKv request is made to get it.
293324 expect ( settings ) . not . undefined ;
294325 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
295326 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
@@ -298,6 +329,8 @@ describe("dynamic refresh", function () {
298329 updateSetting ( "app.settings.fontColor" , "blue" ) ;
299330 await sleepInMs ( 2 * 1000 + 1 ) ;
300331 await settings . refresh ( ) ;
332+ expect ( listKvRequestCount ) . eq ( 1 ) ;
333+ expect ( getKvRequestCount ) . eq ( 2 ) ;
301334 // should not refresh
302335 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
303336 } ) ;
@@ -310,6 +343,8 @@ describe("dynamic refresh", function () {
310343 refreshIntervalInMs : 2000
311344 }
312345 } ) ;
346+ expect ( listKvRequestCount ) . eq ( 1 ) ;
347+ expect ( getKvRequestCount ) . eq ( 0 ) ;
313348 expect ( settings ) . not . undefined ;
314349 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
315350 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
@@ -320,6 +355,8 @@ describe("dynamic refresh", function () {
320355 // after refreshInterval, should really refresh
321356 await sleepInMs ( 2 * 1000 + 1 ) ;
322357 await settings . refresh ( ) ;
358+ expect ( listKvRequestCount ) . eq ( 3 ) ; // 1 + 2 more requests: one conditional request to detect change and one request to reload all key values
359+ expect ( getKvRequestCount ) . eq ( 0 ) ;
323360 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "blue" ) ;
324361 } ) ;
325362
@@ -331,6 +368,8 @@ describe("dynamic refresh", function () {
331368 refreshIntervalInMs : 2000
332369 }
333370 } ) ;
371+ expect ( listKvRequestCount ) . eq ( 1 ) ;
372+ expect ( getKvRequestCount ) . eq ( 0 ) ;
334373
335374 let refreshSuccessfulCount = 0 ;
336375 settings . onRefresh ( ( ) => {
@@ -342,22 +381,60 @@ describe("dynamic refresh", function () {
342381
343382 await sleepInMs ( 2 * 1000 + 1 ) ;
344383 await settings . refresh ( ) ;
345- expect ( refreshSuccessfulCount ) . eq ( 0 ) ; // no change in feature flags, because page etags are the same.
384+ expect ( listKvRequestCount ) . eq ( 2 ) ; // one more conditional request to detect change
385+ expect ( getKvRequestCount ) . eq ( 0 ) ;
386+ expect ( refreshSuccessfulCount ) . eq ( 0 ) ; // no change in key values, because page etags are the same.
346387
347388 // change key value
348389 restoreMocks ( ) ;
349390 const changedKVs = [
350391 { value : "blue" , key : "app.settings.fontColor" } ,
351392 { value : "40" , key : "app.settings.fontSize" }
352393 ] . map ( createMockedKeyValue ) ;
353- mockAppConfigurationClientListConfigurationSettings ( changedKVs ) ;
354- mockAppConfigurationClientGetConfigurationSetting ( changedKVs ) ;
394+ mockAppConfigurationClientListConfigurationSettings ( [ changedKVs ] , listKvCallback ) ;
395+ mockAppConfigurationClientGetConfigurationSetting ( changedKVs , getKvCallback ) ;
355396
356397 await sleepInMs ( 2 * 1000 + 1 ) ;
357398 await settings . refresh ( ) ;
399+ expect ( listKvRequestCount ) . eq ( 4 ) ; // 2 + 2 more requests: one conditional request to detect change and one request to reload all key values
400+ expect ( getKvRequestCount ) . eq ( 0 ) ;
358401 expect ( refreshSuccessfulCount ) . eq ( 1 ) ; // change in key values, because page etags are different.
359402 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "blue" ) ;
360403 } ) ;
404+
405+ it ( "should not refresh any more when there is refresh in progress" , async ( ) => {
406+ const connectionString = createMockedConnectionString ( ) ;
407+ const settings = await load ( connectionString , {
408+ refreshOptions : {
409+ enabled : true ,
410+ refreshIntervalInMs : 2000 ,
411+ watchedSettings : [
412+ { key : "app.settings.fontColor" }
413+ ]
414+ }
415+ } ) ;
416+ expect ( listKvRequestCount ) . eq ( 1 ) ;
417+ expect ( getKvRequestCount ) . eq ( 0 ) ;
418+ expect ( settings ) . not . undefined ;
419+ expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
420+
421+ // change setting
422+ updateSetting ( "app.settings.fontColor" , "blue" ) ;
423+
424+ // after refreshInterval, should really refresh
425+ await sleepInMs ( 2 * 1000 + 1 ) ;
426+ for ( let i = 0 ; i < 5 ; i ++ ) { // in practice, refresh should not be used in this way
427+ settings . refresh ( ) ; // refresh "concurrently"
428+ }
429+ expect ( listKvRequestCount ) . to . be . at . most ( 2 ) ;
430+ expect ( getKvRequestCount ) . to . be . at . most ( 1 ) ;
431+
432+ await sleepInMs ( 1000 ) ; // wait for all 5 refresh attempts to finish
433+
434+ expect ( listKvRequestCount ) . eq ( 2 ) ;
435+ expect ( getKvRequestCount ) . eq ( 1 ) ;
436+ expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "blue" ) ;
437+ } ) ;
361438} ) ;
362439
363440describe ( "dynamic refresh feature flags" , function ( ) {
@@ -368,14 +445,16 @@ describe("dynamic refresh feature flags", function () {
368445
369446 afterEach ( ( ) => {
370447 restoreMocks ( ) ;
448+ listKvRequestCount = 0 ;
449+ getKvRequestCount = 0 ;
371450 } ) ;
372451
373452 it ( "should refresh feature flags when enabled" , async ( ) => {
374453 mockedKVs = [
375454 createMockedFeatureFlag ( "Beta" , { enabled : true } )
376455 ] ;
377- mockAppConfigurationClientListConfigurationSettings ( mockedKVs ) ;
378- mockAppConfigurationClientGetConfigurationSetting ( mockedKVs ) ;
456+ mockAppConfigurationClientListConfigurationSettings ( [ mockedKVs ] , listKvCallback ) ;
457+ mockAppConfigurationClientGetConfigurationSetting ( mockedKVs , getKvCallback ) ;
379458
380459 const connectionString = createMockedConnectionString ( ) ;
381460 const settings = await load ( connectionString , {
@@ -390,6 +469,8 @@ describe("dynamic refresh feature flags", function () {
390469 }
391470 }
392471 } ) ;
472+ expect ( listKvRequestCount ) . eq ( 2 ) ; // one listKv request for kvs and one listKv request for feature flags
473+ expect ( getKvRequestCount ) . eq ( 0 ) ;
393474 expect ( settings ) . not . undefined ;
394475 expect ( settings . get ( "feature_management" ) ) . not . undefined ;
395476 expect ( settings . get < any > ( "feature_management" ) . feature_flags ) . not . undefined ;
@@ -408,6 +489,8 @@ describe("dynamic refresh feature flags", function () {
408489
409490 await sleepInMs ( 2 * 1000 + 1 ) ;
410491 await settings . refresh ( ) ;
492+ expect ( listKvRequestCount ) . eq ( 4 ) ; // 2 + 2 more requests: one conditional request to detect change and one request to reload all feature flags
493+ expect ( getKvRequestCount ) . eq ( 0 ) ;
411494
412495 expect ( settings . get < any > ( "feature_management" ) . feature_flags [ 0 ] . id ) . eq ( "Beta" ) ;
413496 expect ( settings . get < any > ( "feature_management" ) . feature_flags [ 0 ] . enabled ) . eq ( false ) ;
@@ -424,8 +507,8 @@ describe("dynamic refresh feature flags", function () {
424507 createMockedFeatureFlag ( "Beta_1" , { enabled : true } ) ,
425508 createMockedFeatureFlag ( "Beta_2" , { enabled : true } ) ,
426509 ] ;
427- mockAppConfigurationClientListConfigurationSettings ( page1 , page2 ) ;
428- mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] ) ;
510+ mockAppConfigurationClientListConfigurationSettings ( [ page1 , page2 ] , listKvCallback ) ;
511+ mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] , getKvCallback ) ;
429512
430513 const connectionString = createMockedConnectionString ( ) ;
431514 const settings = await load ( connectionString , {
@@ -440,6 +523,8 @@ describe("dynamic refresh feature flags", function () {
440523 }
441524 }
442525 } ) ;
526+ expect ( listKvRequestCount ) . eq ( 2 ) ;
527+ expect ( getKvRequestCount ) . eq ( 0 ) ;
443528
444529 let refreshSuccessfulCount = 0 ;
445530 settings . onRefresh ( ( ) => {
@@ -448,16 +533,20 @@ describe("dynamic refresh feature flags", function () {
448533
449534 await sleepInMs ( 2 * 1000 + 1 ) ;
450535 await settings . refresh ( ) ;
536+ expect ( listKvRequestCount ) . eq ( 3 ) ; // one conditional request to detect change
537+ expect ( getKvRequestCount ) . eq ( 0 ) ;
451538 expect ( refreshSuccessfulCount ) . eq ( 0 ) ; // no change in feature flags, because page etags are the same.
452539
453540 // change feature flag Beta_1 to false
454541 page2 [ 0 ] = createMockedFeatureFlag ( "Beta_1" , { enabled : false } ) ;
455542 restoreMocks ( ) ;
456- mockAppConfigurationClientListConfigurationSettings ( page1 , page2 ) ;
457- mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] ) ;
543+ mockAppConfigurationClientListConfigurationSettings ( [ page1 , page2 ] , listKvCallback ) ;
544+ mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] , getKvCallback ) ;
458545
459546 await sleepInMs ( 2 * 1000 + 1 ) ;
460547 await settings . refresh ( ) ;
548+ expect ( listKvRequestCount ) . eq ( 5 ) ; // 3 + 2 more requests: one conditional request to detect change and one request to reload all feature flags
549+ expect ( getKvRequestCount ) . eq ( 0 ) ;
461550 expect ( refreshSuccessfulCount ) . eq ( 1 ) ; // change in feature flags, because page etags are different.
462551 } ) ;
463552} ) ;
0 commit comments