@@ -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 ( ) => {
@@ -139,6 +150,8 @@ describe("dynamic refresh", function () {
139150 ]
140151 }
141152 } ) ;
153+ expect ( listKvRequestCount ) . eq ( 1 ) ;
154+ expect ( getKvRequestCount ) . eq ( 0 ) ;
142155 expect ( settings ) . not . undefined ;
143156 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
144157 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
@@ -149,10 +162,14 @@ describe("dynamic refresh", function () {
149162 // within refreshInterval, should not really refresh
150163 await settings . refresh ( ) ;
151164 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
165+ expect ( listKvRequestCount ) . eq ( 1 ) ; // no more request should be sent during the refresh interval
166+ expect ( getKvRequestCount ) . eq ( 0 ) ; // no more request should be sent during the refresh interval
152167
153168 // after refreshInterval, should really refresh
154169 await sleepInMs ( 2 * 1000 + 1 ) ;
155170 await settings . refresh ( ) ;
171+ expect ( listKvRequestCount ) . eq ( 2 ) ;
172+ expect ( getKvRequestCount ) . eq ( 1 ) ;
156173 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "blue" ) ;
157174 } ) ;
158175
@@ -167,18 +184,22 @@ describe("dynamic refresh", function () {
167184 ]
168185 }
169186 } ) ;
187+ expect ( listKvRequestCount ) . eq ( 1 ) ;
188+ expect ( getKvRequestCount ) . eq ( 0 ) ;
170189 expect ( settings ) . not . undefined ;
171190 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
172191 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
173192
174193 // delete setting 'app.settings.fontColor'
175194 const newMockedKVs = mockedKVs . filter ( elem => elem . key !== "app.settings.fontColor" ) ;
176195 restoreMocks ( ) ;
177- mockAppConfigurationClientListConfigurationSettings ( newMockedKVs ) ;
178- mockAppConfigurationClientGetConfigurationSetting ( newMockedKVs ) ;
196+ mockAppConfigurationClientListConfigurationSettings ( [ newMockedKVs ] , listKvCallback ) ;
197+ mockAppConfigurationClientGetConfigurationSetting ( newMockedKVs , getKvCallback ) ;
179198
180199 await sleepInMs ( 2 * 1000 + 1 ) ;
181200 await settings . refresh ( ) ;
201+ expect ( listKvRequestCount ) . eq ( 2 ) ;
202+ 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)
182203 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( undefined ) ;
183204 } ) ;
184205
@@ -193,13 +214,17 @@ describe("dynamic refresh", function () {
193214 ]
194215 }
195216 } ) ;
217+ expect ( listKvRequestCount ) . eq ( 1 ) ;
218+ expect ( getKvRequestCount ) . eq ( 0 ) ;
196219 expect ( settings ) . not . undefined ;
197220 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
198221 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
199222
200223 updateSetting ( "app.settings.fontSize" , "50" ) ; // unwatched setting
201224 await sleepInMs ( 2 * 1000 + 1 ) ;
202225 await settings . refresh ( ) ;
226+ expect ( listKvRequestCount ) . eq ( 1 ) ;
227+ expect ( getKvRequestCount ) . eq ( 1 ) ;
203228 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
204229 } ) ;
205230
@@ -215,6 +240,8 @@ describe("dynamic refresh", function () {
215240 ]
216241 }
217242 } ) ;
243+ expect ( listKvRequestCount ) . eq ( 1 ) ;
244+ expect ( getKvRequestCount ) . eq ( 0 ) ;
218245 expect ( settings ) . not . undefined ;
219246 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
220247 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
@@ -224,6 +251,8 @@ describe("dynamic refresh", function () {
224251 updateSetting ( "app.settings.fontSize" , "50" ) ;
225252 await sleepInMs ( 2 * 1000 + 1 ) ;
226253 await settings . refresh ( ) ;
254+ expect ( listKvRequestCount ) . eq ( 2 ) ;
255+ expect ( getKvRequestCount ) . eq ( 2 ) ; // two getKv request for two watched settings
227256 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "50" ) ;
228257 expect ( settings . get ( "app.settings.bgColor" ) ) . eq ( "white" ) ;
229258 } ) ;
@@ -309,6 +338,8 @@ describe("dynamic refresh", function () {
309338 ]
310339 }
311340 } ) ;
341+ expect ( listKvRequestCount ) . eq ( 1 ) ;
342+ 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.
312343 expect ( settings ) . not . undefined ;
313344 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
314345 expect ( settings . get ( "app.settings.fontSize" ) ) . eq ( "40" ) ;
@@ -317,10 +348,45 @@ describe("dynamic refresh", function () {
317348 updateSetting ( "app.settings.fontColor" , "blue" ) ;
318349 await sleepInMs ( 2 * 1000 + 1 ) ;
319350 await settings . refresh ( ) ;
351+ expect ( listKvRequestCount ) . eq ( 1 ) ;
352+ expect ( getKvRequestCount ) . eq ( 2 ) ;
320353 // should not refresh
321354 expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
322355 } ) ;
323356
357+ it ( "should not refresh any more when there is refresh in progress" , async ( ) => {
358+ const connectionString = createMockedConnectionString ( ) ;
359+ const settings = await load ( connectionString , {
360+ refreshOptions : {
361+ enabled : true ,
362+ refreshIntervalInMs : 2000 ,
363+ watchedSettings : [
364+ { key : "app.settings.fontColor" }
365+ ]
366+ }
367+ } ) ;
368+ expect ( listKvRequestCount ) . eq ( 1 ) ;
369+ expect ( getKvRequestCount ) . eq ( 0 ) ;
370+ expect ( settings ) . not . undefined ;
371+ expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "red" ) ;
372+
373+ // change setting
374+ updateSetting ( "app.settings.fontColor" , "blue" ) ;
375+
376+ // after refreshInterval, should really refresh
377+ await sleepInMs ( 2 * 1000 + 1 ) ;
378+ for ( let i = 0 ; i < 5 ; i ++ ) { // in practice, refresh should not be used in this way
379+ settings . refresh ( ) ; // refresh "concurrently"
380+ }
381+ expect ( listKvRequestCount ) . to . be . at . most ( 2 ) ;
382+ expect ( getKvRequestCount ) . to . be . at . most ( 1 ) ;
383+
384+ await sleepInMs ( 1000 ) ; // wait for all 5 refresh attempts to finish
385+
386+ expect ( listKvRequestCount ) . eq ( 2 ) ;
387+ expect ( getKvRequestCount ) . eq ( 1 ) ;
388+ expect ( settings . get ( "app.settings.fontColor" ) ) . eq ( "blue" ) ;
389+ } ) ;
324390} ) ;
325391
326392describe ( "dynamic refresh feature flags" , function ( ) {
@@ -331,14 +397,16 @@ describe("dynamic refresh feature flags", function () {
331397
332398 afterEach ( ( ) => {
333399 restoreMocks ( ) ;
400+ listKvRequestCount = 0 ;
401+ getKvRequestCount = 0 ;
334402 } ) ;
335403
336404 it ( "should refresh feature flags when enabled" , async ( ) => {
337405 mockedKVs = [
338406 createMockedFeatureFlag ( "Beta" , { enabled : true } )
339407 ] ;
340- mockAppConfigurationClientListConfigurationSettings ( mockedKVs ) ;
341- mockAppConfigurationClientGetConfigurationSetting ( mockedKVs ) ;
408+ mockAppConfigurationClientListConfigurationSettings ( [ mockedKVs ] , listKvCallback ) ;
409+ mockAppConfigurationClientGetConfigurationSetting ( mockedKVs , getKvCallback ) ;
342410
343411 const connectionString = createMockedConnectionString ( ) ;
344412 const settings = await load ( connectionString , {
@@ -353,6 +421,8 @@ describe("dynamic refresh feature flags", function () {
353421 }
354422 }
355423 } ) ;
424+ expect ( listKvRequestCount ) . eq ( 2 ) ; // one listKv request for kvs and one listKv request for feature flags
425+ expect ( getKvRequestCount ) . eq ( 0 ) ;
356426 expect ( settings ) . not . undefined ;
357427 expect ( settings . get ( "feature_management" ) ) . not . undefined ;
358428 expect ( settings . get < any > ( "feature_management" ) . feature_flags ) . not . undefined ;
@@ -371,6 +441,8 @@ describe("dynamic refresh feature flags", function () {
371441
372442 await sleepInMs ( 2 * 1000 + 1 ) ;
373443 await settings . refresh ( ) ;
444+ expect ( listKvRequestCount ) . eq ( 4 ) ; // 2 + 2 more requests: one conditional request to detect change and one request to reload all feature flags
445+ expect ( getKvRequestCount ) . eq ( 0 ) ;
374446
375447 expect ( settings . get < any > ( "feature_management" ) . feature_flags [ 0 ] . id ) . eq ( "Beta" ) ;
376448 expect ( settings . get < any > ( "feature_management" ) . feature_flags [ 0 ] . enabled ) . eq ( false ) ;
@@ -387,8 +459,8 @@ describe("dynamic refresh feature flags", function () {
387459 createMockedFeatureFlag ( "Beta_1" , { enabled : true } ) ,
388460 createMockedFeatureFlag ( "Beta_2" , { enabled : true } ) ,
389461 ] ;
390- mockAppConfigurationClientListConfigurationSettings ( page1 , page2 ) ;
391- mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] ) ;
462+ mockAppConfigurationClientListConfigurationSettings ( [ page1 , page2 ] , listKvCallback ) ;
463+ mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] , getKvCallback ) ;
392464
393465 const connectionString = createMockedConnectionString ( ) ;
394466 const settings = await load ( connectionString , {
@@ -403,6 +475,8 @@ describe("dynamic refresh feature flags", function () {
403475 }
404476 }
405477 } ) ;
478+ expect ( listKvRequestCount ) . eq ( 2 ) ;
479+ expect ( getKvRequestCount ) . eq ( 0 ) ;
406480
407481 let refreshSuccessfulCount = 0 ;
408482 settings . onRefresh ( ( ) => {
@@ -411,16 +485,20 @@ describe("dynamic refresh feature flags", function () {
411485
412486 await sleepInMs ( 2 * 1000 + 1 ) ;
413487 await settings . refresh ( ) ;
488+ expect ( listKvRequestCount ) . eq ( 3 ) ; // one conditional request to detect change
489+ expect ( getKvRequestCount ) . eq ( 0 ) ;
414490 expect ( refreshSuccessfulCount ) . eq ( 0 ) ; // no change in feature flags, because page etags are the same.
415491
416492 // change feature flag Beta_1 to false
417493 page2 [ 0 ] = createMockedFeatureFlag ( "Beta_1" , { enabled : false } ) ;
418494 restoreMocks ( ) ;
419- mockAppConfigurationClientListConfigurationSettings ( page1 , page2 ) ;
420- mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] ) ;
495+ mockAppConfigurationClientListConfigurationSettings ( [ page1 , page2 ] , listKvCallback ) ;
496+ mockAppConfigurationClientGetConfigurationSetting ( [ ...page1 , ...page2 ] , getKvCallback ) ;
421497
422498 await sleepInMs ( 2 * 1000 + 1 ) ;
423499 await settings . refresh ( ) ;
500+ expect ( listKvRequestCount ) . eq ( 5 ) ; // 3 + 2 more requests: one conditional request to detect change and one request to reload all feature flags
501+ expect ( getKvRequestCount ) . eq ( 0 ) ;
424502 expect ( refreshSuccessfulCount ) . eq ( 1 ) ; // change in feature flags, because page etags are different.
425503 } ) ;
426504} ) ;
0 commit comments