@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.
198 lines (156 loc) • 6.08 kB
text/typescript
import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
import { act, renderHook } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { pluginService } from '@/services/plugin';
import { DiscoverPluginItem } from '@/types/discover';
import { LobeTool } from '@/types/tool';
import { merge } from '@/utils/merge';
import { useToolStore } from '../../store';
vi.mock('@/services/plugin', () => ({
pluginService: {
updatePluginSettings: vi.fn(),
removeAllPlugins: vi.fn(),
},
}));
beforeEach(() => {
// Reset all mocks before each test
vi.resetAllMocks();
});
describe('useToolStore:plugin', () => {
describe('checkPluginsIsInstalled', () => {
it('should not perform any operations if the plugin list is empty', async () => {
const installPluginsMock = vi.fn();
useToolStore.setState({
loadPluginStore: vi.fn(),
installPlugins: installPluginsMock,
});
const { result } = renderHook(() => useToolStore());
await act(async () => {
await result.current.checkPluginsIsInstalled([]);
});
expect(installPluginsMock).not.toHaveBeenCalled();
});
it('should load the plugin store and install plugins if necessary', async () => {
const plugins = ['plugin1', 'plugin2'];
const loadPluginStoreMock = vi.fn();
const installPluginsMock = vi.fn();
useToolStore.setState({
loadPluginStore: loadPluginStoreMock,
installPlugins: installPluginsMock,
});
const { result } = renderHook(() => useToolStore());
await act(async () => {
await result.current.checkPluginsIsInstalled(plugins);
});
expect(loadPluginStoreMock).toHaveBeenCalled();
expect(installPluginsMock).toHaveBeenCalledWith(plugins);
});
it('should not load the plugin store and install plugins', async () => {
const plugins = ['plugin1', 'plugin2'];
const loadPluginStoreMock = vi.fn();
const installPluginsMock = vi.fn();
useToolStore.setState({
loadPluginStore: loadPluginStoreMock,
installPlugins: installPluginsMock,
installedPlugins: [{ identifier: 'abc' }] as LobeTool[],
oldPluginItems: [{ identifier: 'abc' }] as DiscoverPluginItem[],
});
const { result } = renderHook(() => useToolStore());
await act(async () => {
await result.current.checkPluginsIsInstalled(plugins);
});
expect(loadPluginStoreMock).not.toHaveBeenCalled();
expect(installPluginsMock).toHaveBeenCalledWith(plugins);
});
});
describe('updatePluginSettings', () => {
it('should update settings for a given plugin', async () => {
const pluginId = 'test-plugin';
const newSettings = { setting1: 'new-value' };
const { result } = renderHook(() => useToolStore());
await act(async () => {
await result.current.updatePluginSettings(pluginId, newSettings);
});
expect(pluginService.updatePluginSettings).toBeCalledWith(
pluginId,
newSettings,
expect.any(AbortSignal),
);
});
it('should merge settings for a plugin with existing settings', async () => {
const pluginId = 'test-plugin';
const existingSettings = { setting1: 'old-value', setting2: 'old-value' };
const newSettings = { setting1: 'new-value' };
const mergedSettings = merge(existingSettings, newSettings);
useToolStore.setState({
installedPlugins: [{ identifier: pluginId, settings: existingSettings }] as LobeTool[],
});
const { result } = renderHook(() => useToolStore());
await act(async () => {
await result.current.updatePluginSettings(pluginId, newSettings);
});
expect(pluginService.updatePluginSettings).toBeCalledWith(
pluginId,
mergedSettings,
expect.any(AbortSignal),
);
});
});
describe('removeAllPlugins', () => {
it('should reset all plugin settings', () => {
const { result } = renderHook(() => useToolStore());
act(() => {
result.current.removeAllPlugins();
});
expect(pluginService.removeAllPlugins).toBeCalled();
});
});
describe('validatePluginSettings', () => {
// 模拟插件数据
const testPluginId = 'test-plugin';
// 定义测试用的 schema 和模拟的验证结果
const testSchema = {
properties: { abc: { type: 'string' } },
required: ['abc'],
type: 'object',
};
const testPluginSettings = { abc: 'valid-string' };
const testPlugin = {
type: 'plugin',
identifier: testPluginId,
manifest: {
identifier: testPluginId,
settings: testSchema,
},
settings: testPluginSettings,
} as unknown as LobeTool;
it('should validate settings against the schema and return valid result', async () => {
const { result } = renderHook(() => useToolStore());
act(() => {
useToolStore.setState({
installedPlugins: [testPlugin],
});
});
const validationResult = await result.current.validatePluginSettings(testPluginId);
expect(validationResult).toEqual({ valid: true, errors: [] });
});
it('should return invalid result if settings do not match the schema', async () => {
const { result } = renderHook(() => useToolStore());
act(() => {
useToolStore.setState({
installedPlugins: [{ ...testPlugin, settings: {} }] as any,
});
});
const validationResult = await result.current.validatePluginSettings(testPluginId);
expect(validationResult).toMatchSnapshot();
});
it('should return undefined if manifest or settings are not found', async () => {
useToolStore.setState({
installedPlugins: [],
});
const { result } = renderHook(() => useToolStore());
const validationResult = await result.current.validatePluginSettings(testPluginId);
expect(validationResult).toBeUndefined();
});
});
});