Skip to content

Commit

Permalink
fixup: upgarding test-harnes
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Schrottner <[email protected]>
  • Loading branch information
aepfli committed Feb 21, 2025
1 parent 5f51211 commit 2080024
Show file tree
Hide file tree
Showing 15 changed files with 83 additions and 63 deletions.
4 changes: 4 additions & 0 deletions libs/providers/flagd/src/e2e/step-definitions/configSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ export const configSteps: Steps = (state: State) => {
expect(configElement).toBe(expected);
},
);

then('we should have an error', () => {

});
};
};
15 changes: 12 additions & 3 deletions libs/providers/flagd/src/e2e/step-definitions/eventSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
});
};
11 changes: 0 additions & 11 deletions libs/providers/flagd/src/e2e/step-definitions/flagSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
);
});
};
62 changes: 31 additions & 31 deletions libs/providers/flagd/src/e2e/step-definitions/providerSteps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,59 @@ 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<string, FlagdContainer> & { 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<string, unknown> = {
resolverType: state.resolverType,
deadlineMs: 2000,
};
let type = 'default';
switch (providerType) {
default:
flagdOptions['port'] = container.getPort(state.resolverType);
break;
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 {
Expand All @@ -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));
});
};
17 changes: 9 additions & 8 deletions libs/providers/flagd/src/e2e/tests/flagdContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion libs/providers/flagd/src/e2e/tests/in-process.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
Expand Down
3 changes: 2 additions & 1 deletion libs/providers/flagd/src/e2e/tests/rpc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(',')})`;
},
Expand Down
12 changes: 12 additions & 0 deletions libs/providers/flagd/src/lib/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ export interface Config {
*/
port: number;

/**
* The deadline for connections.
*
* @default 500
*/
deadlineMs: number;

/**
* Determines if TLS should be used.
*
Expand Down Expand Up @@ -79,6 +86,7 @@ export interface Config {
export type FlagdProviderOptions = Partial<Config>;

const DEFAULT_CONFIG: Omit<Config, 'port' | 'resolverType'> = {
deadlineMs: 500,
host: 'localhost',
tls: false,
selector: '',
Expand All @@ -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',
Expand Down Expand Up @@ -128,6 +137,9 @@ const getEnvVarConfig = (): Partial<Config> => {
...(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',
}),
Expand Down
8 changes: 6 additions & 2 deletions libs/providers/flagd/src/lib/service/grpc/grpc-service.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -70,6 +70,8 @@ export class GRPCService implements Service {
private _cache: LRUCache<string, ResolutionDetails<FlagValue>> | undefined;
private _cacheEnabled = false;
private _eventStream: ClientReadableStream<EventStreamResponse> | 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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<T>;
}
Expand Down
2 changes: 1 addition & 1 deletion libs/shared/flagd-core/src/e2e/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<rootdir>/../../../../../shared/flagd-core/${modulePath}${file}`;
}

0 comments on commit 2080024

Please sign in to comment.