fix(core): Fix resuming executions on test webhooks from Wait forms (#13410)

This commit is contained in:
Michael Kret 2025-02-21 18:44:09 +02:00 committed by GitHub
parent 4fa666b976
commit 8ffd3167d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 8 deletions

View file

@ -1,12 +1,16 @@
import type express from 'express';
import { mock } from 'jest-mock-extended'; import { mock } from 'jest-mock-extended';
import { FORM_NODE_TYPE, type Workflow } from 'n8n-workflow'; import { FORM_NODE_TYPE, type Workflow } from 'n8n-workflow';
import type { ExecutionRepository } from '@/databases/repositories/execution.repository'; import type { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WaitingForms } from '@/webhooks/waiting-forms'; import { WaitingForms } from '@/webhooks/waiting-forms';
import type { IExecutionResponse } from '../../interfaces';
import type { WaitingWebhookRequest } from '../webhook.types';
describe('WaitingForms', () => { describe('WaitingForms', () => {
const executionRepository = mock<ExecutionRepository>(); const executionRepository = mock<ExecutionRepository>();
const waitingWebhooks = new WaitingForms(mock(), mock(), executionRepository, mock()); const waitingForms = new WaitingForms(mock(), mock(), executionRepository, mock());
beforeEach(() => { beforeEach(() => {
jest.restoreAllMocks(); jest.restoreAllMocks();
@ -27,7 +31,7 @@ describe('WaitingForms', () => {
}, },
}); });
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'Form1'); const result = waitingForms.findCompletionPage(workflow, {}, 'Form1');
expect(result).toBe('Form1'); expect(result).toBe('Form1');
}); });
@ -45,7 +49,7 @@ describe('WaitingForms', () => {
}, },
}); });
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'Form1'); const result = waitingForms.findCompletionPage(workflow, {}, 'Form1');
expect(result).toBeUndefined(); expect(result).toBeUndefined();
}); });
@ -61,7 +65,7 @@ describe('WaitingForms', () => {
}, },
}); });
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'NonForm'); const result = waitingForms.findCompletionPage(workflow, {}, 'NonForm');
expect(result).toBeUndefined(); expect(result).toBeUndefined();
}); });
@ -79,7 +83,7 @@ describe('WaitingForms', () => {
}, },
}); });
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'Form1'); const result = waitingForms.findCompletionPage(workflow, {}, 'Form1');
expect(result).toBeUndefined(); expect(result).toBeUndefined();
}); });
@ -121,7 +125,7 @@ describe('WaitingForms', () => {
Form3: [], Form3: [],
}; };
const result = waitingWebhooks.findCompletionPage(workflow, runData, 'LastNode'); const result = waitingForms.findCompletionPage(workflow, runData, 'LastNode');
expect(result).toBe('Form3'); expect(result).toBe('Form3');
}); });
@ -151,7 +155,7 @@ describe('WaitingForms', () => {
}, },
}); });
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'LastNode'); const result = waitingForms.findCompletionPage(workflow, {}, 'LastNode');
expect(result).toBeUndefined(); expect(result).toBeUndefined();
}); });
@ -192,8 +196,29 @@ describe('WaitingForms', () => {
Form2: [], Form2: [],
}; };
const result = waitingWebhooks.findCompletionPage(workflow, runData, 'LastNode'); const result = waitingForms.findCompletionPage(workflow, runData, 'LastNode');
expect(result).toBe('Form2'); expect(result).toBe('Form2');
}); });
it('should mark as test form webhook when execution mode is manual', async () => {
jest
// @ts-expect-error Protected method
.spyOn(waitingForms, 'getWebhookExecutionData')
// @ts-expect-error Protected method
.mockResolvedValue(mock<IWebhookResponseCallbackData>());
const execution = mock<IExecutionResponse>({
finished: false,
mode: 'manual',
data: {
resultData: { lastNodeExecuted: 'someNode', error: undefined },
},
});
executionRepository.findSingleExecution.mockResolvedValue(execution);
await waitingForms.executeWebhook(mock<WaitingWebhookRequest>(), mock<express.Response>());
expect(execution.data.isTestWebhook).toBe(true);
});
}); });
}); });

View file

@ -116,6 +116,12 @@ export class WaitingForms extends WaitingWebhooks {
} }
} }
/**
* A manual execution resumed by a webhook call needs to be marked as such
* so workers in scaling mode reuse the existing execution data.
*/
if (execution.mode === 'manual') execution.data.isTestWebhook = true;
return await this.getWebhookExecutionData({ return await this.getWebhookExecutionData({
execution, execution,
req, req,