mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -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 { mock } from 'jest-mock-extended';
|
||||
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 type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||
|
@ -28,6 +29,9 @@ describe('TelemetryEventRelay', () => {
|
|||
mode: 'smtp',
|
||||
},
|
||||
},
|
||||
diagnostics: {
|
||||
enabled: true,
|
||||
},
|
||||
endpoints: {
|
||||
metrics: {
|
||||
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