From 01228f8d6400f036048b880f41c682059d66c6f5 Mon Sep 17 00:00:00 2001 From: Marko Mlakar Date: Mon, 24 Nov 2025 13:38:02 +0100 Subject: [PATCH 1/6] Merge branch 'main' into feat/add-unit-test-config-feature-1406 Signed-off-by: Marko Mlakar --- libs/providers/flagd/src/e2e/constants.ts | 1 + .../flagd/src/e2e/tests/config.spec.ts | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 libs/providers/flagd/src/e2e/tests/config.spec.ts diff --git a/libs/providers/flagd/src/e2e/constants.ts b/libs/providers/flagd/src/e2e/constants.ts index 13bfe14d7..a3319d3de 100644 --- a/libs/providers/flagd/src/e2e/constants.ts +++ b/libs/providers/flagd/src/e2e/constants.ts @@ -4,3 +4,4 @@ export const UNSTABLE_CLIENT_NAME = 'unstable'; export const UNAVAILABLE_CLIENT_NAME = 'unavailable'; export const GHERKIN_FLAGD = getGherkinTestPath('*.feature'); +export const CONFIG_FEATURE = getGherkinTestPath('config.feature'); diff --git a/libs/providers/flagd/src/e2e/tests/config.spec.ts b/libs/providers/flagd/src/e2e/tests/config.spec.ts new file mode 100644 index 000000000..bc333cedf --- /dev/null +++ b/libs/providers/flagd/src/e2e/tests/config.spec.ts @@ -0,0 +1,22 @@ +import { configSteps } from '../step-definitions/configSteps'; +import type { State } from '../step-definitions/state'; +import { autoBindSteps, loadFeatures } from 'jest-cucumber'; +import { CONFIG_FEATURE } from '../constants'; + +jest.setTimeout(50000); +describe('config', () => { + const state: State = { + resolverType: 'in-process', + options: {}, + config: undefined, + events: [], + }; + autoBindSteps( + loadFeatures(CONFIG_FEATURE, { + scenarioNameTemplate: (vars) => { + return `${vars.scenarioTitle} (${vars.scenarioTags.join(',')} ${vars.featureTags.join(',')})`; + }, + }), + [configSteps(state)], + ); +}); From dafd6baeaeced07f96fd1bc8471105bbe720e843 Mon Sep 17 00:00:00 2001 From: Marko Mlakar Date: Tue, 25 Nov 2025 12:59:49 +0100 Subject: [PATCH 2/6] Address test isolation and output formatting concerns * Reset env vars and state object in configSteps beforeEach for test isolation * Added better formatting for the test output Signed-off-by: Marko Mlakar --- libs/providers/flagd/src/e2e/step-definitions/configSteps.ts | 5 +++++ libs/providers/flagd/src/e2e/tests/config.spec.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts index 4405978b6..2b7ba1f3d 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts @@ -3,6 +3,8 @@ import type { State, Steps } from './state'; import { CacheOption, getConfig, ResolverType } from '../../lib/configuration'; import { mapValueToType } from './utils'; +const originalEnv = { ...process.env }; + export const configSteps: Steps = (state: State) => { function mapName(name: string): string { switch (name) { @@ -16,6 +18,9 @@ export const configSteps: Steps = (state: State) => { return ({ given, when, then }: StepsDefinitionCallbackOptions) => { beforeEach(() => { state.options = {}; + state.config = undefined; + state.events = []; + process.env = { ...originalEnv }; }); given(/^an option "(.*)" of type "(.*)" with value "(.*)"$/, (name: string, type: string, value: string) => { state.options[mapName(name)] = mapValueToType(value, type); diff --git a/libs/providers/flagd/src/e2e/tests/config.spec.ts b/libs/providers/flagd/src/e2e/tests/config.spec.ts index bc333cedf..255b99c2b 100644 --- a/libs/providers/flagd/src/e2e/tests/config.spec.ts +++ b/libs/providers/flagd/src/e2e/tests/config.spec.ts @@ -14,7 +14,8 @@ describe('config', () => { autoBindSteps( loadFeatures(CONFIG_FEATURE, { scenarioNameTemplate: (vars) => { - return `${vars.scenarioTitle} (${vars.scenarioTags.join(',')} ${vars.featureTags.join(',')})`; + const tags = [...vars.scenarioTags, ...vars.featureTags]; + return `${vars.scenarioTitle}${tags.length > 0 ? ` (${tags.join(', ')})` : ''}`; }, }), [configSteps(state)], From c9e41b720ba74fc8b5000d3c6f90e16f2bc56fb3 Mon Sep 17 00:00:00 2001 From: Marko Mlakar Date: Tue, 25 Nov 2025 13:36:57 +0100 Subject: [PATCH 3/6] Move env snapshot to function scope in configSteps Signed-off-by: Marko Mlakar --- .../flagd/src/e2e/step-definitions/configSteps.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts index 2b7ba1f3d..faa0791ae 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts @@ -3,8 +3,6 @@ import type { State, Steps } from './state'; import { CacheOption, getConfig, ResolverType } from '../../lib/configuration'; import { mapValueToType } from './utils'; -const originalEnv = { ...process.env }; - export const configSteps: Steps = (state: State) => { function mapName(name: string): string { switch (name) { @@ -16,12 +14,17 @@ export const configSteps: Steps = (state: State) => { } return ({ given, when, then }: StepsDefinitionCallbackOptions) => { + const originalEnv = process.env; beforeEach(() => { state.options = {}; state.config = undefined; state.events = []; process.env = { ...originalEnv }; }); + + afterEach(() => { + process.env = originalEnv; + }); given(/^an option "(.*)" of type "(.*)" with value "(.*)"$/, (name: string, type: string, value: string) => { state.options[mapName(name)] = mapValueToType(value, type); }); From 17f12dae5e6b55de287c938079b73b229ab7eec8 Mon Sep 17 00:00:00 2001 From: Marko Mlakar Date: Tue, 25 Nov 2025 14:08:17 +0100 Subject: [PATCH 4/6] Remove direct reassignment of the env variables and redundant test timeout for the unit test Signed-off-by: Marko Mlakar --- .../flagd/src/e2e/step-definitions/configSteps.ts | 8 +++----- libs/providers/flagd/src/e2e/tests/config.spec.ts | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts index faa0791ae..70ec173b1 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts @@ -14,17 +14,15 @@ export const configSteps: Steps = (state: State) => { } return ({ given, when, then }: StepsDefinitionCallbackOptions) => { - const originalEnv = process.env; beforeEach(() => { state.options = {}; state.config = undefined; state.events = []; - process.env = { ...originalEnv }; + const originalEnv = { ...process.env }; + Object.keys(process.env).forEach((key) => delete process.env[key]); + Object.assign(process.env, originalEnv); }); - afterEach(() => { - process.env = originalEnv; - }); given(/^an option "(.*)" of type "(.*)" with value "(.*)"$/, (name: string, type: string, value: string) => { state.options[mapName(name)] = mapValueToType(value, type); }); diff --git a/libs/providers/flagd/src/e2e/tests/config.spec.ts b/libs/providers/flagd/src/e2e/tests/config.spec.ts index 255b99c2b..62c3db039 100644 --- a/libs/providers/flagd/src/e2e/tests/config.spec.ts +++ b/libs/providers/flagd/src/e2e/tests/config.spec.ts @@ -3,7 +3,6 @@ import type { State } from '../step-definitions/state'; import { autoBindSteps, loadFeatures } from 'jest-cucumber'; import { CONFIG_FEATURE } from '../constants'; -jest.setTimeout(50000); describe('config', () => { const state: State = { resolverType: 'in-process', From cfc6e2f8b1cbff3fa12c0b4562168a153b778131 Mon Sep 17 00:00:00 2001 From: Marko Mlakar Date: Tue, 25 Nov 2025 14:29:03 +0100 Subject: [PATCH 5/6] Improve test isolation and scenario name formatting Signed-off-by: Marko Mlakar --- libs/providers/flagd/src/e2e/step-definitions/configSteps.ts | 3 ++- libs/providers/flagd/src/e2e/tests/config.spec.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts index 70ec173b1..7fd9b142b 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts @@ -13,12 +13,13 @@ export const configSteps: Steps = (state: State) => { } } + const originalEnv = { ...process.env }; + return ({ given, when, then }: StepsDefinitionCallbackOptions) => { beforeEach(() => { state.options = {}; state.config = undefined; state.events = []; - const originalEnv = { ...process.env }; Object.keys(process.env).forEach((key) => delete process.env[key]); Object.assign(process.env, originalEnv); }); diff --git a/libs/providers/flagd/src/e2e/tests/config.spec.ts b/libs/providers/flagd/src/e2e/tests/config.spec.ts index 62c3db039..6189a8de5 100644 --- a/libs/providers/flagd/src/e2e/tests/config.spec.ts +++ b/libs/providers/flagd/src/e2e/tests/config.spec.ts @@ -13,7 +13,7 @@ describe('config', () => { autoBindSteps( loadFeatures(CONFIG_FEATURE, { scenarioNameTemplate: (vars) => { - const tags = [...vars.scenarioTags, ...vars.featureTags]; + const tags = [...new Set([...vars.scenarioTags, ...vars.featureTags])]; return `${vars.scenarioTitle}${tags.length > 0 ? ` (${tags.join(', ')})` : ''}`; }, }), From dbba3014b854a5788056d0209aec8547dbe619c9 Mon Sep 17 00:00:00 2001 From: Marko Mlakar Date: Fri, 28 Nov 2025 11:29:41 +0100 Subject: [PATCH 6/6] Move the config feature unit test logic to configuration.spec * Removed the config.spec.ts from the e2e suite and moved the logic to configuration.spec.ts unit test * Restored env vars safely by deleting only the added keys Signed-off-by: Marko Mlakar --- .../src/e2e/step-definitions/configSteps.ts | 4 +++- .../flagd/src/e2e/tests/config.spec.ts | 22 ------------------ .../flagd/src/lib/configuration.spec.ts | 23 +++++++++++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) delete mode 100644 libs/providers/flagd/src/e2e/tests/config.spec.ts diff --git a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts index 7fd9b142b..0e52ebb9f 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts @@ -20,7 +20,9 @@ export const configSteps: Steps = (state: State) => { state.options = {}; state.config = undefined; state.events = []; - Object.keys(process.env).forEach((key) => delete process.env[key]); + Object.keys(process.env) + .filter((key) => !Object.prototype.hasOwnProperty.call(originalEnv, key)) + .forEach((key) => delete process.env[key]); Object.assign(process.env, originalEnv); }); diff --git a/libs/providers/flagd/src/e2e/tests/config.spec.ts b/libs/providers/flagd/src/e2e/tests/config.spec.ts deleted file mode 100644 index 6189a8de5..000000000 --- a/libs/providers/flagd/src/e2e/tests/config.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { configSteps } from '../step-definitions/configSteps'; -import type { State } from '../step-definitions/state'; -import { autoBindSteps, loadFeatures } from 'jest-cucumber'; -import { CONFIG_FEATURE } from '../constants'; - -describe('config', () => { - const state: State = { - resolverType: 'in-process', - options: {}, - config: undefined, - events: [], - }; - autoBindSteps( - loadFeatures(CONFIG_FEATURE, { - scenarioNameTemplate: (vars) => { - const tags = [...new Set([...vars.scenarioTags, ...vars.featureTags])]; - return `${vars.scenarioTitle}${tags.length > 0 ? ` (${tags.join(', ')})` : ''}`; - }, - }), - [configSteps(state)], - ); -}); diff --git a/libs/providers/flagd/src/lib/configuration.spec.ts b/libs/providers/flagd/src/lib/configuration.spec.ts index 4a0b2d905..7f928e6bc 100644 --- a/libs/providers/flagd/src/lib/configuration.spec.ts +++ b/libs/providers/flagd/src/lib/configuration.spec.ts @@ -2,6 +2,10 @@ import type { Config, FlagdProviderOptions } from './configuration'; import { getConfig } from './configuration'; import { DEFAULT_MAX_CACHE_SIZE } from './constants'; import type { EvaluationContext } from '@openfeature/server-sdk'; +import { configSteps } from '../e2e/step-definitions/configSteps'; +import type { State } from '../e2e/step-definitions/state'; +import { autoBindSteps, loadFeatures } from 'jest-cucumber'; +import { CONFIG_FEATURE } from '../e2e'; describe('Configuration', () => { const OLD_ENV = process.env; @@ -163,4 +167,23 @@ describe('Configuration', () => { }); }); }); + + describe('config.feature', () => { + const state: State = { + resolverType: 'in-process', + options: {}, + config: undefined, + events: [], + }; + + autoBindSteps( + loadFeatures(CONFIG_FEATURE, { + scenarioNameTemplate: (vars) => { + const tags = [...new Set([...vars.scenarioTags, ...vars.featureTags])]; + return `${vars.scenarioTitle}${tags.length > 0 ? ` (${tags.join(', ')})` : ''}`; + }, + }), + [configSteps(state)], + ); + }); });