mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): "Respond to Webhook" should work with workflows with waiting nodes (#12806)
This commit is contained in:
parent
18b6867785
commit
e8635f2574
|
@ -41,7 +41,7 @@ describe('ActiveExecutions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should initialize activeExecutions with empty list', () => {
|
test('Should initialize activeExecutions with empty list', () => {
|
||||||
expect(activeExecutions.getActiveExecutions().length).toBe(0);
|
expect(activeExecutions.getActiveExecutions()).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should add execution to active execution list', async () => {
|
test('Should add execution to active execution list', async () => {
|
||||||
|
@ -49,7 +49,7 @@ describe('ActiveExecutions', () => {
|
||||||
const executionId = await activeExecutions.add(newExecution);
|
const executionId = await activeExecutions.add(newExecution);
|
||||||
|
|
||||||
expect(executionId).toBe(FAKE_EXECUTION_ID);
|
expect(executionId).toBe(FAKE_EXECUTION_ID);
|
||||||
expect(activeExecutions.getActiveExecutions().length).toBe(1);
|
expect(activeExecutions.getActiveExecutions()).toHaveLength(1);
|
||||||
expect(createNewExecution).toHaveBeenCalledTimes(1);
|
expect(createNewExecution).toHaveBeenCalledTimes(1);
|
||||||
expect(updateExistingExecution).toHaveBeenCalledTimes(0);
|
expect(updateExistingExecution).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
@ -59,7 +59,7 @@ describe('ActiveExecutions', () => {
|
||||||
const executionId = await activeExecutions.add(newExecution, FAKE_SECOND_EXECUTION_ID);
|
const executionId = await activeExecutions.add(newExecution, FAKE_SECOND_EXECUTION_ID);
|
||||||
|
|
||||||
expect(executionId).toBe(FAKE_SECOND_EXECUTION_ID);
|
expect(executionId).toBe(FAKE_SECOND_EXECUTION_ID);
|
||||||
expect(activeExecutions.getActiveExecutions().length).toBe(1);
|
expect(activeExecutions.getActiveExecutions()).toHaveLength(1);
|
||||||
expect(createNewExecution).toHaveBeenCalledTimes(0);
|
expect(createNewExecution).toHaveBeenCalledTimes(0);
|
||||||
expect(updateExistingExecution).toHaveBeenCalledTimes(1);
|
expect(updateExistingExecution).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
@ -93,6 +93,37 @@ describe('ActiveExecutions', () => {
|
||||||
await expect(deferredPromise.promise).resolves.toEqual(fakeResponse);
|
await expect(deferredPromise.promise).resolves.toEqual(fakeResponse);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Should copy over startedAt and responsePromise when resuming a waiting execution', async () => {
|
||||||
|
const newExecution = mockExecutionData();
|
||||||
|
const executionId = await activeExecutions.add(newExecution);
|
||||||
|
activeExecutions.setStatus(executionId, 'waiting');
|
||||||
|
activeExecutions.attachResponsePromise(executionId, mockDeferredPromise());
|
||||||
|
|
||||||
|
const waitingExecution = activeExecutions.getExecution(executionId);
|
||||||
|
expect(waitingExecution.responsePromise).toBeDefined();
|
||||||
|
|
||||||
|
// Resume the execution
|
||||||
|
await activeExecutions.add(newExecution, executionId);
|
||||||
|
|
||||||
|
const resumedExecution = activeExecutions.getExecution(executionId);
|
||||||
|
expect(resumedExecution.startedAt).toBe(waitingExecution.startedAt);
|
||||||
|
expect(resumedExecution.responsePromise).toBe(waitingExecution.responsePromise);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Should not remove a waiting execution', async () => {
|
||||||
|
const newExecution = mockExecutionData();
|
||||||
|
const executionId = await activeExecutions.add(newExecution);
|
||||||
|
activeExecutions.setStatus(executionId, 'waiting');
|
||||||
|
activeExecutions.finalizeExecution(executionId);
|
||||||
|
|
||||||
|
// Wait until the next tick to ensure that the post-execution promise has settled
|
||||||
|
await new Promise(setImmediate);
|
||||||
|
|
||||||
|
// Execution should still be in activeExecutions
|
||||||
|
expect(activeExecutions.getActiveExecutions()).toHaveLength(1);
|
||||||
|
expect(activeExecutions.getStatus(executionId)).toBe('waiting');
|
||||||
|
});
|
||||||
|
|
||||||
test('Should remove an existing execution', async () => {
|
test('Should remove an existing execution', async () => {
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
const newExecution = mockExecutionData();
|
const newExecution = mockExecutionData();
|
||||||
|
@ -105,11 +136,10 @@ describe('ActiveExecutions', () => {
|
||||||
await new Promise(setImmediate);
|
await new Promise(setImmediate);
|
||||||
|
|
||||||
// ASSERT
|
// ASSERT
|
||||||
expect(activeExecutions.getActiveExecutions().length).toBe(0);
|
expect(activeExecutions.getActiveExecutions()).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should not try to resolve a post-execute promise for an inactive execution', async () => {
|
test('Should not try to resolve a post-execute promise for an inactive execution', async () => {
|
||||||
// @ts-expect-error Private method
|
|
||||||
const getExecutionSpy = jest.spyOn(activeExecutions, 'getExecution');
|
const getExecutionSpy = jest.spyOn(activeExecutions, 'getExecution');
|
||||||
|
|
||||||
activeExecutions.finalizeExecution('inactive-execution-id', mockFullRunData());
|
activeExecutions.finalizeExecution('inactive-execution-id', mockFullRunData());
|
||||||
|
|
|
@ -94,13 +94,15 @@ export class ActiveExecutions {
|
||||||
await this.executionRepository.updateExistingExecution(executionId, execution);
|
await this.executionRepository.updateExistingExecution(executionId, execution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resumingExecution = this.activeExecutions[executionId];
|
||||||
const postExecutePromise = createDeferredPromise<IRun | undefined>();
|
const postExecutePromise = createDeferredPromise<IRun | undefined>();
|
||||||
|
|
||||||
this.activeExecutions[executionId] = {
|
this.activeExecutions[executionId] = {
|
||||||
executionData,
|
executionData,
|
||||||
startedAt: new Date(),
|
startedAt: resumingExecution?.startedAt ?? new Date(),
|
||||||
postExecutePromise,
|
postExecutePromise,
|
||||||
status: executionStatus,
|
status: executionStatus,
|
||||||
|
responsePromise: resumingExecution?.responsePromise,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Automatically remove execution once the postExecutePromise settles
|
// Automatically remove execution once the postExecutePromise settles
|
||||||
|
@ -111,8 +113,10 @@ export class ActiveExecutions {
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.concurrencyControl.release({ mode: executionData.executionMode });
|
this.concurrencyControl.release({ mode: executionData.executionMode });
|
||||||
delete this.activeExecutions[executionId];
|
if (this.activeExecutions[executionId]?.status !== 'waiting') {
|
||||||
this.logger.debug('Execution removed', { executionId });
|
delete this.activeExecutions[executionId];
|
||||||
|
this.logger.debug('Execution removed', { executionId });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.debug('Execution added', { executionId });
|
this.logger.debug('Execution added', { executionId });
|
||||||
|
@ -227,7 +231,7 @@ export class ActiveExecutions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getExecution(executionId: string): IExecutingWorkflowData {
|
getExecution(executionId: string): IExecutingWorkflowData {
|
||||||
const execution = this.activeExecutions[executionId];
|
const execution = this.activeExecutions[executionId];
|
||||||
if (!execution) {
|
if (!execution) {
|
||||||
throw new ExecutionNotFoundError(executionId);
|
throw new ExecutionNotFoundError(executionId);
|
||||||
|
|
Loading…
Reference in a new issue