From 27614e9e8bdebb24d7f82d6d3a85b5bcdb3bfd97 Mon Sep 17 00:00:00 2001 From: "gru-agent[bot]" <185149714+gru-agent[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:26:05 +0800 Subject: [PATCH] Add unit tests for toolList API functions and update the export statements in toolList.ts. (#4623) Co-authored-by: gru-agent[bot] <185149714+gru-agent[bot]@users.noreply.github.com> --- .../pages/api/support/mcp/server/toolList.ts | 2 + .../api/support/mcp/server/toolList.test.ts | 208 ++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 test/cases/pages/api/support/mcp/server/toolList.test.ts diff --git a/projects/app/src/pages/api/support/mcp/server/toolList.ts b/projects/app/src/pages/api/support/mcp/server/toolList.ts index fbbb186b2..31187a6d4 100644 --- a/projects/app/src/pages/api/support/mcp/server/toolList.ts +++ b/projects/app/src/pages/api/support/mcp/server/toolList.ts @@ -150,3 +150,5 @@ async function handler( } export default NextAPI(handler); + +export { pluginNodes2InputSchema, workflow2InputSchema, handler }; diff --git a/test/cases/pages/api/support/mcp/server/toolList.test.ts b/test/cases/pages/api/support/mcp/server/toolList.test.ts new file mode 100644 index 000000000..7f71ad46d --- /dev/null +++ b/test/cases/pages/api/support/mcp/server/toolList.test.ts @@ -0,0 +1,208 @@ +import { describe, it, expect, vi } from 'vitest'; +import { + pluginNodes2InputSchema, + workflow2InputSchema, + handler +} from '@/pages/api/support/mcp/server/toolList'; +import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; +import { MongoMcpKey } from '@fastgpt/service/support/mcp/schema'; +import { MongoApp } from '@fastgpt/service/core/app/schema'; +import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller'; +import { authAppByTmbId } from '@fastgpt/service/support/permission/app/auth'; +import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; + +vi.mock('@fastgpt/service/support/mcp/schema', () => ({ + MongoMcpKey: { + findOne: vi.fn().mockReturnValue({ + lean: vi.fn() + }) + } +})); + +vi.mock('@fastgpt/service/core/app/schema', () => ({ + MongoApp: { + find: vi.fn().mockReturnValue({ + lean: vi.fn() + }) + } +})); + +vi.mock('@fastgpt/service/core/app/version/controller', () => ({ + getAppLatestVersion: vi.fn() +})); + +vi.mock('@fastgpt/service/support/permission/app/auth', () => ({ + authAppByTmbId: vi.fn() +})); + +describe('toolList', () => { + describe('pluginNodes2InputSchema', () => { + it('should generate input schema for plugin nodes', () => { + const nodes = [ + { + flowNodeType: FlowNodeTypeEnum.pluginInput, + inputs: [ + { + key: 'test', + valueType: 'string', + description: 'test desc', + required: true + } + ] + } + ]; + + const schema = pluginNodes2InputSchema(nodes); + + expect(schema).toEqual({ + type: 'object', + properties: { + test: { + type: 'string', + description: 'test desc' + } + }, + required: ['test'] + }); + }); + }); + + describe('workflow2InputSchema', () => { + it('should generate input schema with file config', () => { + const chatConfig = { + fileSelectConfig: { + canSelectFile: true + }, + variables: [] + }; + + const schema = workflow2InputSchema(chatConfig); + + expect(schema).toEqual({ + type: 'object', + properties: { + question: { + type: 'string', + description: 'Question from user' + }, + fileUrlList: { + type: 'array', + items: { + type: 'string' + }, + description: 'File linkage' + } + }, + required: ['question'] + }); + }); + + it('should generate input schema with variables', () => { + const chatConfig = { + variables: [ + { + key: 'var1', + valueType: 'string', + description: 'test var', + required: true + } + ] + }; + + const schema = workflow2InputSchema(chatConfig); + + expect(schema).toEqual({ + type: 'object', + properties: { + question: { + type: 'string', + description: 'Question from user' + }, + var1: { + type: 'string', + description: 'test var' + } + }, + required: ['question', 'var1'] + }); + }); + }); + + describe('handler', () => { + it('should return tools list', async () => { + const mockMcp = { + tmbId: 'test-tmb', + apps: [ + { + appId: 'app1', + toolName: 'tool1', + toolAlias: 'alias1', + description: 'desc1' + } + ] + }; + + const mockApp = { + _id: 'app1', + name: 'app1' + }; + + const mockVersion = { + nodes: [], + chatConfig: {} + }; + + vi.mocked(MongoMcpKey.findOne).mockReturnValue({ + lean: () => Promise.resolve(mockMcp) + }); + + vi.mocked(MongoApp.find).mockReturnValue({ + lean: () => Promise.resolve([mockApp]) + }); + + vi.mocked(authAppByTmbId).mockResolvedValue(undefined); + vi.mocked(getAppLatestVersion).mockResolvedValue(mockVersion); + + const result = await handler( + { + query: { key: 'test-key' }, + body: {} + }, + {} as any + ); + + expect(result).toEqual([ + { + name: 'alias1', + description: 'desc1', + inputSchema: { + type: 'object', + properties: { + question: { + type: 'string', + description: 'Question from user' + } + }, + required: ['question'] + } + } + ]); + }); + + it('should throw error if mcp key not found', async () => { + vi.mocked(MongoMcpKey.findOne).mockReturnValue({ + lean: () => Promise.resolve(null) + }); + + await expect( + handler( + { + query: { key: 'invalid-key' }, + body: {} + }, + {} as any + ) + ).rejects.toBe(CommonErrEnum.invalidResource); + }); + }); +});