Skip to content

Commit bfea9f3

Browse files
committed
ci: fixing contract test breaking due to async hooks
1 parent 7c73024 commit bfea9f3

5 files changed

Lines changed: 42 additions & 7 deletions

File tree

packages/sdk/server-node/contract-tests/src/sdkClientEntity.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ export function makeSdkConfig(options: ServerSDKConfigParams, tag: string): LDOp
8888
}
8989

9090
if (options.hooks) {
91-
cf.hooks = options.hooks.hooks.map(
92-
(hook) => new TestHook(hook.name, hook.callbackUri, hook.data, hook.errors),
93-
);
91+
cf.hooks = TestHook.forClient(options.hooks.hooks);
9492
}
9593

9694
if (options.wrapper) {

packages/tooling/contract-test-utils/src/client-side/TestHook.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@ import {
88
} from '@launchdarkly/js-client-sdk-common';
99

1010
import { BaseTestHook } from '../shared/BaseTestHook.js';
11+
import { HookPostQueue } from '../shared/HookPostQueue.js';
12+
import { SDKConfigHookInstance } from '../types/ConfigParams.js';
1113

1214
export default class TestHook extends BaseTestHook implements Hook {
15+
static forClient(configs: ReadonlyArray<SDKConfigHookInstance>): TestHook[] {
16+
const queue = new HookPostQueue();
17+
return configs.map((c) => new TestHook(c.name, c.callbackUri, c.data, c.errors, queue));
18+
}
19+
1320
protected async safePost(body: unknown): Promise<void> {
1421
try {
1522
await fetch(this.endpoint, {
@@ -52,7 +59,7 @@ export default class TestHook extends BaseTestHook implements Hook {
5259
if (this.hookErrors?.afterTrack) {
5360
throw new Error(this.hookErrors.afterTrack);
5461
}
55-
this.safePost({
62+
this.enqueuePost({
5663
trackSeriesContext: hookContext,
5764
stage: 'afterTrack',
5865
});

packages/tooling/contract-test-utils/src/server-side/TestHook.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import { integrations, LDEvaluationDetail } from '@launchdarkly/js-server-sdk-common';
22

33
import { BaseTestHook } from '../shared/BaseTestHook.js';
4+
import { HookPostQueue } from '../shared/HookPostQueue.js';
5+
import { SDKConfigHookInstance } from '../types/ConfigParams.js';
46

57
export default class TestHook extends BaseTestHook implements integrations.Hook {
8+
static forClient(configs: ReadonlyArray<SDKConfigHookInstance>): TestHook[] {
9+
const queue = new HookPostQueue();
10+
return configs.map((c) => new TestHook(c.name, c.callbackUri, c.data, c.errors, queue));
11+
}
12+
613
protected async safePost(body: unknown): Promise<void> {
714
try {
815
await fetch(this.endpoint, {

packages/tooling/contract-test-utils/src/shared/BaseTestHook.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
11
import { HookData, HookErrors } from '../types/CommandParams.js';
22

3+
import { HookPostQueue } from './HookPostQueue.js';
4+
35
export abstract class BaseTestHook {
46
protected readonly hookName: string;
57
protected readonly endpoint: string;
68
protected readonly hookData?: HookData;
79
protected readonly hookErrors?: HookErrors;
10+
private readonly _postQueue: HookPostQueue;
811

9-
constructor(name: string, endpoint: string, data?: HookData, errors?: HookErrors) {
12+
constructor(
13+
name: string,
14+
endpoint: string,
15+
data?: HookData,
16+
errors?: HookErrors,
17+
postQueue?: HookPostQueue,
18+
) {
1019
this.hookName = name;
1120
this.endpoint = endpoint;
1221
this.hookData = data;
1322
this.hookErrors = errors;
23+
this._postQueue = postQueue ?? new HookPostQueue();
1424
}
1525

1626
protected abstract safePost(body: unknown): Promise<void>;
1727

28+
protected enqueuePost(body: unknown): void {
29+
this._postQueue.enqueue(() => this.safePost(body));
30+
}
31+
1832
getMetadata() {
1933
return { name: this.hookName };
2034
}
@@ -26,7 +40,7 @@ export abstract class BaseTestHook {
2640
if (this.hookErrors?.beforeEvaluation) {
2741
throw new Error(this.hookErrors.beforeEvaluation);
2842
}
29-
this.safePost({
43+
this.enqueuePost({
3044
evaluationSeriesContext: hookContext,
3145
evaluationSeriesData: data,
3246
stage: 'beforeEvaluation',
@@ -42,7 +56,7 @@ export abstract class BaseTestHook {
4256
if (this.hookErrors?.afterEvaluation) {
4357
throw new Error(this.hookErrors.afterEvaluation);
4458
}
45-
this.safePost({
59+
this.enqueuePost({
4660
evaluationSeriesContext: hookContext,
4761
evaluationSeriesData: data,
4862
stage: 'afterEvaluation',
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// The test harness records hook callbacks in network-arrival order, so
2+
// parallel fetches break ordering assertions. Chain them instead.
3+
export class HookPostQueue {
4+
private _chain: Promise<void> = Promise.resolve();
5+
6+
enqueue(fn: () => Promise<void>): void {
7+
this._chain = this._chain.then(fn).catch(() => {});
8+
}
9+
}

0 commit comments

Comments
 (0)