@@ -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