mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
326 lines
9.2 KiB
TypeScript
326 lines
9.2 KiB
TypeScript
import { mock } from 'jest-mock-extended';
|
|
import {
|
|
NodeOperationError,
|
|
type AssignmentCollectionValue,
|
|
type IExecuteFunctions,
|
|
type INodeTypes,
|
|
type NodeParameterValueType,
|
|
} from 'n8n-workflow';
|
|
|
|
import { GoogleSheet } from '../../Google/Sheet/v2/helpers/GoogleSheet';
|
|
import { Evaluation } from '../Evaluation/Evaluation.node.ee';
|
|
|
|
describe('Test Evaluation', () => {
|
|
const sheetName = 'Sheet5';
|
|
const spreadsheetId = '1oqFpPgEPTGDw7BPkp1SfPXq3Cb3Hyr1SROtf-Ec4zvA';
|
|
|
|
const mockExecuteFunctions = mock<IExecuteFunctions>({});
|
|
|
|
beforeEach(() => {
|
|
(mockExecuteFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
|
(mockExecuteFunctions.getNode as jest.Mock).mockReturnValue({ typeVersion: 4.6 });
|
|
(mockExecuteFunctions.getParentNodes as jest.Mock).mockReturnValue([
|
|
{ type: 'n8n-nodes-base.evaluationTrigger', name: 'Evaluation' },
|
|
]);
|
|
(mockExecuteFunctions.evaluateExpression as jest.Mock).mockReturnValue({
|
|
row_number: 23,
|
|
foo: 1,
|
|
bar: 2,
|
|
_rowsLeft: 2,
|
|
});
|
|
});
|
|
|
|
afterEach(() => jest.clearAllMocks());
|
|
|
|
describe('Test Evaluation Node for Set Output', () => {
|
|
jest.spyOn(GoogleSheet.prototype, 'spreadsheetGetSheet').mockImplementation(async () => {
|
|
return { sheetId: 1, title: sheetName };
|
|
});
|
|
|
|
jest.spyOn(GoogleSheet.prototype, 'updateRows').mockImplementation(async () => {
|
|
return { sheetId: 1, title: sheetName };
|
|
});
|
|
|
|
jest.spyOn(GoogleSheet.prototype, 'batchUpdate').mockImplementation(async () => {
|
|
return { sheetId: 1, title: sheetName };
|
|
});
|
|
|
|
test('credential test for googleApi should be in methods', async () => {
|
|
const evaluationNode = new Evaluation();
|
|
expect(evaluationNode.methods.credentialTest.googleApiCredentialTest).toBeDefined();
|
|
});
|
|
|
|
test('should throw error if output values is empty', async () => {
|
|
mockExecuteFunctions.getNodeParameter.mockImplementation(
|
|
(key: string, _: number, fallbackValue?: string | number | boolean | object) => {
|
|
const mockParams: { [key: string]: unknown } = {
|
|
'outputs.values': [],
|
|
documentId: {
|
|
mode: 'id',
|
|
value: spreadsheetId,
|
|
},
|
|
sheetName,
|
|
sheetMode: 'id',
|
|
operation: 'setOutputs',
|
|
};
|
|
return (mockParams[key] ?? fallbackValue) as NodeParameterValueType;
|
|
},
|
|
);
|
|
|
|
await expect(new Evaluation().execute.call(mockExecuteFunctions)).rejects.toThrow(
|
|
'No outputs to set',
|
|
);
|
|
|
|
expect(GoogleSheet.prototype.updateRows).not.toBeCalled();
|
|
|
|
expect(GoogleSheet.prototype.batchUpdate).not.toBeCalled();
|
|
});
|
|
|
|
test('should update rows and return input data for existing headers', async () => {
|
|
mockExecuteFunctions.getNodeParameter.mockImplementation(
|
|
(key: string, _: number, fallbackValue?: string | number | boolean | object) => {
|
|
const mockParams: { [key: string]: unknown } = {
|
|
'outputs.values': [{ outputName: 'foo', outputValue: 'clam' }],
|
|
documentId: {
|
|
mode: 'id',
|
|
value: spreadsheetId,
|
|
},
|
|
sheetName,
|
|
sheetMode: 'id',
|
|
operation: 'setOutputs',
|
|
};
|
|
return (mockParams[key] ?? fallbackValue) as NodeParameterValueType;
|
|
},
|
|
);
|
|
|
|
await new Evaluation().execute.call(mockExecuteFunctions);
|
|
|
|
expect(GoogleSheet.prototype.updateRows).toHaveBeenCalledWith(
|
|
sheetName,
|
|
[['foo', 'bar']],
|
|
'RAW',
|
|
1,
|
|
);
|
|
|
|
expect(GoogleSheet.prototype.batchUpdate).toHaveBeenCalledWith(
|
|
[
|
|
{
|
|
range: 'Sheet5!A23',
|
|
values: [['clam']],
|
|
},
|
|
],
|
|
'RAW',
|
|
);
|
|
});
|
|
|
|
test('should return empty when there is no parent evaluation trigger', async () => {
|
|
mockExecuteFunctions.getNodeParameter.mockImplementation(
|
|
(key: string, _: number, fallbackValue?: string | number | boolean | object) => {
|
|
const mockParams: { [key: string]: unknown } = {
|
|
'outputs.values': [{ outputName: 'bob', outputValue: 'clam' }],
|
|
documentId: {
|
|
mode: 'id',
|
|
value: spreadsheetId,
|
|
},
|
|
sheetName,
|
|
sheetMode: 'id',
|
|
operation: 'setOutputs',
|
|
};
|
|
return (mockParams[key] ?? fallbackValue) as NodeParameterValueType;
|
|
},
|
|
);
|
|
mockExecuteFunctions.getParentNodes.mockReturnValue([]);
|
|
|
|
const result = await new Evaluation().execute.call(mockExecuteFunctions);
|
|
|
|
expect(result).toEqual([[{ json: {} }]]);
|
|
|
|
expect(GoogleSheet.prototype.updateRows).not.toBeCalled();
|
|
|
|
expect(GoogleSheet.prototype.batchUpdate).not.toBeCalled();
|
|
});
|
|
|
|
test('should update rows and return input data for new headers', async () => {
|
|
mockExecuteFunctions.getNodeParameter.mockImplementation(
|
|
(key: string, _: number, fallbackValue?: string | number | boolean | object) => {
|
|
const mockParams: { [key: string]: unknown } = {
|
|
'outputs.values': [{ outputName: 'bob', outputValue: 'clam' }],
|
|
documentId: {
|
|
mode: 'id',
|
|
value: spreadsheetId,
|
|
},
|
|
sheetName,
|
|
sheetMode: 'id',
|
|
operation: 'setOutputs',
|
|
};
|
|
return (mockParams[key] ?? fallbackValue) as NodeParameterValueType;
|
|
},
|
|
);
|
|
|
|
await new Evaluation().execute.call(mockExecuteFunctions);
|
|
|
|
expect(GoogleSheet.prototype.updateRows).toHaveBeenCalledWith(
|
|
sheetName,
|
|
[['foo', 'bar', 'bob']],
|
|
'RAW',
|
|
1,
|
|
);
|
|
|
|
expect(GoogleSheet.prototype.batchUpdate).toHaveBeenCalledWith(
|
|
[
|
|
{
|
|
range: 'Sheet5!C23',
|
|
values: [['clam']],
|
|
},
|
|
],
|
|
'RAW',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('Test Evaluation Node for Set Metrics', () => {
|
|
const nodeTypes = mock<INodeTypes>();
|
|
const evaluationMetricsNode = new Evaluation();
|
|
|
|
let mockExecuteFunction: IExecuteFunctions;
|
|
|
|
function getMockExecuteFunction(metrics: AssignmentCollectionValue['assignments']) {
|
|
return {
|
|
getInputData: jest.fn().mockReturnValue([{}]),
|
|
|
|
getNodeParameter: jest.fn((param: string, _: number) => {
|
|
if (param === 'metrics') {
|
|
return { assignments: metrics };
|
|
}
|
|
if (param === 'operation') {
|
|
return 'setMetrics';
|
|
}
|
|
return param;
|
|
}),
|
|
|
|
getNode: jest.fn().mockReturnValue({
|
|
typeVersion: 1,
|
|
}),
|
|
} as unknown as IExecuteFunctions;
|
|
}
|
|
|
|
beforeAll(() => {
|
|
mockExecuteFunction = getMockExecuteFunction([
|
|
{
|
|
id: '1',
|
|
name: 'Accuracy',
|
|
value: 0.95,
|
|
type: 'number',
|
|
},
|
|
{
|
|
id: '2',
|
|
name: 'Latency',
|
|
value: 100,
|
|
type: 'number',
|
|
},
|
|
]);
|
|
nodeTypes.getByName.mockReturnValue(evaluationMetricsNode);
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('execute', () => {
|
|
it('should output the defined metrics', async () => {
|
|
const result = await evaluationMetricsNode.execute.call(mockExecuteFunction);
|
|
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0]).toHaveLength(1);
|
|
|
|
const outputItem = result[0][0].json;
|
|
expect(outputItem).toEqual({
|
|
Accuracy: 0.95,
|
|
Latency: 100,
|
|
});
|
|
});
|
|
|
|
it('should handle no metrics defined', async () => {
|
|
mockExecuteFunction = getMockExecuteFunction([]);
|
|
const result = await evaluationMetricsNode.execute.call(mockExecuteFunction);
|
|
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(result[0][0].json).toEqual({});
|
|
});
|
|
|
|
it('should convert string values to numbers', async () => {
|
|
const mockExecuteWithStringValues = getMockExecuteFunction([
|
|
{
|
|
id: '1',
|
|
name: 'Accuracy',
|
|
value: '0.95',
|
|
type: 'number',
|
|
},
|
|
{
|
|
id: '2',
|
|
name: 'Latency',
|
|
value: '100',
|
|
type: 'number',
|
|
},
|
|
]);
|
|
|
|
const result = await evaluationMetricsNode.execute.call(mockExecuteWithStringValues);
|
|
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0]).toHaveLength(1);
|
|
|
|
const outputItem = result[0][0].json;
|
|
expect(outputItem).toEqual({
|
|
Accuracy: 0.95,
|
|
Latency: 100,
|
|
});
|
|
});
|
|
|
|
it('should throw error for non-numeric string values', async () => {
|
|
const mockExecuteWithInvalidValue = getMockExecuteFunction([
|
|
{
|
|
id: '1',
|
|
name: 'Accuracy',
|
|
value: 'not-a-number',
|
|
type: 'number',
|
|
},
|
|
]);
|
|
|
|
await expect(
|
|
evaluationMetricsNode.execute.call(mockExecuteWithInvalidValue),
|
|
).rejects.toThrow(NodeOperationError);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Test Evaluation Node for Check If Evaluating', () => {
|
|
beforeEach(() => {
|
|
(mockExecuteFunctions.getInputData as jest.Mock).mockReturnValue([{ json: {} }]);
|
|
(mockExecuteFunctions.getNode as jest.Mock).mockReturnValue({ typeVersion: 4.6 });
|
|
mockExecuteFunctions.getNodeParameter.mockImplementation(
|
|
(key: string, _: number, fallbackValue?: string | number | boolean | object) => {
|
|
const mockParams: { [key: string]: unknown } = {
|
|
operation: 'checkIfEvaluating',
|
|
};
|
|
return (mockParams[key] ?? fallbackValue) as NodeParameterValueType;
|
|
},
|
|
);
|
|
});
|
|
|
|
afterEach(() => jest.clearAllMocks());
|
|
|
|
test('should return output in normal branch if normal execution', async () => {
|
|
(mockExecuteFunctions.getParentNodes as jest.Mock).mockReturnValue([]);
|
|
const result = await new Evaluation().execute.call(mockExecuteFunctions);
|
|
expect(result).toEqual([[], [{ json: {} }]]);
|
|
});
|
|
|
|
test('should return output in evaluation branch if evaluation execution', async () => {
|
|
(mockExecuteFunctions.getParentNodes as jest.Mock).mockReturnValue([
|
|
{ type: 'n8n-nodes-base.evaluationTrigger', name: 'Evaluation' },
|
|
]);
|
|
|
|
const result = await new Evaluation().execute.call(mockExecuteFunctions);
|
|
expect(result).toEqual([[{ json: {} }], []]);
|
|
});
|
|
});
|
|
});
|