@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.
204 lines (177 loc) • 6.67 kB
text/typescript
import { describe, expect, it } from 'vitest';
import { McpSchema } from '../../types/protocol';
import { generateRFCProtocolUrl, parseProtocolUrl } from '../protocol';
describe('Protocol', () => {
describe('generateRFCProtocolUrl', () => {
it('should generate valid RFC protocol URL for stdio type', () => {
const schema: McpSchema = {
identifier: 'edgeone-mcp',
name: 'EdgeOne MCP',
author: 'Higress Team',
description: 'EdgeOne API integration for LobeChat',
version: '1.0.0',
homepage: 'https://github.com/higress/edgeone-mcp',
config: {
type: 'stdio',
command: 'npx',
args: ['-y', '@higress/edgeone-mcp'],
env: { NODE_ENV: 'production' },
},
};
const url = generateRFCProtocolUrl({
id: 'edgeone-mcp',
schema,
marketId: 'higress',
});
expect(url).toMatch(/^lobehub:\/\/plugin\/install\?/);
expect(url).toContain('id=edgeone-mcp');
expect(url).toContain('marketId=higress');
// Verify schema is URL encoded
const urlObj = new URL(url);
const schemaParam = urlObj.searchParams.get('schema');
expect(schemaParam).toBeTruthy();
// URLSearchParams.get() 自动解码,所以这里得到的是解码后的JSON
expect(schemaParam).toContain('"'); // 解码后的引号
});
it('should generate valid RFC protocol URL for http type', () => {
const schema: McpSchema = {
identifier: 'awesome-api',
name: 'Awesome API',
author: 'Smithery',
description: 'Awesome API integration',
version: '2.0.0',
config: {
type: 'http',
url: 'https://api.smithery.ai/v1/mcp',
headers: {
'Authorization': 'Bearer token123',
'X-Custom-Header': 'value',
},
},
};
const url = generateRFCProtocolUrl({
id: 'awesome-api',
schema,
marketId: 'smithery',
});
expect(url).toMatch(/^lobehub:\/\/plugin\/install\?/);
expect(url).toContain('id=awesome-api');
expect(url).toContain('marketId=smithery');
});
it('should throw error if schema identifier does not match id', () => {
const schema: McpSchema = {
identifier: 'wrong-id',
name: 'Test',
author: 'Test',
description: 'Test',
version: '1.0.0',
config: { type: 'stdio', command: 'test' },
};
expect(() => generateRFCProtocolUrl({ id: 'different-id', schema })).toThrowError(
'Schema identifier must match the id parameter',
);
});
});
describe('parseProtocolUrl', () => {
it('should parse RFC protocol URL correctly', () => {
const schema: McpSchema = {
identifier: 'test-mcp',
name: 'Test MCP',
author: 'Test Author',
description: 'Test Description',
version: '1.0.0',
config: {
type: 'stdio',
command: 'test',
args: ['arg1', 'arg2'],
},
};
const url = generateRFCProtocolUrl({
id: 'test-mcp',
schema,
marketId: 'lobehub',
});
const parsed = parseProtocolUrl(url);
expect(parsed).toBeTruthy();
expect(parsed?.urlType).toBe('plugin');
expect(parsed?.action).toBe('install');
expect(parsed?.params.type).toBe('mcp');
expect(parsed?.params.id).toBe('test-mcp');
expect(parsed?.params.marketId).toBe('lobehub');
expect(parsed?.originalUrl).toBe(url);
// 验证 schema 可以被解析
const parsedSchema = JSON.parse(parsed?.params.schema || '{}');
expect(parsedSchema).toEqual(schema);
});
it('should return null for invalid protocol', () => {
const result = parseProtocolUrl('http://example.com');
expect(result).toBeNull();
});
it('should parse URLs with any action', () => {
const result = parseProtocolUrl('lobehub://plugin/configure?id=test');
expect(result).toBeTruthy();
expect(result?.urlType).toBe('plugin');
expect(result?.action).toBe('configure');
expect(result?.params.id).toBe('test');
});
it('should parse URLs with any query parameters', () => {
const result = parseProtocolUrl('lobehub://plugin/install?custom=value&another=param');
expect(result).toBeTruthy();
expect(result?.urlType).toBe('plugin');
expect(result?.action).toBe('install');
expect(result?.params.custom).toBe('value');
expect(result?.params.another).toBe('param');
});
it('should handle URLs without query parameters', () => {
const result = parseProtocolUrl('lobehub://plugin/install');
expect(result).toBeTruthy();
expect(result?.urlType).toBe('plugin');
expect(result?.action).toBe('install');
expect(Object.keys(result?.params || {})).toHaveLength(0);
});
it('should return null for URLs without action', () => {
const result = parseProtocolUrl('lobehub://plugin/');
expect(result).toBeNull();
});
});
describe('URL encoding/decoding', () => {
it('should handle special characters correctly', () => {
const schema: McpSchema = {
identifier: 'special-chars',
name: '特殊字符 ñ 🚀',
author: 'Test <test@example.com>',
description: 'Description with "quotes" and \'apostrophes\'',
version: '1.0.0',
config: {
type: 'stdio',
command: 'cmd',
args: ['arg with spaces', 'arg/with/slashes'],
},
};
const url = generateRFCProtocolUrl({ id: 'special-chars', schema });
const parsed = parseProtocolUrl(url);
expect(parsed).toBeTruthy();
expect(parsed?.params.id).toBe('special-chars');
expect(parsed?.params.type).toBe('mcp');
// 验证 schema 可以正确解析
const parsedSchema = JSON.parse(parsed?.params.schema || '{}');
expect(parsedSchema).toEqual(schema);
});
it('should handle different protocol schemes', () => {
const testCases = [
'lobehub://plugin/install?test=value',
'lobehub-dev://plugin/install?test=value',
'lobehub-beta://plugin/install?test=value',
'lobehub-nightly://plugin/install?test=value',
];
testCases.forEach((url) => {
const parsed = parseProtocolUrl(url);
expect(parsed).toBeTruthy();
expect(parsed?.urlType).toBe('plugin');
expect(parsed?.action).toBe('install');
expect(parsed?.params.test).toBe('value');
expect(parsed?.originalUrl).toBe(url);
});
});
});
});