From 2080024bff0a1e5408fa9730e23fb4c5d9738f13 Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Fri, 21 Feb 2025 13:45:01 +0100 Subject: [PATCH] fixup: upgarding test-harnes Signed-off-by: Simon Schrottner --- libs/providers/flagd-web/schemas | 2 +- libs/providers/flagd/schemas | 2 +- libs/providers/flagd/spec | 2 +- .../src/e2e/step-definitions/configSteps.ts | 4 ++ .../src/e2e/step-definitions/eventSteps.ts | 15 ++++- .../src/e2e/step-definitions/flagSteps.ts | 11 ---- .../src/e2e/step-definitions/providerSteps.ts | 62 +++++++++---------- .../flagd/src/e2e/tests/flagdContainer.ts | 17 ++--- .../flagd/src/e2e/tests/in-process.spec.ts | 2 +- .../providers/flagd/src/e2e/tests/rpc.spec.ts | 3 +- libs/providers/flagd/src/lib/configuration.ts | 12 ++++ .../src/lib/service/grpc/grpc-service.ts | 8 ++- libs/shared/flagd-core/flagd-schemas | 2 +- libs/shared/flagd-core/spec | 2 +- libs/shared/flagd-core/src/e2e/index.ts | 2 +- 15 files changed, 83 insertions(+), 63 deletions(-) diff --git a/libs/providers/flagd-web/schemas b/libs/providers/flagd-web/schemas index bb763438a..76d611fd9 160000 --- a/libs/providers/flagd-web/schemas +++ b/libs/providers/flagd-web/schemas @@ -1 +1 @@ -Subproject commit bb763438abc5b0e97dc26a795bc72b7a9f3e4020 +Subproject commit 76d611fd94689d906af316105ac12670d40f7648 diff --git a/libs/providers/flagd/schemas b/libs/providers/flagd/schemas index bb763438a..2aa89b314 160000 --- a/libs/providers/flagd/schemas +++ b/libs/providers/flagd/schemas @@ -1 +1 @@ -Subproject commit bb763438abc5b0e97dc26a795bc72b7a9f3e4020 +Subproject commit 2aa89b31432284507af3873de9b0bb7b68dd02c7 diff --git a/libs/providers/flagd/spec b/libs/providers/flagd/spec index a69f748db..ffebdecd7 160000 --- a/libs/providers/flagd/spec +++ b/libs/providers/flagd/spec @@ -1 +1 @@ -Subproject commit a69f748db2edfec7015ca6bb702ca22fd8c5ef30 +Subproject commit ffebdecd725ed1a19c96927f66472c86e97ce551 diff --git a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts index 62c925574..784ece4b4 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/configSteps.ts @@ -38,5 +38,9 @@ export const configSteps: Steps = (state: State) => { expect(configElement).toBe(expected); }, ); + + then('we should have an error', () => { + + }); }; }; diff --git a/libs/providers/flagd/src/e2e/step-definitions/eventSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/eventSteps.ts index d89dee45c..2e1f6f0d1 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/eventSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/eventSteps.ts @@ -34,16 +34,25 @@ export const eventSteps: Steps = then(/^the (.*) event handler should have been executed$/, async (type: string) => { await waitFor(() => expect(state.events.find((value) => value.type == type)).toBeDefined(), { timeout: 20000 }); expect(state.events.find((value) => value.type == type)).toBeDefined(); - state.events = state.events.filter((a) => a.type !== type); + state.events = []; }); then(/^the (.*) event handler should have been executed within (\d+)ms$/, async (type: string, ms: number) => { await waitFor(() => expect(state.events.find((value) => value.type == type)).toBeDefined(), { timeout: ms }); const actual = state.events.find((value) => value.type == type); expect(actual).toBeDefined(); - state.events = state.events.filter((a) => a.type !== type); + state.events = []; console.error('here bin cih'); }); - when(/^a (.*) event was fired$/, () => {}); + when(/^a (.*) event was fired$/, async (type: string) => { + await waitFor(() => expect(state.events.find((value) => value.type == type)), { timeout: 2000 }); + expect(state.events.find((value) => value.type == type)).toBeDefined(); + state.events = []; + }); + + then('the flag should be part of the event payload', async () => { + await waitFor(() => expect(state.events.find((value) => value.type == 'change')), { timeout: 2000 }); + state.events = []; + }); }; diff --git a/libs/providers/flagd/src/e2e/step-definitions/flagSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/flagSteps.ts index c08f41d9c..9a598c7c0 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/flagSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/flagSteps.ts @@ -64,15 +64,4 @@ export const flagSteps: Steps = then(/^the variant should be "(.*)"$/, (arg0) => { expect(state.details?.variant).toBe(arg0); }); - when('the flag was modified', async () => { - await waitFor( - () => - expect( - state.events.find( - (value) => value.type == 'change' && (value.details?.flagsChanged as string[]).includes(state.flag!.name), - ), - ).toBeDefined(), - { timeout: 5000 }, - ); - }); }; diff --git a/libs/providers/flagd/src/e2e/step-definitions/providerSteps.ts b/libs/providers/flagd/src/e2e/step-definitions/providerSteps.ts index b50690151..fe5ebd22d 100644 --- a/libs/providers/flagd/src/e2e/step-definitions/providerSteps.ts +++ b/libs/providers/flagd/src/e2e/step-definitions/providerSteps.ts @@ -2,56 +2,43 @@ import { OpenFeature } from '@openfeature/server-sdk'; import { FlagdContainer } from '../tests/flagdContainer'; import { State, Steps } from './state'; import { FlagdProvider } from '../../lib/flagd-provider'; - -type Containers = Record & { default: FlagdContainer }; +import { waitFor } from "./utils"; export const providerSteps: Steps = (state: State) => ({ given, when, then }) => { - const containers: Containers = { - default: FlagdContainer.build(), - }; + const container: FlagdContainer = FlagdContainer.build(); beforeAll(async () => { console.log('Setting flagd provider...'); - const promises = []; - - for (const container of Object.values(containers)) { - promises.push(container.start()); - } - - return Promise.all(promises); + return container.start(); }, 50000); afterAll(async () => { await OpenFeature.close(); - for (const container of Object.values(containers)) { - await container.stop(); - } + await container.stop(); }); beforeEach(async () => { - const promises = []; - - for (const container of Object.values(containers)) { - promises.push(container.start()); + if (container.isStarted()) { + return container.start(); } - - return Promise.all(promises); + return Promise.resolve(); }, 50000); - - function getContainer(providerType: string): FlagdContainer { - if (Object.hasOwn(containers, providerType)) { - return containers[providerType]; + afterEach(async () => { + if (state.client) { + await fetch('http://' + container.getLaunchpadUrl() + '/stop'); + await new Promise((r) => setTimeout(r, 100)); } - return containers.default; - } + return Promise.resolve(); + }, 50000); given(/a (.*) flagd provider/, async (providerType: string) => { - const container = getContainer(providerType); const flagdOptions: Record = { resolverType: state.resolverType, + deadlineMs: 2000, }; + let type = 'default'; switch (providerType) { default: flagdOptions['port'] = container.getPort(state.resolverType); @@ -59,7 +46,15 @@ export const providerSteps: Steps = case 'unavailable': flagdOptions['port'] = 9999; break; + case 'ssl': + // todo: configure properly + flagdOptions['port'] = container.getPort(state.resolverType); + type = 'ssl'; + break; } + + await fetch('http://' + container.getLaunchpadUrl() + '/start?config=' + type); + await new Promise((r) => setTimeout(r, 50)); if (providerType == 'unavailable') { OpenFeature.setProvider(providerType, new FlagdProvider(flagdOptions)); } else { @@ -71,8 +66,13 @@ export const providerSteps: Steps = }); when(/^the connection is lost for (\d+)s$/, async (time) => { - const container = getContainer(state.providerType!); - await container.stop(); - setTimeout(() => container.start(), time * 1000); + console.log('stopping flagd'); + await fetch('http://' + container.getLaunchpadUrl() + '/restart?seconds=' + time); + await new Promise((r) => setTimeout(r, 50)); + }); + + when('the flag was modified', async () => { + await fetch('http://' + container.getLaunchpadUrl() + '/change'); + await new Promise((r) => setTimeout(r, 50)); }); }; diff --git a/libs/providers/flagd/src/e2e/tests/flagdContainer.ts b/libs/providers/flagd/src/e2e/tests/flagdContainer.ts index 762673973..d0e45f4c0 100644 --- a/libs/providers/flagd/src/e2e/tests/flagdContainer.ts +++ b/libs/providers/flagd/src/e2e/tests/flagdContainer.ts @@ -8,12 +8,13 @@ export class FlagdContainer extends GenericContainer { private started: StartedTestContainer | undefined; private stopped: StoppedTestContainer | undefined; - public static build(feature: string | undefined = undefined) { - return new FlagdContainer(this.generateImageName(feature)).withExposedPorts(8013, 8014, 8015, 8016); + public static build() { + return new FlagdContainer(this.generateImageName()); } private constructor(image: string) { super(image); + this.withExposedPorts(8080, 8013, 8014, 8015, 8016); } isStarted(): boolean { @@ -42,15 +43,15 @@ export class FlagdContainer extends GenericContainer { return containerPromise; } - private static generateImageName(feature: string | undefined): string { + getLaunchpadUrl() { + return this.started?.getHost() + ':' + this.started?.getMappedPort(8080); + } + + private static generateImageName(): string { const image = this.imageBase; const file = path.join(__dirname, './../../../../../shared/flagd-core/test-harness/', 'version.txt'); const version = fs.readFileSync(file, 'utf8').trim(); - let featurePart = ''; - if (feature) { - featurePart = `-${feature}`; - } - return `${image}${featurePart}:v${version}`; + return `${image}:v${version}`; } getPort(resolverType: ResolverType) { diff --git a/libs/providers/flagd/src/e2e/tests/in-process.spec.ts b/libs/providers/flagd/src/e2e/tests/in-process.spec.ts index 2835479b3..a6b16e4b1 100644 --- a/libs/providers/flagd/src/e2e/tests/in-process.spec.ts +++ b/libs/providers/flagd/src/e2e/tests/in-process.spec.ts @@ -10,7 +10,7 @@ import { contextSteps } from '../step-definitions/contextSteps'; const steps = [providerSteps, configSteps, eventSteps, flagSteps, contextSteps]; jest.setTimeout(50000); -describe('rpc', () => { +describe('in-process', () => { const state: State = { resolverType: 'in-process', options: {}, diff --git a/libs/providers/flagd/src/e2e/tests/rpc.spec.ts b/libs/providers/flagd/src/e2e/tests/rpc.spec.ts index 2c1a593e4..f8d7f9fc8 100644 --- a/libs/providers/flagd/src/e2e/tests/rpc.spec.ts +++ b/libs/providers/flagd/src/e2e/tests/rpc.spec.ts @@ -20,7 +20,8 @@ describe('rpc', () => { }; autoBindSteps( loadFeatures(GHERKIN_FLAGD, { - tagFilter: '@rpc and not @targetURI and not @customCert and not @events and not @sync and not @offline and not @grace', + tagFilter: + '@rpc and not @targetURI and not @customCert and not @events and not @stream and not @grace', scenarioNameTemplate: (vars) => { return `${vars.scenarioTitle} (${vars.scenarioTags.join(',')} ${vars.featureTags.join(',')})`; }, diff --git a/libs/providers/flagd/src/lib/configuration.ts b/libs/providers/flagd/src/lib/configuration.ts index 64834072c..da8faff64 100644 --- a/libs/providers/flagd/src/lib/configuration.ts +++ b/libs/providers/flagd/src/lib/configuration.ts @@ -18,6 +18,13 @@ export interface Config { */ port: number; + /** + * The deadline for connections. + * + * @default 500 + */ + deadlineMs: number; + /** * Determines if TLS should be used. * @@ -79,6 +86,7 @@ export interface Config { export type FlagdProviderOptions = Partial; const DEFAULT_CONFIG: Omit = { + deadlineMs: 500, host: 'localhost', tls: false, selector: '', @@ -93,6 +101,7 @@ const DEFAULT_IN_PROCESS_CONFIG: Config = { ...DEFAULT_CONFIG, resolverType: 'in enum ENV_VAR { FLAGD_HOST = 'FLAGD_HOST', FLAGD_PORT = 'FLAGD_PORT', + FLAGD_DEADLINE_MS = 'FLAGD_DEADLINE_MS', FLAGD_TLS = 'FLAGD_TLS', FLAGD_SOCKET_PATH = 'FLAGD_SOCKET_PATH', FLAGD_CACHE = 'FLAGD_CACHE', @@ -128,6 +137,9 @@ const getEnvVarConfig = (): Partial => { ...(Number(process.env[ENV_VAR.FLAGD_PORT]) && { port: Number(process.env[ENV_VAR.FLAGD_PORT]), }), + ...(Number(process.env[ENV_VAR.FLAGD_DEADLINE_MS]) && { + deadlineMs: Number(process.env[ENV_VAR.FLAGD_DEADLINE_MS]), + }), ...(process.env[ENV_VAR.FLAGD_TLS] && { tls: process.env[ENV_VAR.FLAGD_TLS]?.toLowerCase() === 'true', }), diff --git a/libs/providers/flagd/src/lib/service/grpc/grpc-service.ts b/libs/providers/flagd/src/lib/service/grpc/grpc-service.ts index f98a230e5..ac6a416eb 100644 --- a/libs/providers/flagd/src/lib/service/grpc/grpc-service.ts +++ b/libs/providers/flagd/src/lib/service/grpc/grpc-service.ts @@ -1,4 +1,4 @@ -import { ClientReadableStream, ClientUnaryCall, ServiceError, credentials, status, ClientOptions } from '@grpc/grpc-js'; +import { ClientOptions, ClientReadableStream, ClientUnaryCall, credentials, ServiceError, status } from '@grpc/grpc-js'; import { ConnectivityState } from '@grpc/grpc-js/build/src/connectivity-state'; import { EvaluationContext, @@ -70,6 +70,8 @@ export class GRPCService implements Service { private _cache: LRUCache> | undefined; private _cacheEnabled = false; private _eventStream: ClientReadableStream | undefined = undefined; + private _deadline: number; + private get _cacheActive() { // the cache is "active" (able to be used) if the config enabled it, AND the gRPC stream is live return this._cacheEnabled && this._client.getChannel().getConnectivityState(false) === ConnectivityState.READY; @@ -95,6 +97,7 @@ export class GRPCService implements Service { tls ? credentials.createSsl() : credentials.createInsecure(), clientOptions, ); + this._deadline = config.deadlineMs; if (config.cache === 'lru') { this._cacheEnabled = true; @@ -165,7 +168,7 @@ export class GRPCService implements Service { // close the previous stream if we're reconnecting closeStreamIfDefined(this._eventStream); - const stream = this._client.eventStream({}, {}); + const stream = this._client.eventStream({ waitForReady: true}, {}); stream.on('error', (err: Error) => { rejectConnect?.(err); this.handleError(reconnectCallback, changedCallback, disconnectCallback); @@ -240,6 +243,7 @@ export class GRPCService implements Service { const resolver = promisify(promise); if (this._cacheActive) { const cached = this._cache?.get(flagKey); + console.log('cache active and this was in the cache', cached); if (cached) { return { ...cached, reason: StandardResolutionReasons.CACHED } as ResolutionDetails; } diff --git a/libs/shared/flagd-core/flagd-schemas b/libs/shared/flagd-core/flagd-schemas index bb763438a..76d611fd9 160000 --- a/libs/shared/flagd-core/flagd-schemas +++ b/libs/shared/flagd-core/flagd-schemas @@ -1 +1 @@ -Subproject commit bb763438abc5b0e97dc26a795bc72b7a9f3e4020 +Subproject commit 76d611fd94689d906af316105ac12670d40f7648 diff --git a/libs/shared/flagd-core/spec b/libs/shared/flagd-core/spec index 5b0706598..ffebdecd7 160000 --- a/libs/shared/flagd-core/spec +++ b/libs/shared/flagd-core/spec @@ -1 +1 @@ -Subproject commit 5b070659853062262e618c74da5b640555f9ae18 +Subproject commit ffebdecd725ed1a19c96927f66472c86e97ce551 diff --git a/libs/shared/flagd-core/src/e2e/index.ts b/libs/shared/flagd-core/src/e2e/index.ts index b3e52724f..e43c3d615 100644 --- a/libs/shared/flagd-core/src/e2e/index.ts +++ b/libs/shared/flagd-core/src/e2e/index.ts @@ -3,5 +3,5 @@ export const E2E_CLIENT_NAME = 'e2e'; export const IMAGE_VERSION = 'v0.5.21'; export function getGherkinTestPath(file: string, modulePath = 'test-harness/gherkin/'): string { - return `./libs/shared/flagd-core/${modulePath}${file}`; + return `/../../../../../shared/flagd-core/${modulePath}${file}`; }