UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

257 lines (222 loc) 8.02 kB
import { Mock, beforeEach, describe, expect, it, vi } from 'vitest'; import { edgeClient } from '@/libs/trpc/client'; import { globalHelpers } from '@/store/global/helpers'; import { toolService } from '../tool'; import openAPIV3 from './openai/OpenAPI_V3.json'; import OpenAIPlugin from './openai/plugin.json'; // Mocking modules and functions vi.mock('@/store/global/helpers', () => ({ globalHelpers: { getCurrentLanguage: vi.fn(), }, })); vi.mock('@/libs/trpc/client', () => ({ edgeClient: { market: { getPluginIndex: { query: vi.fn(), }, }, }, })); beforeEach(() => { vi.resetAllMocks(); }); describe('ToolService', () => { describe('getToolList', () => { it('should fetch and return the plugin list', async () => { // Arrange const fakeResponse = { plugins: [{ name: 'TestPlugin' }] }; (globalHelpers.getCurrentLanguage as Mock).mockReturnValue('tt'); (edgeClient.market.getPluginIndex.query as Mock).mockResolvedValue(fakeResponse); // Act const pluginList = await toolService.getToolList(); // Assert expect(globalHelpers.getCurrentLanguage).toHaveBeenCalled(); expect(pluginList).toEqual(fakeResponse.plugins); }); it('should handle fetch error', async () => { // Arrange (globalHelpers.getCurrentLanguage as Mock).mockReturnValue('en'); (edgeClient.market.getPluginIndex.query as Mock).mockRejectedValue( new Error('Network error'), ); // Act & Assert await expect(toolService.getToolList()).rejects.toThrow('Network error'); }); }); describe('getToolManifest', () => { it('should return manifest', async () => { const manifestUrl = 'http://fake-url.com/manifest.json'; const fakeManifest = { $schema: '../node_modules/@lobehub/chat-plugin-sdk/schema.json', api: [ { url: 'https://realtime-weather.chat-plugin.lobehub.com/api/v1', name: 'fetchCurrentWeather', description: '获取当前天气情况', parameters: { properties: { city: { description: '城市名称', type: 'string', }, }, required: ['city'], type: 'object', }, }, ], author: 'LobeHub', createAt: '2023-08-12', homepage: 'https://github.com/lobehub/chat-plugin-realtime-weather', identifier: 'realtime-weather', meta: { avatar: '🌈', tags: ['weather', 'realtime'], title: 'Realtime Weather', description: 'Get realtime weather information', }, ui: { url: 'https://realtime-weather.chat-plugin.lobehub.com/iframe', height: 310, }, version: '1', }; global.fetch = vi.fn(() => Promise.resolve({ headers: new Headers({ 'content-type': 'application/json' }), ok: true, json: () => Promise.resolve(fakeManifest), }), ) as any; const manifest = await toolService.getToolManifest(manifestUrl); expect(fetch).toHaveBeenCalledWith(manifestUrl); expect(manifest).toEqual(fakeManifest); }); it('should return error on noManifest', async () => { try { await toolService.getToolManifest(); } catch (e) { expect(e).toEqual(new TypeError('noManifest')); } }); it('should return error on manifestInvalid', async () => { const fakeManifest = { name: 'TestPlugin', version: '1.0.0' }; const manifestUrl = 'http://fake-url.com/manifest.json'; global.fetch = vi.fn(() => Promise.resolve({ headers: new Headers({ 'content-type': 'application/json' }), ok: true, json: () => Promise.resolve(fakeManifest), }), ) as any; try { await toolService.getToolManifest(manifestUrl); } catch (e) { expect(e).toEqual(new TypeError('manifestInvalid')); } }); it('should return error on fetchError', async () => { const manifestUrl = 'http://fake-url.com/manifest.json'; global.fetch = vi.fn(() => Promise.reject(new Error('Network error'))); try { await toolService.getToolManifest(manifestUrl); } catch (e) { expect(e).toEqual(new TypeError('fetchError')); } expect(fetch).toHaveBeenCalledWith(manifestUrl); }); it('should return error on manifestInvalid', async () => { const fakeManifest = { name: 'TestPlugin', version: '1.0.0' }; const manifestUrl = 'http://fake-url.com/manifest.json'; global.fetch = vi.fn(() => Promise.resolve({ ok: false, headers: new Headers({ 'content-type': 'application/json' }), json: () => Promise.resolve(fakeManifest), }), ) as any; try { await toolService.getToolManifest(manifestUrl); } catch (e) { expect(e).toEqual(new TypeError('fetchError')); } }); describe('support OpenAPI manifest', () => { it('should get plugin manifest', async () => { const manifestUrl = 'http://fake-url.com/manifest.json'; const openapiUrl = 'http://fake-url.com/openapiUrl.json'; const fakeManifest = { $schema: '../node_modules/@lobehub/chat-plugin-sdk/schema.json', api: [], openapi: openapiUrl, author: 'LobeHub', createAt: '2023-08-12', homepage: 'https://github.com/lobehub/chat-plugin-realtime-weather', identifier: 'realtime-weather', meta: { avatar: '🌈', tags: ['weather', 'realtime'], title: 'Realtime Weather', description: 'Get realtime weather information', }, ui: { url: 'https://realtime-weather.chat-plugin.lobehub.com/iframe', height: 310, }, version: '1', }; global.fetch = vi.fn((url) => Promise.resolve({ ok: true, headers: new Headers({ 'content-type': 'application/json' }), json: () => Promise.resolve(url === openapiUrl ? openAPIV3 : fakeManifest), }), ) as any; const manifest = await toolService.getToolManifest(manifestUrl); expect(manifest).toMatchSnapshot(); }); it('should return error on openAPIInvalid', async () => { const openapiUrl = 'http://fake-url.com/openapiUrl.json'; const manifestUrl = 'http://fake-url.com/manifest.json'; const fakeManifest = { $schema: '../node_modules/@lobehub/chat-plugin-sdk/schema.json', api: [], openapi: openapiUrl, author: 'LobeHub', createAt: '2023-08-12', homepage: 'https://github.com/lobehub/chat-plugin-realtime-weather', identifier: 'realtime-weather', meta: { avatar: '🌈', tags: ['weather', 'realtime'], title: 'Realtime Weather', description: 'Get realtime weather information', }, ui: { url: 'https://realtime-weather.chat-plugin.lobehub.com/iframe', height: 310, }, version: '1', }; global.fetch = vi.fn((url) => Promise.resolve({ ok: true, headers: new Headers({ 'content-type': 'application/json' }), json: () => Promise.resolve(url === openapiUrl ? [] : fakeManifest), }), ) as any; try { await toolService.getToolManifest(manifestUrl); } catch (e) { expect(e).toEqual(new TypeError('openAPIInvalid')); } }); }); }); it('can parse the OpenAI plugin', async () => { const manifest = toolService['convertOpenAIManifestToLobeManifest'](OpenAIPlugin as any); expect(manifest).toMatchSnapshot(); }); });