Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/appConfigurationImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {

for await (const page of pageIterator) {
// when conditional request is sent, the response will be 304 if not changed
if (page._response.status === 200) { // created or changed
if (Number(page._response.status) === 200) { // created or changed
return true;
}
}
Expand Down
44 changes: 43 additions & 1 deletion test/refresh.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import chaiAsPromised from "chai-as-promised";
chai.use(chaiAsPromised);
const expect = chai.expect;
import { load } from "../src/index.js";
import { mockAppConfigurationClientListConfigurationSettings, mockAppConfigurationClientGetConfigurationSetting, restoreMocks, createMockedConnectionString, createMockedKeyValue, sleepInMs, createMockedFeatureFlag } from "./utils/testHelper.js";
import { mockAppConfigurationClientListConfigurationSettings, mockAppConfigurationClientListConfigurationSettingsWithStringStatus, mockAppConfigurationClientGetConfigurationSetting, restoreMocks, createMockedConnectionString, createMockedKeyValue, sleepInMs, createMockedFeatureFlag } from "./utils/testHelper.js";
import * as uuid from "uuid";

let mockedKVs: any[] = [];
Expand Down Expand Up @@ -574,6 +574,48 @@ describe("dynamic refresh feature flags", function () {

});

it("should refresh feature flags when page response status is string", async () => {
mockedKVs = [
createMockedFeatureFlag("Beta", { enabled: true })
];
mockAppConfigurationClientListConfigurationSettingsWithStringStatus([mockedKVs], listKvCallback);
mockAppConfigurationClientGetConfigurationSetting(mockedKVs, getKvCallback);

const connectionString = createMockedConnectionString();
const settings = await load(connectionString, {
featureFlagOptions: {
enabled: true,
selectors: [{
keyFilter: "Beta"
}],
refresh: {
enabled: true,
refreshIntervalInMs: 2000 // 2 seconds for quick test.
}
}
});
expect(listKvRequestCount).eq(2); // one listKv request for kvs and one listKv request for feature flags
expect(getKvRequestCount).eq(0);
expect(settings).not.undefined;
expect(settings.get<any>("feature_management").feature_flags[0].enabled).eq(true);

// change feature flag Beta to false
updateSetting(".appconfig.featureflag/Beta", JSON.stringify({
"id": "Beta",
"description": "",
"enabled": false,
"conditions": {
"client_filters": []
}
}));

await sleepInMs(2 * 1000 + 1);
await settings.refresh();
expect(listKvRequestCount).eq(4); // 2 + 2 more requests: one conditional request to detect change and one request to reload all feature flags
expect(getKvRequestCount).eq(0);
expect(settings.get<any>("feature_management").feature_flags[0].enabled).eq(false);
});

it("should refresh feature flags based on page etags, only on change", async () => {
// mock multiple pages of feature flags
const page1 = [
Expand Down
17 changes: 15 additions & 2 deletions test/utils/testHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function _filterKVs(unfilteredKvs: ConfigurationSetting[], listOptions: any) {
});
}

function getMockedIterator(pages: ConfigurationSetting[][], kvs: ConfigurationSetting[], listOptions: any) {
function getMockedIterator(pages: ConfigurationSetting[][], kvs: ConfigurationSetting[], listOptions: any, useStringStatus: boolean = false) {
const mockIterator: AsyncIterableIterator<any> & { byPage(): AsyncIterableIterator<any> } = {
[Symbol.asyncIterator](): AsyncIterableIterator<any> {
kvs = _filterKVs(pages.flat(), listOptions);
Expand Down Expand Up @@ -95,7 +95,7 @@ function getMockedIterator(pages: ConfigurationSetting[][], kvs: ConfigurationSe
value: {
items,
etag,
_response: { status: statusCode }
_response: { status: useStringStatus ? `${statusCode}` : statusCode }
}
};
}
Expand Down Expand Up @@ -126,6 +126,18 @@ function mockAppConfigurationClientListConfigurationSettings(pages: Configuratio
});
}

function mockAppConfigurationClientListConfigurationSettingsWithStringStatus(pages: ConfigurationSetting[][], customCallback?: (listOptions) => any) {

sinon.stub(AppConfigurationClient.prototype, "listConfigurationSettings").callsFake((listOptions) => {
if (customCallback) {
customCallback(listOptions);
}

const kvs = _filterKVs(pages.flat(), listOptions);
return getMockedIterator(pages, kvs, listOptions, true);
});
}

function mockAppConfigurationClientLoadBalanceMode(pages: ConfigurationSetting[][], clientWrapper: ConfigurationClientWrapper, countObject: { count: number }) {
sinon.stub(clientWrapper.client, "listConfigurationSettings").callsFake((listOptions) => {
countObject.count += 1;
Expand Down Expand Up @@ -323,6 +335,7 @@ class HttpRequestHeadersPolicy {
export {
sinon,
mockAppConfigurationClientListConfigurationSettings,
mockAppConfigurationClientListConfigurationSettingsWithStringStatus,
mockAppConfigurationClientGetConfigurationSetting,
mockAppConfigurationClientGetSnapshot,
mockAppConfigurationClientListConfigurationSettingsForSnapshot,
Expand Down