mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-19 13:09:39 +08:00
- MCP HTTP 支持 schema-only 模式,远程配置默认不暴露 execute_sql - OpenClaw/Hermans 向导补充安全边界与结构模式命令 - 拆分 AI 面板错误边界和 Linux CJK 字体提示组件
164 lines
6.5 KiB
TypeScript
164 lines
6.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import type { AIMCPClientInstallStatus } from '../types';
|
|
import {
|
|
buildRemoteMCPClientGuide,
|
|
buildRemoteMCPClientQuickStart,
|
|
EMPTY_MCP_CLIENT_STATUSES,
|
|
formatMCPLaunchCommand,
|
|
isRemoteMCPClientStatus,
|
|
normalizeMCPClientStatuses,
|
|
pickPreferredMCPClient,
|
|
} from './mcpClientInstallStatus';
|
|
|
|
describe('mcpClientInstallStatus helpers', () => {
|
|
it('fills missing clients with default placeholder statuses', () => {
|
|
const statuses = normalizeMCPClientStatuses([
|
|
{
|
|
client: 'codex',
|
|
displayName: 'Codex',
|
|
installed: true,
|
|
matchesCurrent: true,
|
|
message: '已检测到 Codex 用户级 GoNavi MCP 配置,且与当前 GoNavi 安装路径一致',
|
|
},
|
|
]);
|
|
|
|
expect(statuses).toEqual([
|
|
EMPTY_MCP_CLIENT_STATUSES[0],
|
|
{
|
|
client: 'codex',
|
|
displayName: 'Codex',
|
|
installMode: 'auto',
|
|
installed: true,
|
|
matchesCurrent: true,
|
|
clientDetected: false,
|
|
clientCommand: 'codex',
|
|
clientPath: '',
|
|
message: '已检测到 Codex 用户级 GoNavi MCP 配置,且与当前 GoNavi 安装路径一致',
|
|
args: [],
|
|
},
|
|
EMPTY_MCP_CLIENT_STATUSES[2],
|
|
EMPTY_MCP_CLIENT_STATUSES[3],
|
|
]);
|
|
});
|
|
|
|
it('prefers an already-installed but outdated client over a completely uninstalled one', () => {
|
|
const statuses: AIMCPClientInstallStatus[] = [
|
|
{
|
|
client: 'claude-code',
|
|
displayName: 'Claude Code',
|
|
installed: false,
|
|
matchesCurrent: false,
|
|
message: '未检测到 Claude Code 用户级 GoNavi MCP 配置',
|
|
},
|
|
{
|
|
client: 'codex',
|
|
displayName: 'Codex',
|
|
installed: true,
|
|
matchesCurrent: false,
|
|
message: '已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新',
|
|
},
|
|
];
|
|
|
|
expect(pickPreferredMCPClient(statuses)).toBe('codex');
|
|
});
|
|
|
|
it('prefers a locally detected client command when neither client has existing GoNavi MCP config', () => {
|
|
const statuses: AIMCPClientInstallStatus[] = [
|
|
{
|
|
client: 'claude-code',
|
|
displayName: 'Claude Code',
|
|
installed: false,
|
|
matchesCurrent: false,
|
|
clientDetected: false,
|
|
clientCommand: 'claude',
|
|
message: '未检测到 Claude Code 用户级 GoNavi MCP 配置',
|
|
},
|
|
{
|
|
client: 'codex',
|
|
displayName: 'Codex',
|
|
installed: false,
|
|
matchesCurrent: false,
|
|
clientDetected: true,
|
|
clientCommand: 'codex',
|
|
clientPath: 'C:/Users/mock/AppData/Roaming/npm/codex.cmd',
|
|
message: '未检测到 Codex 用户级 GoNavi MCP 配置',
|
|
},
|
|
];
|
|
|
|
expect(pickPreferredMCPClient(statuses)).toBe('codex');
|
|
});
|
|
|
|
it('prefers a client that already matches current GoNavi over another client with a stale config', () => {
|
|
const statuses: AIMCPClientInstallStatus[] = [
|
|
{
|
|
client: 'claude-code',
|
|
displayName: 'Claude Code',
|
|
installed: true,
|
|
matchesCurrent: true,
|
|
clientDetected: true,
|
|
clientCommand: 'claude',
|
|
message: '已检测到 Claude Code 用户级 GoNavi MCP 配置,且与当前 GoNavi 安装路径一致',
|
|
},
|
|
{
|
|
client: 'codex',
|
|
displayName: 'Codex',
|
|
installed: true,
|
|
matchesCurrent: false,
|
|
clientDetected: true,
|
|
clientCommand: 'codex',
|
|
message: '已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新',
|
|
},
|
|
];
|
|
|
|
expect(pickPreferredMCPClient(statuses)).toBe('claude-code');
|
|
});
|
|
|
|
it('keeps the user-selected client when it is still present in the latest status list', () => {
|
|
expect(pickPreferredMCPClient(EMPTY_MCP_CLIENT_STATUSES, 'codex')).toBe('codex');
|
|
expect(pickPreferredMCPClient(EMPTY_MCP_CLIENT_STATUSES, 'openclaw')).toBe('openclaw');
|
|
});
|
|
|
|
it('formats quoted launch commands for display and clipboard use', () => {
|
|
expect(formatMCPLaunchCommand({
|
|
command: 'C:/Program Files/GoNavi/GoNavi.exe',
|
|
args: ['mcp-server', '--stdio'],
|
|
})).toBe('"C:/Program Files/GoNavi/GoNavi.exe" mcp-server --stdio');
|
|
});
|
|
|
|
it('marks OpenClaw and Hermans as remote bridge clients and builds a safe guide', () => {
|
|
const openClaw = EMPTY_MCP_CLIENT_STATUSES.find((item) => item.client === 'openclaw');
|
|
|
|
expect(isRemoteMCPClientStatus(openClaw)).toBe(true);
|
|
const guide = buildRemoteMCPClientGuide(openClaw);
|
|
expect(guide).toContain('GoNavi MCP 远程接入说明 - OpenClaw');
|
|
expect(guide).toContain('云端 Agent 不需要保存数据库密码');
|
|
expect(guide).toContain('默认使用 schema-only 模式,不注册 execute_sql');
|
|
expect(guide).toContain('不能直接使用 Windows 本地 stdio 命令');
|
|
expect(guide).toContain('allowMutating=true');
|
|
expect(guide).toContain('"type": "streamable-http"');
|
|
expect(guide).toContain('"Authorization": "Bearer <随机token>"');
|
|
expect(guide).toContain('GoNavi.exe mcp-server remote-config --client openclaw --url https://<你的域名或隧道地址>/mcp --token <随机token> --schema-only');
|
|
expect(guide).toContain('GoNavi.exe mcp-server http --addr 127.0.0.1:8765 --path /mcp --token <随机token> --schema-only');
|
|
});
|
|
|
|
it('builds remote quick-start snippets for cloud agents without database secrets', () => {
|
|
const quickStart = buildRemoteMCPClientQuickStart({
|
|
client: 'hermans',
|
|
displayName: 'OpenClaw',
|
|
});
|
|
|
|
expect(quickStart.displayName).toBe('OpenClaw');
|
|
expect(quickStart.configJson).toContain('"type": "streamable-http"');
|
|
expect(quickStart.configJson).toContain('"url": "https://<你的域名或隧道地址>/mcp"');
|
|
expect(quickStart.configJson).toContain('"Authorization": "Bearer <随机token>"');
|
|
expect(quickStart.configJson).not.toContain('password');
|
|
expect(quickStart.configCommand).toBe('GoNavi.exe mcp-server remote-config --client hermans --url https://<你的域名或隧道地址>/mcp --token <随机token> --schema-only');
|
|
expect(quickStart.launchCommand).toBe('GoNavi.exe mcp-server http --addr 127.0.0.1:8765 --path /mcp --token <随机token> --schema-only');
|
|
expect(quickStart.standaloneCommand).toBe('gonavi-mcp-server http --addr 127.0.0.1:8765 --path /mcp --token <随机token> --schema-only');
|
|
expect(quickStart.verificationSteps.join('\n')).toContain('get_connections');
|
|
expect(quickStart.securityNotes.join('\n')).toContain('默认 --schema-only 不注册 execute_sql');
|
|
expect(quickStart.securityNotes.join('\n')).toContain('allowMutating=true');
|
|
});
|
|
});
|