mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 21:07:28 -08:00
refactor(core): Add tests to workflowPostExecute
in TelemetryEventRelay
(#12437)
This commit is contained in:
parent
1d3cb9f5ac
commit
b78210c58a
|
@ -1,7 +1,8 @@
|
||||||
import type { GlobalConfig } from '@n8n/config';
|
import type { GlobalConfig } from '@n8n/config';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import { InstanceSettings } from 'n8n-core';
|
import { InstanceSettings } from 'n8n-core';
|
||||||
import type { IWorkflowBase } from 'n8n-workflow';
|
import type { INode, INodesGraphResult } from 'n8n-workflow';
|
||||||
|
import { NodeApiError, TelemetryHelpers, type IRun, type IWorkflowBase } from 'n8n-workflow';
|
||||||
|
|
||||||
import { N8N_VERSION } from '@/constants';
|
import { N8N_VERSION } from '@/constants';
|
||||||
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||||
|
@ -28,6 +29,9 @@ describe('TelemetryEventRelay', () => {
|
||||||
mode: 'smtp',
|
mode: 'smtp',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
diagnostics: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
endpoints: {
|
endpoints: {
|
||||||
metrics: {
|
metrics: {
|
||||||
enable: true,
|
enable: true,
|
||||||
|
@ -1106,4 +1110,393 @@ describe('TelemetryEventRelay', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('workflow post execute events', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockWorkflowBase = mock<IWorkflowBase>({
|
||||||
|
id: 'workflow123',
|
||||||
|
name: 'Test Workflow',
|
||||||
|
active: true,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1',
|
||||||
|
name: 'Start',
|
||||||
|
type: 'n8n-nodes-base.start',
|
||||||
|
parameters: {},
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [100, 200],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
connections: {},
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
staticData: {},
|
||||||
|
settings: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not track when workflow has no id', async () => {
|
||||||
|
const event: RelayEventMap['workflow-post-execute'] = {
|
||||||
|
workflow: { ...mockWorkflowBase, id: '' },
|
||||||
|
executionId: 'execution123',
|
||||||
|
userId: 'user123',
|
||||||
|
};
|
||||||
|
|
||||||
|
eventService.emit('workflow-post-execute', event);
|
||||||
|
|
||||||
|
expect(telemetry.trackWorkflowExecution).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not track when execution status is "waiting"', async () => {
|
||||||
|
const event: RelayEventMap['workflow-post-execute'] = {
|
||||||
|
workflow: mockWorkflowBase,
|
||||||
|
executionId: 'execution123',
|
||||||
|
userId: 'user123',
|
||||||
|
runData: {
|
||||||
|
status: 'waiting',
|
||||||
|
data: { resultData: {} },
|
||||||
|
} as IRun,
|
||||||
|
};
|
||||||
|
|
||||||
|
eventService.emit('workflow-post-execute', event);
|
||||||
|
|
||||||
|
expect(telemetry.trackWorkflowExecution).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should track successful workflow execution', async () => {
|
||||||
|
const runData = mock<IRun>({
|
||||||
|
finished: true,
|
||||||
|
status: 'success',
|
||||||
|
mode: 'manual',
|
||||||
|
data: { resultData: {} },
|
||||||
|
});
|
||||||
|
|
||||||
|
const event: RelayEventMap['workflow-post-execute'] = {
|
||||||
|
workflow: mockWorkflowBase,
|
||||||
|
executionId: 'execution123',
|
||||||
|
userId: 'user123',
|
||||||
|
runData: runData as unknown as IRun,
|
||||||
|
};
|
||||||
|
|
||||||
|
eventService.emit('workflow-post-execute', event);
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(telemetry.trackWorkflowExecution).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
workflow_id: 'workflow123',
|
||||||
|
user_id: 'user123',
|
||||||
|
success: true,
|
||||||
|
is_manual: true,
|
||||||
|
execution_mode: 'manual',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call telemetry.track when manual node execution finished', async () => {
|
||||||
|
sharedWorkflowRepository.findSharingRole.mockResolvedValue('workflow:editor');
|
||||||
|
|
||||||
|
const runData = {
|
||||||
|
status: 'error',
|
||||||
|
mode: 'manual',
|
||||||
|
data: {
|
||||||
|
startData: {
|
||||||
|
destinationNode: 'OpenAI',
|
||||||
|
runNodeFilter: ['OpenAI'],
|
||||||
|
},
|
||||||
|
resultData: {
|
||||||
|
runData: {},
|
||||||
|
lastNodeExecuted: 'OpenAI',
|
||||||
|
error: new NodeApiError(
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
typeVersion: 1,
|
||||||
|
name: 'Jira',
|
||||||
|
type: 'n8n-nodes-base.jira',
|
||||||
|
parameters: {},
|
||||||
|
position: [100, 200],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Error message',
|
||||||
|
description: 'Incorrect API key provided',
|
||||||
|
httpCode: '401',
|
||||||
|
stack: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Error message',
|
||||||
|
description: 'Error description',
|
||||||
|
level: 'warning',
|
||||||
|
functionality: 'regular',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as IRun;
|
||||||
|
|
||||||
|
const nodeGraph: INodesGraphResult = {
|
||||||
|
nodeGraph: { node_types: [], node_connections: [], webhookNodeNames: [] },
|
||||||
|
nameIndices: {
|
||||||
|
Jira: '1',
|
||||||
|
OpenAI: '1',
|
||||||
|
},
|
||||||
|
} as unknown as INodesGraphResult;
|
||||||
|
|
||||||
|
jest.spyOn(TelemetryHelpers, 'generateNodesGraph').mockImplementation(() => nodeGraph);
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(TelemetryHelpers, 'getNodeTypeForName')
|
||||||
|
.mockImplementation(
|
||||||
|
() => ({ type: 'n8n-nodes-base.jira', version: 1, name: 'Jira' }) as unknown as INode,
|
||||||
|
);
|
||||||
|
|
||||||
|
const event: RelayEventMap['workflow-post-execute'] = {
|
||||||
|
workflow: mockWorkflowBase,
|
||||||
|
executionId: 'execution123',
|
||||||
|
userId: 'user123',
|
||||||
|
runData,
|
||||||
|
};
|
||||||
|
|
||||||
|
eventService.emit('workflow-post-execute', event);
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(telemetry.track).toHaveBeenCalledWith(
|
||||||
|
'Manual node exec finished',
|
||||||
|
expect.objectContaining({
|
||||||
|
webhook_domain: null,
|
||||||
|
user_id: 'user123',
|
||||||
|
workflow_id: 'workflow123',
|
||||||
|
status: 'error',
|
||||||
|
executionStatus: 'error',
|
||||||
|
sharing_role: 'sharee',
|
||||||
|
error_message: 'Error message',
|
||||||
|
error_node_type: 'n8n-nodes-base.jira',
|
||||||
|
error_node_id: '1',
|
||||||
|
node_id: '1',
|
||||||
|
node_type: 'n8n-nodes-base.jira',
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph.nodeGraph),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(telemetry.trackWorkflowExecution).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
workflow_id: 'workflow123',
|
||||||
|
success: false,
|
||||||
|
is_manual: true,
|
||||||
|
execution_mode: 'manual',
|
||||||
|
version_cli: N8N_VERSION,
|
||||||
|
error_message: 'Error message',
|
||||||
|
error_node_type: 'n8n-nodes-base.jira',
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph.nodeGraph),
|
||||||
|
error_node_id: '1',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call telemetry.track when manual node execution finished with canceled error message', async () => {
|
||||||
|
sharedWorkflowRepository.findSharingRole.mockResolvedValue('workflow:owner');
|
||||||
|
|
||||||
|
const runData = {
|
||||||
|
status: 'error',
|
||||||
|
mode: 'manual',
|
||||||
|
data: {
|
||||||
|
startData: {
|
||||||
|
destinationNode: 'OpenAI',
|
||||||
|
runNodeFilter: ['OpenAI'],
|
||||||
|
},
|
||||||
|
resultData: {
|
||||||
|
runData: {},
|
||||||
|
lastNodeExecuted: 'OpenAI',
|
||||||
|
error: new NodeApiError(
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
typeVersion: 1,
|
||||||
|
name: 'Jira',
|
||||||
|
type: 'n8n-nodes-base.jira',
|
||||||
|
parameters: {},
|
||||||
|
position: [100, 200],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Error message',
|
||||||
|
description: 'Incorrect API key provided',
|
||||||
|
httpCode: '401',
|
||||||
|
stack: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Error message canceled',
|
||||||
|
description: 'Error description',
|
||||||
|
level: 'warning',
|
||||||
|
functionality: 'regular',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as IRun;
|
||||||
|
|
||||||
|
const nodeGraph: INodesGraphResult = {
|
||||||
|
nodeGraph: { node_types: [], node_connections: [] },
|
||||||
|
nameIndices: {
|
||||||
|
Jira: '1',
|
||||||
|
OpenAI: '1',
|
||||||
|
},
|
||||||
|
} as unknown as INodesGraphResult;
|
||||||
|
|
||||||
|
jest.spyOn(TelemetryHelpers, 'generateNodesGraph').mockImplementation(() => nodeGraph);
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(TelemetryHelpers, 'getNodeTypeForName')
|
||||||
|
.mockImplementation(
|
||||||
|
() => ({ type: 'n8n-nodes-base.jira', version: 1, name: 'Jira' }) as unknown as INode,
|
||||||
|
);
|
||||||
|
|
||||||
|
const event: RelayEventMap['workflow-post-execute'] = {
|
||||||
|
workflow: mockWorkflowBase,
|
||||||
|
executionId: 'execution123',
|
||||||
|
userId: 'user123',
|
||||||
|
runData,
|
||||||
|
};
|
||||||
|
|
||||||
|
eventService.emit('workflow-post-execute', event);
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(telemetry.track).toHaveBeenCalledWith(
|
||||||
|
'Manual node exec finished',
|
||||||
|
expect.objectContaining({
|
||||||
|
webhook_domain: null,
|
||||||
|
user_id: 'user123',
|
||||||
|
workflow_id: 'workflow123',
|
||||||
|
status: 'canceled',
|
||||||
|
executionStatus: 'canceled',
|
||||||
|
sharing_role: 'owner',
|
||||||
|
error_message: 'Error message canceled',
|
||||||
|
error_node_type: 'n8n-nodes-base.jira',
|
||||||
|
error_node_id: '1',
|
||||||
|
node_id: '1',
|
||||||
|
node_type: 'n8n-nodes-base.jira',
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph.nodeGraph),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(telemetry.trackWorkflowExecution).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
workflow_id: 'workflow123',
|
||||||
|
success: false,
|
||||||
|
is_manual: true,
|
||||||
|
execution_mode: 'manual',
|
||||||
|
version_cli: N8N_VERSION,
|
||||||
|
error_message: 'Error message canceled',
|
||||||
|
error_node_type: 'n8n-nodes-base.jira',
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph.nodeGraph),
|
||||||
|
error_node_id: '1',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call telemetry.track when manual workflow execution finished', async () => {
|
||||||
|
sharedWorkflowRepository.findSharingRole.mockResolvedValue('workflow:owner');
|
||||||
|
|
||||||
|
const runData = {
|
||||||
|
status: 'error',
|
||||||
|
mode: 'manual',
|
||||||
|
data: {
|
||||||
|
startData: {
|
||||||
|
runNodeFilter: ['OpenAI'],
|
||||||
|
},
|
||||||
|
resultData: {
|
||||||
|
runData: {
|
||||||
|
Jira: [
|
||||||
|
{
|
||||||
|
data: { main: [[{ json: { headers: { origin: 'https://www.test.com' } } }]] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
lastNodeExecuted: 'OpenAI',
|
||||||
|
error: new NodeApiError(
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
typeVersion: 1,
|
||||||
|
name: 'Jira',
|
||||||
|
type: 'n8n-nodes-base.jira',
|
||||||
|
parameters: {},
|
||||||
|
position: [100, 200],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Error message',
|
||||||
|
description: 'Incorrect API key provided',
|
||||||
|
httpCode: '401',
|
||||||
|
stack: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Error message',
|
||||||
|
description: 'Error description',
|
||||||
|
level: 'warning',
|
||||||
|
functionality: 'regular',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as IRun;
|
||||||
|
|
||||||
|
const nodeGraph: INodesGraphResult = {
|
||||||
|
webhookNodeNames: ['Jira'],
|
||||||
|
nodeGraph: { node_types: [], node_connections: [] },
|
||||||
|
nameIndices: {
|
||||||
|
Jira: '1',
|
||||||
|
OpenAI: '1',
|
||||||
|
},
|
||||||
|
} as unknown as INodesGraphResult;
|
||||||
|
|
||||||
|
jest.spyOn(TelemetryHelpers, 'generateNodesGraph').mockImplementation(() => nodeGraph);
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(TelemetryHelpers, 'getNodeTypeForName')
|
||||||
|
.mockImplementation(
|
||||||
|
() => ({ type: 'n8n-nodes-base.jira', version: 1, name: 'Jira' }) as unknown as INode,
|
||||||
|
);
|
||||||
|
|
||||||
|
const event: RelayEventMap['workflow-post-execute'] = {
|
||||||
|
workflow: mockWorkflowBase,
|
||||||
|
executionId: 'execution123',
|
||||||
|
userId: 'user123',
|
||||||
|
runData,
|
||||||
|
};
|
||||||
|
|
||||||
|
eventService.emit('workflow-post-execute', event);
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(telemetry.track).toHaveBeenCalledWith(
|
||||||
|
'Manual workflow exec finished',
|
||||||
|
expect.objectContaining({
|
||||||
|
webhook_domain: 'test.com',
|
||||||
|
user_id: 'user123',
|
||||||
|
workflow_id: 'workflow123',
|
||||||
|
status: 'error',
|
||||||
|
executionStatus: 'error',
|
||||||
|
sharing_role: 'owner',
|
||||||
|
error_message: 'Error message',
|
||||||
|
error_node_type: 'n8n-nodes-base.jira',
|
||||||
|
error_node_id: '1',
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph.nodeGraph),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(telemetry.trackWorkflowExecution).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
workflow_id: 'workflow123',
|
||||||
|
success: false,
|
||||||
|
is_manual: true,
|
||||||
|
execution_mode: 'manual',
|
||||||
|
version_cli: N8N_VERSION,
|
||||||
|
error_message: 'Error message',
|
||||||
|
error_node_type: 'n8n-nodes-base.jira',
|
||||||
|
node_graph_string: JSON.stringify(nodeGraph.nodeGraph),
|
||||||
|
error_node_id: '1',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue