2024-09-04 03:06:17 -07:00
|
|
|
import type { IExecuteFunctions, INodeParameters, INodeType } from 'n8n-workflow';
|
|
|
|
import { NodeConnectionType } from 'n8n-workflow';
|
|
|
|
import { z } from 'zod';
|
|
|
|
|
2024-09-30 06:38:56 -07:00
|
|
|
import { createNodeAsTool } from '@/CreateNodeAsTool';
|
|
|
|
|
2024-09-04 03:06:17 -07:00
|
|
|
jest.mock('@langchain/core/tools', () => ({
|
|
|
|
DynamicStructuredTool: jest.fn().mockImplementation((config) => ({
|
|
|
|
name: config.name,
|
|
|
|
description: config.description,
|
|
|
|
schema: config.schema,
|
|
|
|
func: config.func,
|
|
|
|
})),
|
|
|
|
}));
|
|
|
|
|
|
|
|
describe('createNodeAsTool', () => {
|
|
|
|
let mockCtx: IExecuteFunctions;
|
|
|
|
let mockNode: INodeType;
|
|
|
|
let mockNodeParameters: INodeParameters;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
mockCtx = {
|
|
|
|
getNodeParameter: jest.fn(),
|
|
|
|
addInputData: jest.fn(),
|
|
|
|
addOutputData: jest.fn(),
|
|
|
|
} as unknown as IExecuteFunctions;
|
|
|
|
|
|
|
|
mockNode = {
|
|
|
|
description: {
|
|
|
|
name: 'TestNode',
|
|
|
|
description: 'Test node description',
|
|
|
|
},
|
|
|
|
execute: jest.fn().mockResolvedValue([[{ json: { result: 'test' } }]]),
|
|
|
|
} as unknown as INodeType;
|
|
|
|
|
|
|
|
mockNodeParameters = {
|
|
|
|
param1: "{{ '__PLACEHOLDER: Test parameter' }}",
|
|
|
|
param2: 'static value',
|
|
|
|
nestedParam: {
|
|
|
|
subParam: "{{ '__PLACEHOLDER: Nested parameter' }}",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
jest.clearAllMocks();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create a DynamicStructuredTool with correct properties', () => {
|
|
|
|
const tool = createNodeAsTool(mockNode, mockCtx, mockNodeParameters);
|
|
|
|
|
|
|
|
expect(tool).toBeDefined();
|
|
|
|
expect(tool.name).toBe('TestNode');
|
|
|
|
expect(tool.description).toBe('Test node description');
|
|
|
|
expect(tool.schema).toBeDefined();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should use toolDescription if provided', () => {
|
|
|
|
const customDescription = 'Custom tool description';
|
|
|
|
(mockCtx.getNodeParameter as jest.Mock).mockReturnValue(customDescription);
|
|
|
|
|
|
|
|
const tool = createNodeAsTool(mockNode, mockCtx, mockNodeParameters);
|
|
|
|
|
|
|
|
expect(tool.description).toBe(customDescription);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create a schema based on placeholder values in nodeParameters', () => {
|
|
|
|
const tool = createNodeAsTool(mockNode, mockCtx, mockNodeParameters);
|
|
|
|
|
|
|
|
expect(tool.schema).toBeDefined();
|
|
|
|
expect(tool.schema.shape).toHaveProperty('param1');
|
|
|
|
expect(tool.schema.shape).toHaveProperty('nestedParam__subParam');
|
|
|
|
expect(tool.schema.shape).not.toHaveProperty('param2');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle nested parameters correctly', () => {
|
|
|
|
const tool = createNodeAsTool(mockNode, mockCtx, mockNodeParameters);
|
|
|
|
|
|
|
|
expect(tool.schema.shape.nestedParam__subParam).toBeInstanceOf(z.ZodString);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create a function that wraps the node execution', async () => {
|
|
|
|
const tool = createNodeAsTool(mockNode, mockCtx, mockNodeParameters);
|
|
|
|
|
|
|
|
const result = await tool.func({ param1: 'test value', nestedParam__subParam: 'nested value' });
|
|
|
|
|
|
|
|
expect(mockCtx.addInputData).toHaveBeenCalledWith(NodeConnectionType.AiTool, [
|
|
|
|
[{ json: { param1: 'test value', nestedParam__subParam: 'nested value' } }],
|
|
|
|
]);
|
|
|
|
expect(mockNode.execute).toHaveBeenCalled();
|
|
|
|
expect(mockCtx.addOutputData).toHaveBeenCalledWith(NodeConnectionType.AiTool, 0, [
|
|
|
|
[{ json: { response: [{ result: 'test' }] } }],
|
|
|
|
]);
|
|
|
|
expect(result).toBe(JSON.stringify([{ result: 'test' }]));
|
|
|
|
});
|
|
|
|
});
|