I have a function that calls itself recursively until the function it's calling is done. Here is the function under test (I have modified it in order to post on Stackoverflow as it is proprietary.):
private async awaitQueryCompletion(queryId: string): Promise<void> {
setTimeout(async () => {
const output: ExecutionOutput =
await this.getQueryExecution({ QueryExecutionId: queryId }).promise();
let state: string | undefined;
if (ExecutionOutput.QueryExecution !== undefined && ExecutionOutput.QueryExecution.Status) {
state = ExecutionOutput.QueryExecution.Status.State;
}
if (state !== undefined && state === "RUNNING") {
await this.awaitQueryCompletion(queryId);
}
}, this.RETRY_INTERVAL);
}
Here is my test: Setup:
beforeEach(() => {
sandbox = createSandbox();
getQueryExecutionStub = sandbox.stub(QueryClass, "getQueryExecution")
timer = sandbox.useFakeTimers({ shouldAdvanceTime: true});
});
it.only("Should call getQueryExecution twice with correct params", async () => {
const INTERVAL: number = 5010;
getQueryExecutionStub.onFirstCall()
.returns({
promise: async (): Promise<ExecutionOutput> => {
return Promise.resolve({
QueryExecution: {
Status: {
State: "RUNNING"
}
}
});
}
});
getQueryExecutionStub.onSecondCall()
.returns({promise: async (): Promise<ExecutionOutput> => {
return Promise.resolve({
QueryExecution: {
Status: {
State: "SUCCEEDED"
}
}
});
}
});
await selector.query(testInput);
timer.tick(INTERVAL);
timer.tick(INTERVAL);
expect(getQueryExecutionStub.calledTwice).to.equal(true);
});
});
What I want is for the getQueryExecutionStub to be called twice so I'm mocking the setTimeout function and trying to act as thought two cycles of the timeout have happened. I get it to run the timeout once but I can't figure out how to make it run again. Any and all help would be greatly appreciated! I've looked through both the lolex docs:(https://github.com/sinonjs/lolex) and sinon fake timers docs (https://sinonjs.org/releases/v8.0.4/fake-timers/).
So I was able to figure this out after a little digging.
private async awaitQueryCompletion(queryId: string, context: Context): Promise<void> {
return new Promise(async (resolve: Function, reject: Function): Promise<void> => {
// tslint:disable-next-line: no-inferred-empty-object-type
this.timeout(async () => {
try {
log.debug("Checking query completion");
const queryExecutionOutput: Athena.GetQueryExecutionOutput =
await this.athenaClient.getQueryExecution({ QueryExecutionId: queryId }).promise();
let state: string | undefined;
if (queryExecutionOutput.QueryExecution !== undefined
&& queryExecutionOutput.QueryExecution.Status !== undefined) {
state = queryExecutionOutput.QueryExecution.Status.State;
}
if (state !== undefined && state === "RUNNING") {
if (context.getRemainingTimeInMillis() > this.TIMEOUT_PADDING_MS) {
await this.awaitQueryCompletion(queryId, context);
resolve();
} else {
log.error(`Failure: Unable to complete query before lambda shut down...`);
reject();
}
} else if (state === "SUCCEEDED") {
resolve();
} else if (state === "FAILED") {
throw new Error(state);
} else {
log.error("Unable to determine the state of the query...");
reject();
}
} catch (e) {
log.error(`${JSON.stringify(e)}`);
return reject(e);
}
}, this.RETRY_INTERVAL_MS);
});
}
What I needed was to wrap my function in a promise and after each tick
resolve that promise to the next tick
could fire.