From 29e5bc1f291deaaa2874d06ba1e1dd1ec21c275e Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Mon, 25 Sep 2023 23:48:25 -0700 Subject: [PATCH 1/5] Update AISku.ts --- AISKU/src/AISku.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/AISKU/src/AISku.ts b/AISKU/src/AISku.ts index 87240638b..02319ee98 100644 --- a/AISKU/src/AISku.ts +++ b/AISKU/src/AISku.ts @@ -9,15 +9,16 @@ import { Sender } from "@microsoft/applicationinsights-channel-js"; import { AnalyticsPluginIdentifier, DEFAULT_BREEZE_PATH, IAutoExceptionTelemetry, IConfig, IDependencyTelemetry, IEventTelemetry, IExceptionTelemetry, IMetricTelemetry, IPageViewPerformanceTelemetry, IPageViewTelemetry, IRequestHeaders, - ITelemetryContext as Common_ITelemetryContext, ITraceTelemetry, PropertiesPluginIdentifier, ThrottleMgr, parseConnectionString + ITelemetryContext as Common_ITelemetryContext, IThrottleMgrConfig, ITraceTelemetry, PropertiesPluginIdentifier, ThrottleMgr, + parseConnectionString } from "@microsoft/applicationinsights-common"; import { AppInsightsCore, FeatureOptInMode, IAppInsightsCore, IChannelControls, IConfigDefaults, IConfiguration, ICookieMgr, ICustomProperties, IDiagnosticLogger, IDistributedTraceContext, IDynamicConfigHandler, ILoadedPlugin, INotificationManager, IPlugin, ITelemetryInitializerHandler, ITelemetryItem, ITelemetryPlugin, ITelemetryUnloadState, IUnloadHook, UnloadHandler, WatcherFunction, - _eInternalMessageId, _throwInternal, addPageHideEventListener, addPageUnloadEventListener, cfgDfValidate, createDynamicConfig, - createProcessTelemetryContext, createUniqueNamespace, doPerf, eLoggingSeverity, hasDocument, hasWindow, isArray, isFeatureEnabled, - isFunction, isNullOrUndefined, isReactNative, isString, mergeEvtNamespace, onConfigChange, proxyAssign, proxyFunctions, + _eInternalMessageId, _throwInternal, addPageHideEventListener, addPageUnloadEventListener, cfgDfMerge, cfgDfValidate, + createDynamicConfig, createProcessTelemetryContext, createUniqueNamespace, doPerf, eLoggingSeverity, hasDocument, hasWindow, isArray, + isFeatureEnabled, isFunction, isNullOrUndefined, isReactNative, isString, mergeEvtNamespace, onConfigChange, proxyAssign, proxyFunctions, removePageHideEventListener, removePageUnloadEventListener } from "@microsoft/applicationinsights-core-js"; import { @@ -62,7 +63,7 @@ const default_throttle_config = { monthInterval: 3, daysOfMonth: [28] } -}; +} as IThrottleMgrConfig; // We need to include all properties that we only reference that we want to be dynamically updatable here // So they are converted even when not specified in the passed configuration @@ -76,12 +77,14 @@ const defaultConfigValues: IConfigDefaults = { [CDN_USAGE]: {mode: FeatureOptInMode.disable}, [SDK_LOADER_VER]: {mode: FeatureOptInMode.disable} }, - throttleMgrCfg:{ - [_eInternalMessageId.DefaultThrottleMsgKey]:default_throttle_config, - [_eInternalMessageId.InstrumentationKeyDeprecation]:default_throttle_config, - [_eInternalMessageId.SdkLdrUpdate]:default_throttle_config, - [_eInternalMessageId.CdnDeprecation]:default_throttle_config - } + throttleMgrCfg: cfgDfMerge<{[key:number]: IThrottleMgrConfig}>( + { + [_eInternalMessageId.DefaultThrottleMsgKey]:cfgDfMerge(default_throttle_config), + [_eInternalMessageId.InstrumentationKeyDeprecation]:cfgDfMerge(default_throttle_config), + [_eInternalMessageId.SdkLdrUpdate]:cfgDfMerge(default_throttle_config), + [_eInternalMessageId.CdnDeprecation]:cfgDfMerge(default_throttle_config) + } + ) }; function _chkDiagLevel(value: number) { From 06083fe0d9d497496abda85f18be8181cbee0e65 Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Tue, 26 Sep 2023 01:54:51 -0700 Subject: [PATCH 2/5] Update ThrottleSentMessage.tests.ts --- AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts b/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts index f45ba20b0..1cede5c1b 100644 --- a/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts +++ b/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts @@ -17,6 +17,7 @@ const tconfig = { } as IThrottleLimit, interval: { monthInterval: 1, + daysOfMonth: [1], // must add here dayInterval: undefined } as IThrottleInterval } as IThrottleMgrConfig; @@ -75,9 +76,9 @@ export class ThrottleSentMessage extends AITestClass { } public registerTests() { + this.cdnDeprecatedMessageTests(); this.ikeyMessageTests(); this.snippetVerMessageTests(); - this.cdnDeprecatedMessageTests(); } public cdnDeprecatedMessageTests(): void { From 372693bcd54e14834558704ffba301e983baf4c6 Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Mon, 2 Oct 2023 16:08:45 -0700 Subject: [PATCH 3/5] add config overwrite test --- AISKU/Tests/Unit/src/CdnThrottle.tests.ts | 278 ++++++++++++++++++ .../Unit/src/ThrottleSentMessage.tests.ts | 2 +- AISKU/Tests/Unit/src/aiskuunittests.ts | 4 + AISKU/src/AISku.ts | 24 +- 4 files changed, 297 insertions(+), 11 deletions(-) create mode 100644 AISKU/Tests/Unit/src/CdnThrottle.tests.ts diff --git a/AISKU/Tests/Unit/src/CdnThrottle.tests.ts b/AISKU/Tests/Unit/src/CdnThrottle.tests.ts new file mode 100644 index 000000000..b142cf158 --- /dev/null +++ b/AISKU/Tests/Unit/src/CdnThrottle.tests.ts @@ -0,0 +1,278 @@ +import { ApplicationInsights, ApplicationInsightsContainer, IApplicationInsights, IConfig, IConfiguration, LoggingSeverity, Snippet, _eInternalMessageId } from '../../../src/applicationinsights-web' +import { AITestClass, Assert, IFetchArgs, PollingAssert} from '@microsoft/ai-test-framework'; +import { IThrottleInterval, IThrottleLimit, IThrottleMgrConfig } from '@microsoft/applicationinsights-common'; +import { SinonSpy } from 'sinon'; +import { AppInsightsSku } from '../../../src/AISku'; +import { createSnippetV5 } from './testSnippetV5'; +import { FeatureOptInMode, getGlobal, getGlobalInst, isFunction, newId } from '@microsoft/applicationinsights-core-js'; +import { createSnippetV6 } from './testSnippetV6'; +import { CfgSyncPlugin, ICfgSyncConfig, ICfgSyncMode } from '@microsoft/applicationinsights-cfgsync-js'; +import { createSyncPromise } from '@nevware21/ts-async'; + +const TestInstrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11'; + +const tconfig = { + disabled: false, + limit: { + samplingRate: 1, + maxSendNumber:100 + } as IThrottleLimit, + interval: { + monthInterval: 1, + daysOfMonth: [1], // must add here + dayInterval: undefined + } as IThrottleInterval +} as IThrottleMgrConfig; + +const default_throttle_config = { + disabled: true, + limit: { + samplingRate: 100, + maxSendNumber: 1 + }, + interval: { + monthInterval: 3, + daysOfMonth: [28] + } +} as IThrottleMgrConfig; + +const config = { + instrumentationKey:"testIkey", + enableAjaxPerfTracking: true, + throttleMgrCfg: { + 109: { + disabled: false, + limit: { + samplingRate: 1000000, + maxSendNumber: 1 + }, + interval: { + monthInterval: 1, + daysOfMonth:[1] + } + }, + 106: { + disabled: false, + limit: { + samplingRate: 1000000, + maxSendNumber: 1 + }, + interval: { + monthInterval: 1, + daysOfMonth:[1] + } + } + } +} as IConfiguration & IConfig; + + + +export class CdnThrottle extends AITestClass { + private _ai: AppInsightsSku; + private getAi: ApplicationInsights; + private _config: IConfiguration | IConfig; + private identifier: string; + private fetchStub: any; + init: ApplicationInsights; + private res: any; + private _fetch: any; + + constructor() { + super("CdnThrottle"); + } + + public _getTestConfig() { + let config: IConfiguration | IConfig = { + instrumentationKey: TestInstrumentationKey, + featureOptIn : {["iKeyUsage"]: {mode: FeatureOptInMode.enable}}, + extensionConfig : {["AppInsightsCfgSyncPlugin"] : { + syncMode: ICfgSyncMode.Receive, + cfgUrl: "testurl" + }} + }; + return config; + } + + public testInitialize() { + try { + this.identifier = "AppInsightsCfgSyncPlugin"; + this._config = this._getTestConfig(); + this._fetch = getGlobalInst("fetch"); + let doc = getGlobal(); + let cdnCfg = { + enabled: true, + config: config + } as ICfgSyncConfig; + doc["res"] = new (doc as any).Response(JSON.stringify(cdnCfg), { + status: 200, + headers: { "Content-type": "application/json" } + }); + } catch (e) { + console.error('Failed to initialize', e.message); + } + } + + public testFinishedCleanup(): void { + if (this._ai) { + this._ai.unload(false); + } + this.fetchStub = null; + getGlobal().fetch = this._fetch; + } + + public registerTests() { + this.testCaseAsync({ + name: "CfgSyncPlugin: customer enable ikey messsage change, new config fetch from config url overwrite throttle setting and send message", + stepDelay: 10, + useFakeTimers: true, + steps: [ () => { + let doc = getGlobal(); + hookFetch((resolve) => { // global instance cannot access test private instance + AITestClass.orgSetTimeout(function() { + resolve( doc["res"]); + }, 0); + }); + this.fetchStub = this.sandbox.spy((doc as any), "fetch"); + this.init = new ApplicationInsights({ + config: { + instrumentationKey: TestInstrumentationKey, + extensionConfig : {["AppInsightsCfgSyncPlugin"] : { + syncMode: ICfgSyncMode.Receive, + cfgUrl: "testurl" + }} + } + }); + this.init.loadAppInsights(); + this._ai = this.init; + }].concat(PollingAssert.createPollingAssert(() => { + + if (this.fetchStub.called){ + let core = this._ai['core']; + let _logger = core.logger; + let loggingSpy = this.sandbox.stub(_logger, 'throwInternal'); + Assert.ok(!loggingSpy.called); + + // now enable feature + this.init.config.featureOptIn = {["iKeyUsage"]: {mode: FeatureOptInMode.enable}}; + this.clock.tick(1); + Assert.ok(loggingSpy.called); + Assert.equal(_eInternalMessageId.InstrumentationKeyDeprecation, loggingSpy.args[0][1]); + let message= loggingSpy.args[0][2]; + Assert.ok(message.includes("Instrumentation key")); + return true; + } + return false; + + }, "response received", 60, 1000) as any) + }); + this.testCaseAsync({ + name: "CfgSyncPlugin: customer didn't set throttle config, successfully fetch from config url", + stepDelay: 10, + useFakeTimers: true, + steps: [ () => { + let doc = getGlobal(); + hookFetch((resolve) => { // global instance cannot access test private instance + AITestClass.orgSetTimeout(function() { + resolve( doc["res"]); + }, 0); + }); + + this.fetchStub = this.sandbox.spy((doc as any), "fetch"); + this.init = new ApplicationInsights({ + config: this._config, + }); + this.init.loadAppInsights(); + this._ai = this.init; + }].concat(PollingAssert.createPollingAssert(() => { + + if (this.fetchStub.called){ + let plugin = this._ai.appInsights['core'].getPlugin(this.identifier).plugin; + let newCfg = plugin.getCfg(); + Assert.equal(JSON.stringify(newCfg.throttleMgrCfg), JSON.stringify(config.throttleMgrCfg)); + // cdn should not be changed + let cdnCfg = this.init.config.throttleMgrCfg[_eInternalMessageId.CdnDeprecation]; + Assert.equal(JSON.stringify(cdnCfg), JSON.stringify(default_throttle_config)); + return true; + } + return false; + + }, "response received", 60, 1000) as any) + }); + + this.testCaseAsync({ + name: "CfgSyncPlugin: customer set throttle config, new config fetch from config url could overwrite original one", + stepDelay: 10, + useFakeTimers: true, + steps: [ () => { + let doc = getGlobal(); + hookFetch((resolve) => { // global instance cannot access test private instance + AITestClass.orgSetTimeout(function() { + resolve( doc["res"]); + }, 0); + }); + + this.fetchStub = this.sandbox.spy((doc as any), "fetch"); + let config = { + instrumentationKey: TestInstrumentationKey, + featureOptIn : {["iKeyUsage"]: {mode: FeatureOptInMode.enable}}, + throttleMgrCfg: { + 109: { + disabled: false, + limit: { + samplingRate: 50, + maxSendNumber: 1 + }, + interval: { + daysOfMonth:[1], + monthInterval: 1 + } + } + }, + extensionConfig : {["AppInsightsCfgSyncPlugin"] : { + syncMode: ICfgSyncMode.Receive, + cfgUrl: "testurl" + }} + }; + + this.init = new ApplicationInsights({ + config: config, + }); + this.init.loadAppInsights(); + this._ai = this.init; + }].concat(PollingAssert.createPollingAssert(() => { + + if (this.fetchStub.called){ + let plugin = this._ai.appInsights['core'].getPlugin(this.identifier).plugin; + let newCfg = plugin.getCfg(); + Assert.equal(JSON.stringify(newCfg.throttleMgrCfg), JSON.stringify(config.throttleMgrCfg)); + // cdn should not be overwritten + let cdnCfg = this.init.config.throttleMgrCfg[_eInternalMessageId.CdnDeprecation]; + Assert.equal(JSON.stringify(cdnCfg), JSON.stringify(default_throttle_config)); + let ikeyCfg = this.init.config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation]; + Assert.equal(JSON.stringify(ikeyCfg), JSON.stringify(config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation])); + return true; + } + return false; + }, "response received", 60, 1000) as any) + }); + + + + + + } +} + +function hookFetch(executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void): IFetchArgs[] { + let calls:IFetchArgs[] = []; + let global = getGlobal() as any; + global.fetch = function(input: RequestInfo, init?: RequestInit) { + calls.push({ + input, + init + }); + return createSyncPromise(executor); + } + + return calls; +} \ No newline at end of file diff --git a/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts b/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts index 1cede5c1b..c1707b9f3 100644 --- a/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts +++ b/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts @@ -83,7 +83,7 @@ export class ThrottleSentMessage extends AITestClass { public cdnDeprecatedMessageTests(): void { this.testCase({ - name: "CdnDeprecatedMessageTests: Message is sent when az416426 is used", + name: "ThrottleSentMessage: Message is sent when az416426 is used", useFakeTimers: true, test: () => { Assert.ok(this._ai, 'ApplicationInsights SDK exists'); diff --git a/AISKU/Tests/Unit/src/aiskuunittests.ts b/AISKU/Tests/Unit/src/aiskuunittests.ts index 466241b20..2f6e71d7e 100644 --- a/AISKU/Tests/Unit/src/aiskuunittests.ts +++ b/AISKU/Tests/Unit/src/aiskuunittests.ts @@ -6,6 +6,8 @@ import { SanitizerE2ETests } from './sanitizer.e2e.tests'; import { ValidateE2ETests } from './validate.e2e.tests'; import { SenderE2ETests } from './sender.e2e.tests'; import { SnippetInitializationTests } from './SnippetInitialization.Tests'; +import { CdnThrottle} from "./CdnThrottle.tests"; +import { ThrottleSentMessage } from "./ThrottleSentMessage.tests"; export function runTests() { new AISKUSizeCheck().registerTests(); @@ -17,4 +19,6 @@ export function runTests() { new SenderE2ETests().registerTests(); new SnippetInitializationTests(false).registerTests(); new SnippetInitializationTests(true).registerTests(); + // new ThrottleSentMessage().registerTests(); + new CdnThrottle().registerTests(); } \ No newline at end of file diff --git a/AISKU/src/AISku.ts b/AISKU/src/AISku.ts index 02319ee98..0b3f6363b 100644 --- a/AISKU/src/AISku.ts +++ b/AISKU/src/AISku.ts @@ -9,8 +9,8 @@ import { Sender } from "@microsoft/applicationinsights-channel-js"; import { AnalyticsPluginIdentifier, DEFAULT_BREEZE_PATH, IAutoExceptionTelemetry, IConfig, IDependencyTelemetry, IEventTelemetry, IExceptionTelemetry, IMetricTelemetry, IPageViewPerformanceTelemetry, IPageViewTelemetry, IRequestHeaders, - ITelemetryContext as Common_ITelemetryContext, IThrottleMgrConfig, ITraceTelemetry, PropertiesPluginIdentifier, ThrottleMgr, - parseConnectionString + ITelemetryContext as Common_ITelemetryContext, IThrottleInterval, IThrottleLimit, IThrottleMgrConfig, ITraceTelemetry, + PropertiesPluginIdentifier, ThrottleMgr, parseConnectionString } from "@microsoft/applicationinsights-common"; import { AppInsightsCore, FeatureOptInMode, IAppInsightsCore, IChannelControls, IConfigDefaults, IConfiguration, ICookieMgr, ICustomProperties, @@ -53,16 +53,20 @@ const SDK_LOADER_VER = "SdkLoaderVer"; const UNDEFINED_VALUE: undefined = undefined; +const default_limit = { + samplingRate: 100, + maxSendNumber: 1 +} as IThrottleLimit; + +const default_interval = { + monthInterval: 3, + daysOfMonth: [28] +} as IThrottleInterval; + const default_throttle_config = { disabled: true, - limit: { - samplingRate: 100, - maxSendNumber: 1 - }, - interval: { - monthInterval: 3, - daysOfMonth: [28] - } + limit: cfgDfMerge(default_limit), + interval: cfgDfMerge(default_interval) } as IThrottleMgrConfig; // We need to include all properties that we only reference that we want to be dynamically updatable here From 55e743e9dc45422c59c41c0716ed05d4de5a6dd7 Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Mon, 2 Oct 2023 17:25:19 -0700 Subject: [PATCH 4/5] clear local storage --- AISKU/Tests/Unit/src/CdnThrottle.tests.ts | 43 +++++++++++++++++-- .../Unit/src/ThrottleSentMessage.tests.ts | 6 +++ AISKU/Tests/Unit/src/aiskuunittests.ts | 2 +- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/AISKU/Tests/Unit/src/CdnThrottle.tests.ts b/AISKU/Tests/Unit/src/CdnThrottle.tests.ts index b142cf158..5fde19b44 100644 --- a/AISKU/Tests/Unit/src/CdnThrottle.tests.ts +++ b/AISKU/Tests/Unit/src/CdnThrottle.tests.ts @@ -65,6 +65,35 @@ const config = { } } as IConfiguration & IConfig; +const cdnOptinConfig = { + instrumentationKey:"testIkey", + enableAjaxPerfTracking: true, + throttleMgrCfg: { + 109: { + disabled: true, + limit: { + samplingRate: 1000000, + maxSendNumber: 1 + }, + interval: { + monthInterval: 1, + daysOfMonth:[1] + } + }, + 106: { + disabled: true, + limit: { + samplingRate: 1000000, + maxSendNumber: 1 + }, + interval: { + monthInterval: 1, + daysOfMonth:[1] + } + } + } +} as IConfiguration & IConfig; + export class CdnThrottle extends AITestClass { @@ -95,6 +124,9 @@ export class CdnThrottle extends AITestClass { public testInitialize() { try { + if (window.localStorage){ + window.localStorage.clear(); + } this.identifier = "AppInsightsCfgSyncPlugin"; this._config = this._getTestConfig(); this._fetch = getGlobalInst("fetch"); @@ -103,6 +135,10 @@ export class CdnThrottle extends AITestClass { enabled: true, config: config } as ICfgSyncConfig; + let cdnFeatureOptInCfg = { + enabled: true, + config: cdnOptinConfig + } as ICfgSyncConfig; doc["res"] = new (doc as any).Response(JSON.stringify(cdnCfg), { status: 200, headers: { "Content-type": "application/json" } @@ -118,6 +154,9 @@ export class CdnThrottle extends AITestClass { } this.fetchStub = null; getGlobal().fetch = this._fetch; + if (window.localStorage){ + window.localStorage.clear(); + } } public registerTests() { @@ -255,11 +294,7 @@ export class CdnThrottle extends AITestClass { return false; }, "response received", 60, 1000) as any) }); - - - - } } diff --git a/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts b/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts index c1707b9f3..317c6dda0 100644 --- a/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts +++ b/AISKU/Tests/Unit/src/ThrottleSentMessage.tests.ts @@ -51,6 +51,9 @@ export class ThrottleSentMessage extends AITestClass { public testInitialize() { try { + if (window.localStorage){ + window.localStorage.clear(); + } this.useFakeServer = false; this._config = this._getTestConfig(); @@ -73,6 +76,9 @@ export class ThrottleSentMessage extends AITestClass { // force unload this._ai.unload(false); } + if (window.localStorage){ + window.localStorage.clear(); + } } public registerTests() { diff --git a/AISKU/Tests/Unit/src/aiskuunittests.ts b/AISKU/Tests/Unit/src/aiskuunittests.ts index 2f6e71d7e..d2056151e 100644 --- a/AISKU/Tests/Unit/src/aiskuunittests.ts +++ b/AISKU/Tests/Unit/src/aiskuunittests.ts @@ -19,6 +19,6 @@ export function runTests() { new SenderE2ETests().registerTests(); new SnippetInitializationTests(false).registerTests(); new SnippetInitializationTests(true).registerTests(); - // new ThrottleSentMessage().registerTests(); + new ThrottleSentMessage().registerTests(); new CdnThrottle().registerTests(); } \ No newline at end of file From 04410ae71e7c09d6bc7f296fba4ffc70b888d92e Mon Sep 17 00:00:00 2001 From: siyuniu-ms Date: Tue, 3 Oct 2023 13:18:09 -0700 Subject: [PATCH 5/5] Update CdnThrottle.tests.ts --- AISKU/Tests/Unit/src/CdnThrottle.tests.ts | 218 +++++++++++++++------- 1 file changed, 149 insertions(+), 69 deletions(-) diff --git a/AISKU/Tests/Unit/src/CdnThrottle.tests.ts b/AISKU/Tests/Unit/src/CdnThrottle.tests.ts index 5fde19b44..600a08b36 100644 --- a/AISKU/Tests/Unit/src/CdnThrottle.tests.ts +++ b/AISKU/Tests/Unit/src/CdnThrottle.tests.ts @@ -4,26 +4,14 @@ import { IThrottleInterval, IThrottleLimit, IThrottleMgrConfig } from '@microsof import { SinonSpy } from 'sinon'; import { AppInsightsSku } from '../../../src/AISku'; import { createSnippetV5 } from './testSnippetV5'; -import { FeatureOptInMode, getGlobal, getGlobalInst, isFunction, newId } from '@microsoft/applicationinsights-core-js'; +import { CdnFeatureMode, FeatureOptInMode, getGlobal, getGlobalInst, isFunction, newId } from '@microsoft/applicationinsights-core-js'; import { createSnippetV6 } from './testSnippetV6'; import { CfgSyncPlugin, ICfgSyncConfig, ICfgSyncMode } from '@microsoft/applicationinsights-cfgsync-js'; import { createSyncPromise } from '@nevware21/ts-async'; +import { ICfgSyncCdnConfig } from '@microsoft/applicationinsights-cfgsync-js/src/Interfaces/ICfgSyncCdnConfig'; const TestInstrumentationKey = 'b7170927-2d1c-44f1-acec-59f4e1751c11'; -const tconfig = { - disabled: false, - limit: { - samplingRate: 1, - maxSendNumber:100 - } as IThrottleLimit, - interval: { - monthInterval: 1, - daysOfMonth: [1], // must add here - dayInterval: undefined - } as IThrottleInterval -} as IThrottleMgrConfig; - const default_throttle_config = { disabled: true, limit: { @@ -36,64 +24,61 @@ const default_throttle_config = { } } as IThrottleMgrConfig; -const config = { - instrumentationKey:"testIkey", - enableAjaxPerfTracking: true, - throttleMgrCfg: { - 109: { - disabled: false, - limit: { - samplingRate: 1000000, - maxSendNumber: 1 - }, - interval: { - monthInterval: 1, - daysOfMonth:[1] - } +const throttleCfg = { + 109: { + disabled: false, + limit: { + samplingRate: 1000000, + maxSendNumber: 2 }, - 106: { - disabled: false, - limit: { - samplingRate: 1000000, - maxSendNumber: 1 - }, - interval: { - monthInterval: 1, - daysOfMonth:[1] - } + interval: { + monthInterval: 2, + daysOfMonth:[1] + } + }, + 106: { + disabled: false, + limit: { + samplingRate: 1000000, + maxSendNumber: 2 + }, + interval: { + monthInterval: 2, + daysOfMonth:[1] } } -} as IConfiguration & IConfig; +} -const cdnOptinConfig = { - instrumentationKey:"testIkey", - enableAjaxPerfTracking: true, - throttleMgrCfg: { - 109: { - disabled: true, - limit: { - samplingRate: 1000000, - maxSendNumber: 1 - }, - interval: { - monthInterval: 1, - daysOfMonth:[1] - } +const throttleCfgDisable = { + 109: { + disabled: true, + limit: { + samplingRate: 1000000, + maxSendNumber: 4 }, - 106: { - disabled: true, - limit: { - samplingRate: 1000000, - maxSendNumber: 1 - }, - interval: { - monthInterval: 1, - daysOfMonth:[1] - } + interval: { + monthInterval: 4, + daysOfMonth:[1] + } + }, + 106: { + disabled: true, + limit: { + samplingRate: 1000000, + maxSendNumber: 4 + }, + interval: { + monthInterval: 4, + daysOfMonth:[1] } } -} as IConfiguration & IConfig; +} +const sampleConfig = { + instrumentationKey:"testIkey", + enableAjaxPerfTracking: true, + throttleMgrCfg: throttleCfg +} as IConfiguration & IConfig; export class CdnThrottle extends AITestClass { @@ -105,6 +90,7 @@ export class CdnThrottle extends AITestClass { init: ApplicationInsights; private res: any; private _fetch: any; + loggingSpy: any; constructor() { super("CdnThrottle"); @@ -133,16 +119,36 @@ export class CdnThrottle extends AITestClass { let doc = getGlobal(); let cdnCfg = { enabled: true, - config: config + config: sampleConfig } as ICfgSyncConfig; let cdnFeatureOptInCfg = { enabled: true, - config: cdnOptinConfig + featureOptIn:{ + ["enableWParamFeature"]: {mode: CdnFeatureMode.enable, onCfg: {["maxMessageLimit"]: 11}, offCfg: {["maxMessageLimit"]: 12}}, + ["iKeyUsage"]: { + mode: CdnFeatureMode.enable, + onCfg: { + "throttleMgrCfg.106.disabled":false, + "throttleMgrCfg.109.disabled":false, + }, + offCfg: { + "throttleMgrCfg.106.disabled":true, + "throttleMgrCfg.109.disabled":true, + } + }}, + config: { + maxMessageLimit: 10, + throttleMgrCfg: throttleCfgDisable + } } as ICfgSyncConfig; doc["res"] = new (doc as any).Response(JSON.stringify(cdnCfg), { status: 200, headers: { "Content-type": "application/json" } }); + doc["res2"] = new (doc as any).Response(JSON.stringify(cdnFeatureOptInCfg), { + status: 200, + headers: { "Content-type": "application/json" } + }); } catch (e) { console.error('Failed to initialize', e.message); } @@ -227,7 +233,7 @@ export class CdnThrottle extends AITestClass { if (this.fetchStub.called){ let plugin = this._ai.appInsights['core'].getPlugin(this.identifier).plugin; let newCfg = plugin.getCfg(); - Assert.equal(JSON.stringify(newCfg.throttleMgrCfg), JSON.stringify(config.throttleMgrCfg)); + Assert.equal(JSON.stringify(newCfg.throttleMgrCfg), JSON.stringify(sampleConfig.throttleMgrCfg)); // cdn should not be changed let cdnCfg = this.init.config.throttleMgrCfg[_eInternalMessageId.CdnDeprecation]; Assert.equal(JSON.stringify(cdnCfg), JSON.stringify(default_throttle_config)); @@ -283,17 +289,91 @@ export class CdnThrottle extends AITestClass { if (this.fetchStub.called){ let plugin = this._ai.appInsights['core'].getPlugin(this.identifier).plugin; let newCfg = plugin.getCfg(); - Assert.equal(JSON.stringify(newCfg.throttleMgrCfg), JSON.stringify(config.throttleMgrCfg)); + Assert.equal(JSON.stringify(newCfg.throttleMgrCfg), JSON.stringify(sampleConfig.throttleMgrCfg)); // cdn should not be overwritten let cdnCfg = this.init.config.throttleMgrCfg[_eInternalMessageId.CdnDeprecation]; Assert.equal(JSON.stringify(cdnCfg), JSON.stringify(default_throttle_config)); let ikeyCfg = this.init.config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation]; - Assert.equal(JSON.stringify(ikeyCfg), JSON.stringify(config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation])); + Assert.equal(JSON.stringify(ikeyCfg), JSON.stringify(sampleConfig.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation])); return true; } return false; }, "response received", 60, 1000) as any) }); + + this.testCaseAsync({ + name: "CfgSyncPlugin: customer enable feature opt in, then the config in cdn feature opt in is applied", + stepDelay: 10, + useFakeTimers: true, + steps: [ () => { + let doc = getGlobal(); + hookFetch((resolve) => { // global instance cannot access test private instance + AITestClass.orgSetTimeout(function() { + resolve( doc["res2"]); + }, 0); + }); + this.fetchStub = this.sandbox.spy((doc as any), "fetch"); + this.init = new ApplicationInsights({ + config: { + instrumentationKey: TestInstrumentationKey, + extensionConfig : {["AppInsightsCfgSyncPlugin"] : { + syncMode: ICfgSyncMode.Receive, + cfgUrl: "testurl" + }}, + featureOptIn : {["iKeyUsage"]: {mode: FeatureOptInMode.enable}, + ["enableWParamFeature"]: {mode: FeatureOptInMode.enable}} + } + }); + this.init.loadAppInsights(); + this._ai = this.init; + }].concat(PollingAssert.createPollingAssert(() => { + + if (this.fetchStub.called){ + Assert.equal(this.init.config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation].disabled, false); + Assert.equal(this.init.config.throttleMgrCfg[_eInternalMessageId.CdnDeprecation].disabled, true); + Assert.equal(this.init.config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation].limit?.maxSendNumber, throttleCfgDisable[_eInternalMessageId.InstrumentationKeyDeprecation].limit?.maxSendNumber); + return true; + } + return false; + }, "response received", 60, 1000) as any) + }); + + this.testCaseAsync({ + name: "CfgSyncPlugin: customer disable feature opt in, the origin config on cdn will apply", + stepDelay: 10, + useFakeTimers: true, + steps: [ () => { + let doc = getGlobal(); + hookFetch((resolve) => { // global instance cannot access test private instance + AITestClass.orgSetTimeout(function() { + resolve( doc["res2"]); + }, 0); + }); + this.fetchStub = this.sandbox.spy((doc as any), "fetch"); + this.init = new ApplicationInsights({ + config: { + instrumentationKey: TestInstrumentationKey, + extensionConfig : {["AppInsightsCfgSyncPlugin"] : { + syncMode: ICfgSyncMode.Receive, + cfgUrl: "testurl" + }}, + featureOptIn : { + ["enableWParamFeature"]: {mode: FeatureOptInMode.enable}} + } + }); + this.init.loadAppInsights(); + this._ai = this.init; + }].concat(PollingAssert.createPollingAssert(() => { + if (this.fetchStub.called){ + Assert.equal(this.init.config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation].disabled, true); + Assert.equal(this.init.config.throttleMgrCfg[_eInternalMessageId.CdnDeprecation].disabled, true); + Assert.equal(this.init.config.throttleMgrCfg[_eInternalMessageId.InstrumentationKeyDeprecation].limit?.maxSendNumber, throttleCfgDisable[_eInternalMessageId.InstrumentationKeyDeprecation].limit?.maxSendNumber); + return true; + } + return false; + }, "response received", 60, 1000) as any) + }); + } }