From 9979451c3909d7c0bc4c0d9710cb1d90f422721e Mon Sep 17 00:00:00 2001 From: zhiyuanliang Date: Fri, 10 Jan 2025 23:50:05 +0800 Subject: [PATCH 1/2] add basic telemetry test --- Samples/BasicTelemetry.sample.json | 4 +- .../featureEvaluationValidation.test.ts | 4 +- libraryValidations/JavaScript/package.json | 2 +- .../JavaScript/telemetryValidation.test.ts | 69 +++++++++++++++++++ .../telemetryWithProviderValidation.test.ts | 12 ++-- libraryValidations/JavaScript/utils.ts | 23 ++++--- 6 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 libraryValidations/JavaScript/telemetryValidation.test.ts diff --git a/Samples/BasicTelemetry.sample.json b/Samples/BasicTelemetry.sample.json index 85e4f88..b46b5a9 100644 --- a/Samples/BasicTelemetry.sample.json +++ b/Samples/BasicTelemetry.sample.json @@ -4,7 +4,7 @@ { "id": "TelemetryVariant", "description": "", - "enabled": "true", + "enabled": true, "conditions": { "client_filters": [] }, @@ -19,7 +19,7 @@ "default_when_enabled": "True_Override" }, "telemetry": { - "enabled": "true", + "enabled": true, "metadata": { "ETag": "cmwBRcIAq1jUyKL3Kj8bvf9jtxBrFg-R-ayExStMC90", "FeatureFlagReference": "https://fake-config-store/kv/.appconfig.featureflag/TelemetryVariant", diff --git a/libraryValidations/JavaScript/featureEvaluationValidation.test.ts b/libraryValidations/JavaScript/featureEvaluationValidation.test.ts index f9ac7b5..7db2867 100644 --- a/libraryValidations/JavaScript/featureEvaluationValidation.test.ts +++ b/libraryValidations/JavaScript/featureEvaluationValidation.test.ts @@ -3,7 +3,7 @@ import * as fs from "node:fs/promises"; import { FeatureManager, ConfigurationObjectFeatureFlagProvider } from "@microsoft/feature-management"; -import {FILE_PATH, SAMPLE_JSON_KEY, TESTS_JSON_KEY, validateFeatureEvaluation, FeatureFlagTest } from "./utils.js"; +import { FILE_PATH, SAMPLE_JSON_KEY, TESTS_JSON_KEY, validateFeatureEvaluation, FeatureFlagTest } from "./utils.js"; async function runTest(testName: string) { const config = JSON.parse(await fs.readFile(FILE_PATH + testName + SAMPLE_JSON_KEY, "utf8")); @@ -11,7 +11,7 @@ async function runTest(testName: string) { const ffProvider = new ConfigurationObjectFeatureFlagProvider(config); const fm = new FeatureManager(ffProvider); - for (const testcase of testcases){ + for (const testcase of testcases) { validateFeatureEvaluation(testcase, fm); } } diff --git a/libraryValidations/JavaScript/package.json b/libraryValidations/JavaScript/package.json index 8824aab..c03af95 100644 --- a/libraryValidations/JavaScript/package.json +++ b/libraryValidations/JavaScript/package.json @@ -20,7 +20,7 @@ "@microsoft/feature-management": "2.0.0-preview.3", "@microsoft/feature-management-applicationinsights-browser": "2.0.0-preview.3", "@microsoft/feature-management-applicationinsights-node": "2.0.0-preview.3", - "@azure/app-configuration-provider": "2.0.0-preview.1", + "@azure/app-configuration-provider": "2.0.0-preview.2", "@microsoft/applicationinsights-web": "^3.3.4", "applicationinsights": "^2.9.6" } diff --git a/libraryValidations/JavaScript/telemetryValidation.test.ts b/libraryValidations/JavaScript/telemetryValidation.test.ts new file mode 100644 index 0000000..c5c1b5c --- /dev/null +++ b/libraryValidations/JavaScript/telemetryValidation.test.ts @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as sinon from "sinon"; +import * as fs from "node:fs/promises"; +import { FeatureManager, ConfigurationObjectFeatureFlagProvider } from "@microsoft/feature-management"; +import { createTelemetryPublisher as createNodeTelemetryPublisher } from "@microsoft/feature-management-applicationinsights-node"; +import { createTelemetryPublisher as createBrowserTelemetryPublisher } from "@microsoft/feature-management-applicationinsights-browser"; +import { FILE_PATH, SAMPLE_JSON_KEY, TESTS_JSON_KEY, validateFeatureEvaluation, validateTelemetry, FeatureFlagTest } from "./utils.js"; +import { ApplicationInsights } from "@microsoft/applicationinsights-web"; +import applicationInsights from "applicationinsights"; + +// For telemetry validation +let eventNameToValidate; +let eventPropertiesToValidate; + +applicationInsights.setup("DUMMY-CONNECTION-STRING").start(); +sinon.stub(applicationInsights.defaultClient, "trackEvent").callsFake((event) => { + eventNameToValidate = event.name; + eventPropertiesToValidate = event.properties; +}); + +const appInsights = new ApplicationInsights({ config: { connectionString: "DUMMY-CONNECTION-STRING" }}); +sinon.stub(appInsights, "trackEvent").callsFake((event, customProperties) => { + eventNameToValidate = event.name; + eventPropertiesToValidate = customProperties; +}); + +async function runTestWithNodePackage(testName: string) { + const config = JSON.parse(await fs.readFile(FILE_PATH + testName + SAMPLE_JSON_KEY, "utf8")); + const testcases: FeatureFlagTest[] = JSON.parse(await fs.readFile(FILE_PATH + testName + TESTS_JSON_KEY, "utf8")); + const ffProvider = new ConfigurationObjectFeatureFlagProvider(config); + const fm = new FeatureManager(ffProvider, { onFeatureEvaluated: createNodeTelemetryPublisher(applicationInsights.defaultClient) }); + + for (const testcase of testcases) { + const featureFlagName = testcase.FeatureFlagName; + const context = { userId: testcase.Inputs?.User, groups: testcase.Inputs?.Groups }; + await fm.getVariant(featureFlagName, context); + validateTelemetry(testcase, undefined, eventNameToValidate, eventPropertiesToValidate); + validateFeatureEvaluation(testcase, fm); + } +} + +async function runTestWithBrowserPackage(testName: string) { + const config = JSON.parse(await fs.readFile(FILE_PATH + testName + SAMPLE_JSON_KEY, "utf8")); + const testcases: FeatureFlagTest[] = JSON.parse(await fs.readFile(FILE_PATH + testName + TESTS_JSON_KEY, "utf8")); + const ffProvider = new ConfigurationObjectFeatureFlagProvider(config); + const fm = new FeatureManager(ffProvider, { onFeatureEvaluated: createBrowserTelemetryPublisher(appInsights) }); + + for (const testcase of testcases) { + const featureFlagName = testcase.FeatureFlagName; + const context = { userId: testcase.Inputs?.User, groups: testcase.Inputs?.Groups }; + await fm.getVariant(featureFlagName, context); + validateTelemetry(testcase, undefined, eventNameToValidate, eventPropertiesToValidate); + validateFeatureEvaluation(testcase, fm); + } +} + +describe("telemetry with provider and node package", function () { + it("should pass BasicTelemetry test", async () => { + await runTestWithNodePackage("BasicTelemetry"); + }); +}); + +describe("telemetry with provider and browser package", function () { + it("should pass BasicTelemetry test", async () => { + await runTestWithBrowserPackage("BasicTelemetry"); + }); +}); diff --git a/libraryValidations/JavaScript/telemetryWithProviderValidation.test.ts b/libraryValidations/JavaScript/telemetryWithProviderValidation.test.ts index edd37af..a9177df 100644 --- a/libraryValidations/JavaScript/telemetryWithProviderValidation.test.ts +++ b/libraryValidations/JavaScript/telemetryWithProviderValidation.test.ts @@ -7,7 +7,7 @@ import { load } from "@azure/app-configuration-provider"; import { FeatureManager, ConfigurationMapFeatureFlagProvider } from "@microsoft/feature-management"; import { createTelemetryPublisher as createNodeTelemetryPublisher } from "@microsoft/feature-management-applicationinsights-node"; import { createTelemetryPublisher as createBrowserTelemetryPublisher } from "@microsoft/feature-management-applicationinsights-browser"; -import {FILE_PATH, TESTS_JSON_KEY, FeatureFlagTest, validateFeatureEvaluation, validateTelemetryWithProvider } from "./utils.js"; +import { FILE_PATH, TESTS_JSON_KEY, FeatureFlagTest, validateFeatureEvaluation, validateTelemetry } from "./utils.js"; import { ApplicationInsights } from "@microsoft/applicationinsights-web"; import applicationInsights from "applicationinsights"; @@ -48,11 +48,12 @@ async function runTestWithProviderAndNodePackage(testName: string) { const testcases: FeatureFlagTest[] = JSON.parse(await fs.readFile(FILE_PATH + testName + TESTS_JSON_KEY, "utf8")); const ffProvider = new ConfigurationMapFeatureFlagProvider(config); const fm = new FeatureManager(ffProvider, { onFeatureEvaluated: createNodeTelemetryPublisher(applicationInsights.defaultClient) }); - for (const testcase of testcases){ + for (const testcase of testcases) { const featureFlagName = testcase.FeatureFlagName; const context = { userId: testcase.Inputs?.User, groups: testcase.Inputs?.Groups }; await fm.getVariant(featureFlagName, context); - validateTelemetryWithProvider(testcase, connectionString, eventNameToValidate, eventPropertiesToValidate); + validateTelemetry(testcase, connectionString, eventNameToValidate, eventPropertiesToValidate); + validateFeatureEvaluation(testcase, fm); } } @@ -77,11 +78,12 @@ async function runTestWithProviderAndBrowserPackage(testName: string) { const testcases: FeatureFlagTest[] = JSON.parse(await fs.readFile(FILE_PATH + testName + TESTS_JSON_KEY, "utf8")); const ffProvider = new ConfigurationMapFeatureFlagProvider(config); const fm = new FeatureManager(ffProvider, { onFeatureEvaluated: createBrowserTelemetryPublisher(appInsights) }); - for (const testcase of testcases){ + for (const testcase of testcases) { const featureFlagName = testcase.FeatureFlagName; const context = { userId: testcase.Inputs?.User, groups: testcase.Inputs?.Groups }; await fm.getVariant(featureFlagName, context); - validateTelemetryWithProvider(testcase, connectionString, eventNameToValidate, eventPropertiesToValidate); + validateTelemetry(testcase, connectionString, eventNameToValidate, eventPropertiesToValidate); + validateFeatureEvaluation(testcase, fm); } } diff --git a/libraryValidations/JavaScript/utils.ts b/libraryValidations/JavaScript/utils.ts index 05633c8..43dccc6 100644 --- a/libraryValidations/JavaScript/utils.ts +++ b/libraryValidations/JavaScript/utils.ts @@ -108,10 +108,10 @@ export async function validateFeatureEvaluation(testcase: FeatureFlagTest, featu } } -export function validateTelemetryWithProvider(testcase: FeatureFlagTest, connectionString: string, eventNameToValidate: string, eventPropertiesToValidate: any) { - // if (testcase.Telemetry?.EventName) { - // expect(eventNameToValidate).to.eq(testcase.Telemetry.EventName); - // } +export function validateTelemetry(testcase: FeatureFlagTest, connectionString: string | undefined, eventNameToValidate: string, eventPropertiesToValidate: any) { + if (testcase.Telemetry?.EventName) { + expect(eventNameToValidate).to.eq(testcase.Telemetry.EventName); + } const eventProperties = testcase.Telemetry?.EventProperties; if (eventProperties) { @@ -143,16 +143,21 @@ export function validateTelemetryWithProvider(testcase: FeatureFlagTest, connect expect(eventPropertiesToValidate["FeatureFlagId"]).to.eq(eventProperties.FeatureFlagId); } if (eventProperties.FeatureFlagReference) { - const endpointMatch = connectionString.match(/Endpoint=([^;]+)/); - if (endpointMatch) { - expect(eventPropertiesToValidate["FeatureFlagReference"]).to.eq(endpointMatch[1] + eventProperties.FeatureFlagReference); + if (connectionString === undefined) { + expect(eventPropertiesToValidate["FeatureFlagReference"]).to.eq(eventProperties.FeatureFlagReference); } else { - expect.fail("Connection string does not contain endpoint."); + const endpointMatch = connectionString.match(/Endpoint=([^;]+)/); + if (endpointMatch) { + expect(eventPropertiesToValidate["FeatureFlagReference"]).to.eq(endpointMatch[1] + eventProperties.FeatureFlagReference); + } + else { + expect.fail("Connection string does not contain endpoint."); + } } } if (eventProperties.TargetingId) { expect(eventPropertiesToValidate["TargetingId"]).to.eq(eventProperties.TargetingId); } } -} \ No newline at end of file +} From 060366a30ace18ea9fced771fed219236d885022 Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Mon, 18 Aug 2025 11:29:44 +0800 Subject: [PATCH 2/2] update --- libraryValidations/JavaScript/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraryValidations/JavaScript/package.json b/libraryValidations/JavaScript/package.json index c03af95..e13bb1c 100644 --- a/libraryValidations/JavaScript/package.json +++ b/libraryValidations/JavaScript/package.json @@ -17,10 +17,10 @@ "sinon": "^15.2.0", "tslib": "^2.6.2", "typescript": "^5.3.3", - "@microsoft/feature-management": "2.0.0-preview.3", - "@microsoft/feature-management-applicationinsights-browser": "2.0.0-preview.3", - "@microsoft/feature-management-applicationinsights-node": "2.0.0-preview.3", - "@azure/app-configuration-provider": "2.0.0-preview.2", + "@microsoft/feature-management": "latest", + "@microsoft/feature-management-applicationinsights-browser": "latest", + "@microsoft/feature-management-applicationinsights-node": "latest", + "@azure/app-configuration-provider": "latest", "@microsoft/applicationinsights-web": "^3.3.4", "applicationinsights": "^2.9.6" }