Skip to content

Commit 1cedd26

Browse files
authored
Merge pull request #40 from ServerlessLife/unhandled-timeouts
fix: fix an issue that made it impossible to catch the timeout exception
2 parents 0910194 + 905601a commit 1cedd26

File tree

5 files changed

+46
-36
lines changed

5 files changed

+46
-36
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

listener/WsListener.ts

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { ServerlessSpyListenerParams } from './ServerlessSpyListenerParams';
55
import { getTopic } from './topic';
66
import { WaitForParams } from './WaitForParams';
77
import { FunctionRequestSpyEvent } from '../common/spyEvents/FunctionRequestSpyEvent';
8-
import { SpyEvent } from '../common/spyEvents/SpyEvent';
98
import { SpyMessage } from '../common/spyEvents/SpyMessage';
109

1110
export class WsListener<TSpyEvents> {
@@ -229,46 +228,49 @@ export class WsListener<TSpyEvents> {
229228
serviceKeyForFunction: string,
230229
functionContextAwsRequestId?: string
231230
) {
232-
let tracker: Tracker;
233-
234-
const promise = new Promise((resolve, reject) => {
235-
tracker = {
231+
return (paramsW?: WaitForParams) => {
232+
let resolve: (value: void | PromiseLike<any>) => void;
233+
const promise = new Promise((res) => {
234+
resolve = res;
235+
});
236+
const tracker: Tracker = {
236237
finished: false,
238+
// @ts-ignore
237239
promiseResolve: resolve,
238-
promiseReject: reject,
239240
serviceKeyForFunction,
240241
functionContextAwsRequestId,
241242
};
242-
});
243243

244-
return (paramsW?: WaitForParams<SpyEvent>) => {
245244
tracker.condition = paramsW?.condition;
246245

247-
const timer = setTimeout(() => {
248-
if (tracker.finished) return;
249-
tracker.finished = true;
250-
let message = `Timeout waiting for Serverless Spy message ${serviceKeyForFunction}.`;
251-
252-
if (tracker.possibleSpyMessageDataForDebugging) {
253-
message += ` Similar matching spy event data: ${JSON.stringify(
254-
tracker.possibleSpyMessageDataForDebugging,
255-
null,
256-
2
257-
)}`;
258-
}
259-
260-
tracker.promiseReject(new Error(message));
261-
}, paramsW?.timoutMs || 10000);
246+
let timeoutPid: NodeJS.Timeout | undefined;
247+
const timer = new Promise((_, reject) => {
248+
timeoutPid = setTimeout(() => {
249+
if (tracker.finished) return;
250+
tracker.finished = true;
251+
let message = `Timeout waiting for Serverless Spy message ${serviceKeyForFunction}.`;
252+
253+
if (tracker.possibleSpyMessageDataForDebugging) {
254+
message += ` Similar matching spy event data: ${JSON.stringify(
255+
tracker.possibleSpyMessageDataForDebugging,
256+
null,
257+
2
258+
)}`;
259+
}
262260

263-
void promise.finally(() => {
264-
clearTimeout(timer);
261+
reject(new Error(message));
262+
}, paramsW?.timoutMs || 10000);
265263
});
266264

267265
if (!this.resolveTrackerInOldMessages(tracker)) {
268266
this.trackers.push(tracker);
269267
}
270268

271-
return promise;
269+
return Promise.race([promise, timer]).finally(() => {
270+
if (!!timeoutPid) {
271+
clearTimeout(timeoutPid);
272+
}
273+
});
272274
};
273275
}
274276

@@ -279,10 +281,10 @@ export class WsListener<TSpyEvents> {
279281
await this.stop();
280282
};
281283

282-
const proxy = new Proxy(spyListener, {
284+
return new Proxy<ServerlessSpyListener<TSpyEvents>>(spyListener, {
283285
get: (target: any, objectKey: string) => {
284286
if (target.hasOwnProperty(objectKey)) {
285-
return target[objectKey];
287+
return target[objectKey].bind(target);
286288
} else if (
287289
typeof objectKey === 'string' &&
288290
objectKey.startsWith(this.functionPrefix)
@@ -295,8 +297,6 @@ export class WsListener<TSpyEvents> {
295297
}
296298
},
297299
});
298-
299-
return proxy as ServerlessSpyListener<TSpyEvents>;
300300
}
301301

302302
private log(message: string, ...optionalParams: any[]) {
@@ -313,7 +313,6 @@ export class WsListener<TSpyEvents> {
313313

314314
type Tracker = {
315315
promiseResolve: (data: any) => void;
316-
promiseReject: (data: any) => void;
317316
finished: boolean;
318317
serviceKey?: string;
319318
serviceKeyForFunction?: string;

test/cdk/.projen/tasks.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/cdk/test/__snapshots__/snsToSqs.test.ts.snap

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/cdk/test/lambda.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ describe('Lambda', () => {
173173
});
174174
});
175175

176+
test('Timeout test', async () => {
177+
const res = serverlessSpyListener.waitForFunctionMyLambdaThatFailsRequest({
178+
condition: (event) => event.request === 'aabbcc',
179+
timoutMs: 50,
180+
});
181+
await expect(res).rejects.toThrow(/Timeout\s.*/);
182+
});
183+
176184
test('Test error', async () => {
177185
const lambdaClient = new LambdaClient({});
178186

0 commit comments

Comments
 (0)