@becrafter/prompt-manager 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/app/desktop/assets/app.1.png +0 -0
  2. package/app/desktop/assets/app.png +0 -0
  3. package/app/desktop/assets/icons/icon.icns +0 -0
  4. package/app/desktop/assets/icons/icon.ico +0 -0
  5. package/app/desktop/assets/icons/icon.png +0 -0
  6. package/app/desktop/assets/icons/tray.png +0 -0
  7. package/app/desktop/assets/templates/about.html +147 -0
  8. package/app/desktop/assets/tray.1.png +0 -0
  9. package/app/desktop/assets/tray.png +0 -0
  10. package/app/desktop/docs/ASSETS_PLANNING.md +351 -0
  11. package/app/desktop/docs/REFACTORING_SUMMARY.md +205 -0
  12. package/app/desktop/main.js +340 -0
  13. package/app/desktop/package-lock.json +6912 -0
  14. package/app/desktop/package.json +119 -0
  15. package/app/desktop/preload.js +7 -0
  16. package/app/desktop/src/core/error-handler.js +108 -0
  17. package/app/desktop/src/core/event-emitter.js +84 -0
  18. package/app/desktop/src/core/logger.js +130 -0
  19. package/app/desktop/src/core/state-manager.js +125 -0
  20. package/app/desktop/src/services/module-loader.js +330 -0
  21. package/app/desktop/src/services/runtime-manager.js +398 -0
  22. package/app/desktop/src/services/service-manager.js +210 -0
  23. package/app/desktop/src/services/update-manager.js +267 -0
  24. package/app/desktop/src/ui/about-dialog-manager.js +208 -0
  25. package/app/desktop/src/ui/admin-window-manager.js +757 -0
  26. package/app/desktop/src/ui/splash-manager.js +253 -0
  27. package/app/desktop/src/ui/tray-manager.js +186 -0
  28. package/app/desktop/src/utils/icon-manager.js +133 -0
  29. package/app/desktop/src/utils/path-utils.js +58 -0
  30. package/app/desktop/src/utils/resource-paths.js +49 -0
  31. package/app/desktop/src/utils/resource-sync.js +260 -0
  32. package/app/desktop/src/utils/runtime-sync.js +241 -0
  33. package/app/desktop/src/utils/self-check.js +288 -0
  34. package/app/desktop/src/utils/template-renderer.js +284 -0
  35. package/app/desktop/src/utils/version-utils.js +59 -0
  36. package/env.example +1 -1
  37. package/package.json +12 -1
  38. package/packages/server/.eslintrc.js +70 -0
  39. package/packages/server/.husky/pre-commit +8 -0
  40. package/packages/server/.husky/pre-push +8 -0
  41. package/packages/server/.prettierrc +14 -0
  42. package/packages/server/dev-server.js +90 -0
  43. package/packages/server/jsdoc.conf.json +39 -0
  44. package/packages/server/package.json +2 -0
  45. package/packages/server/playwright.config.js +62 -0
  46. package/packages/server/scripts/generate-docs.js +300 -0
  47. package/packages/server/server.js +1 -0
  48. package/packages/server/services/TerminalService.js +218 -21
  49. package/packages/server/tests/e2e/terminal-e2e.test.js +315 -0
  50. package/packages/server/tests/integration/terminal-websocket.test.js +372 -0
  51. package/packages/server/tests/integration/tools.test.js +264 -0
  52. package/packages/server/tests/setup.js +45 -0
  53. package/packages/server/tests/unit/TerminalService.test.js +410 -0
  54. package/packages/server/tests/unit/WebSocketService.test.js +403 -0
  55. package/packages/server/tests/unit/core.test.js +94 -0
  56. package/packages/server/typedoc.json +52 -0
  57. package/packages/server/utils/config.js +1 -1
  58. package/packages/server/utils/util.js +59 -5
  59. package/packages/server/vitest.config.js +74 -0
@@ -0,0 +1,372 @@
1
+ /**
2
+ * 终端和WebSocket服务集成测试
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
6
+ import { WebSocket } from 'ws';
7
+ import { terminalService } from '../../services/TerminalService.js';
8
+ import { webSocketService } from '../../services/WebSocketService.js';
9
+ import { startServer, stopServer } from '../../server.js';
10
+
11
+ // Mock WebSocket客户端
12
+ vi.mock('ws', () => ({
13
+ WebSocket: vi.fn().mockImplementation(() => ({
14
+ readyState: 1,
15
+ send: vi.fn(),
16
+ close: vi.fn(),
17
+ on: vi.fn(),
18
+ addEventListener: vi.fn()
19
+ }))
20
+ }));
21
+
22
+ describe('Terminal and WebSocket Integration', () => {
23
+ let server;
24
+ let wsPort;
25
+ let mockWebSocketClient;
26
+
27
+ beforeEach(async () => {
28
+ vi.clearAllMocks();
29
+
30
+ // 启动测试服务器
31
+ server = await startServer({
32
+ configOverrides: {
33
+ port: 0, // 使用随机端口
34
+ adminEnable: true
35
+ }
36
+ });
37
+
38
+ // 获取服务器端口
39
+ const address = server.address();
40
+ wsPort = 8081; // WebSocket固定端口
41
+
42
+ // 创建Mock WebSocket客户端
43
+ mockWebSocketClient = new WebSocket();
44
+ });
45
+
46
+ afterEach(async () => {
47
+ // 清理终端会话
48
+ await terminalService.shutdown();
49
+
50
+ // 停止WebSocket服务
51
+ await webSocketService.stop();
52
+
53
+ // 停止服务器
54
+ if (server) {
55
+ await stopServer();
56
+ }
57
+ });
58
+
59
+ describe('完整终端会话流程', () => {
60
+ it('应该完成完整的终端会话生命周期', async () => {
61
+ // 启动WebSocket服务
62
+ await webSocketService.start();
63
+
64
+ // 模拟WebSocket连接
65
+ const mockConnection = {
66
+ clientId: 'test-client-1',
67
+ sessionId: null,
68
+ ws: {
69
+ readyState: 1,
70
+ send: vi.fn(),
71
+ close: vi.fn(),
72
+ on: vi.fn()
73
+ },
74
+ send: vi.fn(),
75
+ sendError: vi.fn()
76
+ };
77
+
78
+ // 添加连接到服务
79
+ webSocketService.connections.set(mockConnection.clientId, mockConnection);
80
+
81
+ // 1. 创建终端会话
82
+ const createMessage = {
83
+ type: 'terminal.create',
84
+ sessionId: 'integration-test-session',
85
+ size: { cols: 80, rows: 24 },
86
+ workingDirectory: process.cwd()
87
+ };
88
+
89
+ // 处理创建会话消息
90
+ await mockConnection.handleMessage?.(JSON.stringify(createMessage));
91
+
92
+ // 验证会话创建
93
+ expect(terminalService.hasSession('integration-test-session')).toBe(true);
94
+ const session = terminalService.getSession('integration-test-session');
95
+ expect(session).toBeDefined();
96
+ expect(session.size).toEqual({ cols: 80, rows: 24 });
97
+
98
+ // 2. 发送命令到终端
99
+ const dataMessage = {
100
+ type: 'terminal.data',
101
+ data: 'echo "Hello, World!"\n'
102
+ };
103
+
104
+ await mockConnection.handleMessage?.(JSON.stringify(dataMessage));
105
+
106
+ // 验证数据发送
107
+ expect(session.write).toHaveBeenCalledWith('echo "Hello, World!"\n');
108
+
109
+ // 3. 调整终端大小
110
+ const resizeMessage = {
111
+ type: 'terminal.resize',
112
+ cols: 120,
113
+ rows: 30
114
+ };
115
+
116
+ await mockConnection.handleMessage?.(JSON.stringify(resizeMessage));
117
+
118
+ // 验证大小调整
119
+ expect(session.resize).toHaveBeenCalledWith(120, 30);
120
+
121
+ // 4. 关闭终端会话
122
+ const closeMessage = {
123
+ type: 'terminal.close'
124
+ };
125
+
126
+ await mockConnection.handleMessage?.(JSON.stringify(closeMessage));
127
+
128
+ // 验证会话关闭
129
+ expect(terminalService.hasSession('integration-test-session')).toBe(false);
130
+ }, 15000);
131
+
132
+ it('应该处理多个并发终端会话', async () => {
133
+ await webSocketService.start();
134
+
135
+ // 创建多个客户端连接
136
+ const clients = [];
137
+ for (let i = 0; i < 3; i++) {
138
+ const client = {
139
+ clientId: `test-client-${i}`,
140
+ sessionId: null,
141
+ ws: {
142
+ readyState: 1,
143
+ send: vi.fn(),
144
+ close: vi.fn(),
145
+ on: vi.fn()
146
+ },
147
+ send: vi.fn(),
148
+ sendError: vi.fn()
149
+ };
150
+ clients.push(client);
151
+ webSocketService.connections.set(client.clientId, client);
152
+ }
153
+
154
+ // 为每个客户端创建会话
155
+ const sessions = [];
156
+ for (let i = 0; i < clients.length; i++) {
157
+ const createMessage = {
158
+ type: 'terminal.create',
159
+ sessionId: `session-${i}`,
160
+ size: { cols: 80, rows: 24 }
161
+ };
162
+
163
+ await clients[i].handleMessage?.(JSON.stringify(createMessage));
164
+ sessions.push(`session-${i}`);
165
+ }
166
+
167
+ // 验证所有会话都已创建
168
+ expect(terminalService.getAllSessions()).toHaveLength(3);
169
+
170
+ // 并发发送命令
171
+ const commands = ['echo "test1"', 'echo "test2"', 'echo "test3"'];
172
+ const promises = clients.map((client, index) => {
173
+ const dataMessage = {
174
+ type: 'terminal.data',
175
+ data: `${commands[index]}\n`
176
+ };
177
+ return client.handleMessage?.(JSON.stringify(dataMessage));
178
+ });
179
+
180
+ await Promise.all(promises);
181
+
182
+ // 验证所有命令都已发送
183
+ for (const sessionId of sessions) {
184
+ const session = terminalService.getSession(sessionId);
185
+ expect(session.write).toHaveBeenCalled();
186
+ }
187
+
188
+ // 清理所有会话
189
+ for (const client of clients) {
190
+ const closeMessage = { type: 'terminal.close' };
191
+ await client.handleMessage?.(JSON.stringify(closeMessage));
192
+ }
193
+
194
+ expect(terminalService.getAllSessions()).toHaveLength(0);
195
+ }, 20000);
196
+ });
197
+
198
+ describe('错误处理和恢复', () => {
199
+ it('应该处理终端会话创建失败', async () => {
200
+ await webSocketService.start();
201
+
202
+ const mockConnection = {
203
+ clientId: 'error-test-client',
204
+ ws: {
205
+ readyState: 1,
206
+ send: vi.fn(),
207
+ close: vi.fn(),
208
+ on: vi.fn()
209
+ },
210
+ send: vi.fn(),
211
+ sendError: vi.fn()
212
+ };
213
+
214
+ webSocketService.connections.set(mockConnection.clientId, mockConnection);
215
+
216
+ // 尝试创建无效的会话(使用无效的工作目录)
217
+ const createMessage = {
218
+ type: 'terminal.create',
219
+ sessionId: 'invalid-session',
220
+ workingDirectory: '/invalid/directory/that/does/not/exist'
221
+ };
222
+
223
+ await mockConnection.handleMessage?.(JSON.stringify(createMessage));
224
+
225
+ // 验证错误消息已发送
226
+ expect(mockConnection.sendError).toHaveBeenCalled();
227
+ });
228
+
229
+ it('应该处理WebSocket连接中断', async () => {
230
+ await webSocketService.start();
231
+
232
+ // 创建一个会话
233
+ const session = await terminalService.createSession();
234
+ expect(session).toBeDefined();
235
+
236
+ // 模拟连接断开
237
+ const mockConnection = {
238
+ clientId: 'disconnect-test-client',
239
+ sessionId: session.id,
240
+ ws: {
241
+ readyState: 1,
242
+ send: vi.fn(),
243
+ close: vi.fn(),
244
+ on: vi.fn()
245
+ }
246
+ };
247
+
248
+ webSocketService.connections.set(mockConnection.clientId, mockConnection);
249
+
250
+ // 触发连接关闭处理
251
+ mockConnection.ws.readyState = 3; // WebSocket.CLOSED
252
+ mockConnection.handleClose?.(1000, 'Normal closure');
253
+
254
+ // 验证会话已被清理
255
+ setTimeout(() => {
256
+ expect(terminalService.hasSession(session.id)).toBe(false);
257
+ }, 100);
258
+ });
259
+ });
260
+
261
+ describe('性能和资源管理', () => {
262
+ it('应该限制最大并发会话数', async () => {
263
+ // 创建有限制配置的服务
264
+ const limitedService = new (await import('../../services/TerminalService.js')).TerminalService({
265
+ maxSessions: 2
266
+ });
267
+
268
+ // 创建最大数量的会话
269
+ await limitedService.createSession();
270
+ await limitedService.createSession();
271
+
272
+ // 尝试创建超出限制的会话
273
+ await expect(limitedService.createSession()).rejects.toThrow('Maximum sessions limit reached');
274
+
275
+ await limitedService.shutdown();
276
+ });
277
+
278
+ it('应该清理超时的会话', async () => {
279
+ const timeoutService = new (await import('../../services/TerminalService.js')).TerminalService({
280
+ timeout: 100 // 100ms超时
281
+ });
282
+
283
+ // 创建会话
284
+ const session = await timeoutService.createSession();
285
+ expect(session).toBeDefined();
286
+
287
+ // 手动设置会话为非活跃状态
288
+ session.lastActivity = new Date(Date.now() - 200); // 超过超时时间
289
+
290
+ // 手动触发清理
291
+ timeoutService.cleanupInactiveSessions();
292
+
293
+ // 验证会话已被清理
294
+ expect(timeoutService.getAllSessions()).toHaveLength(0);
295
+
296
+ await timeoutService.shutdown();
297
+ });
298
+ });
299
+
300
+ describe('跨平台兼容性', () => {
301
+ it('应该在不同平台上创建正确的Shell命令', async () => {
302
+ const platform = process.platform;
303
+
304
+ // 创建会话
305
+ const session = await terminalService.createSession();
306
+
307
+ // 验证Shell命令
308
+ const expectedShell = platform === 'win32'
309
+ ? process.env.COMSPEC || 'cmd.exe'
310
+ : process.env.SHELL || '/bin/bash';
311
+
312
+ expect(session.shell).toContain(expectedShell.split('/').pop() || expectedShell.split('\\').pop());
313
+
314
+ await terminalService.removeSession(session.id);
315
+ });
316
+
317
+ it('应该执行跨平台命令', async () => {
318
+ // 测试基本命令
319
+ const result = await terminalService.executeCommand('echo "cross-platform test"');
320
+
321
+ expect(result).toBeDefined();
322
+ expect(typeof result.exitCode).toBe('number');
323
+ expect(result.stdout).toContain('cross-platform test');
324
+ });
325
+ });
326
+
327
+ describe('安全性', () => {
328
+ it('应该限制命令执行权限', async () => {
329
+ // 尝试执行危险命令(应该被限制或失败)
330
+ const dangerousCommands = [
331
+ 'rm -rf /',
332
+ 'format c:',
333
+ 'sudo rm -rf /'
334
+ ];
335
+
336
+ for (const command of dangerousCommands) {
337
+ // 这些命令应该失败或被阻止
338
+ try {
339
+ const result = await terminalService.executeCommand(command);
340
+ // 如果没有抛出错误,至少应该有非零退出码
341
+ expect(result.exitCode).not.toBe(0);
342
+ } catch (error) {
343
+ // 或者应该抛出权限错误
344
+ expect(error.message).toContain('permission') ||
345
+ expect(error.message).toContain('denied') ||
346
+ expect(error.message).toContain('forbidden');
347
+ }
348
+ }
349
+ });
350
+
351
+ it('应该限制文件系统访问', async () => {
352
+ // 尝试访问系统敏感目录
353
+ const sensitivePaths = [
354
+ '/etc/passwd',
355
+ 'C:\\Windows\\System32\\config',
356
+ '/etc/shadow'
357
+ ];
358
+
359
+ for (const path of sensitivePaths) {
360
+ try {
361
+ const result = await terminalService.executeCommand(`cat ${path}`);
362
+ // 如果成功访问,应该返回空或权限错误
363
+ expect(result.exitCode).not.toBe(0);
364
+ } catch (error) {
365
+ // 应该抛出权限错误
366
+ expect(error.message).toContain('permission') ||
367
+ expect(error.message).toContain('denied');
368
+ }
369
+ }
370
+ });
371
+ });
372
+ });
@@ -0,0 +1,264 @@
1
+ /**
2
+ * 工具系统测试文件
3
+ *
4
+ * 用于验证工具加载、工具管理等功能是否正常工作
5
+ */
6
+
7
+ import { toolLoaderService } from './tool-loader.service.js';
8
+ import { handleToolM } from './tool-manager.handler.js';
9
+ import { logger } from '../utils/logger.js';
10
+
11
+ // 测试工具加载服务
12
+ async function testToolLoader() {
13
+ console.log('\n========== 测试工具加载服务 ==========\n');
14
+
15
+ try {
16
+ // 初始化工具加载器
17
+ console.log('1. 初始化工具加载器...');
18
+ await toolLoaderService.initialize();
19
+ console.log('✓ 工具加载器初始化成功');
20
+
21
+ // 获取所有工具列表
22
+ console.log('\n2. 获取所有工具列表...');
23
+ const tools = toolLoaderService.getAllTools();
24
+ console.log(`✓ 共加载 ${tools.length} 个工具:`);
25
+ tools.forEach(tool => {
26
+ console.log(` - ${tool.name}: ${tool.metadata.description || '无描述'}`);
27
+ });
28
+
29
+ // 检查 filesystem 工具是否存在
30
+ console.log('\n3. 检查 filesystem 工具是否存在...');
31
+ const hasFilesystem = toolLoaderService.hasTool('filesystem');
32
+ if (hasFilesystem) {
33
+ console.log('✓ filesystem 工具已加载');
34
+
35
+ // 获取 filesystem 工具详情
36
+ const filesystemTool = toolLoaderService.getTool('filesystem');
37
+ console.log(` - 元数据:`, filesystemTool.metadata);
38
+ } else {
39
+ console.log('✗ filesystem 工具未找到');
40
+ }
41
+
42
+ return true;
43
+ } catch (error) {
44
+ console.error('✗ 测试失败:', error.message);
45
+ console.error(error.stack);
46
+ return false;
47
+ }
48
+ }
49
+
50
+ // 测试手册模式
51
+ async function testManualMode() {
52
+ console.log('\n========== 测试手册模式 (manual) ==========\n');
53
+
54
+ try {
55
+ const yamlInput = `tool: tool://filesystem
56
+ mode: manual`;
57
+
58
+ console.log('YAML 输入:');
59
+ console.log(yamlInput);
60
+ console.log('\n执行 handleToolM...');
61
+
62
+ const result = await handleToolM({ yaml: yamlInput });
63
+
64
+ console.log('\n✓ 手册模式执行成功');
65
+ console.log('返回结果类型:', result.content[0].type);
66
+ console.log('手册内容长度:', result.content[0].text.length, '字符');
67
+ console.log('\n手册内容预览(前 500 字符):');
68
+ console.log(result.content[0].text.substring(0, 500));
69
+ console.log('...\n');
70
+
71
+ return true;
72
+ } catch (error) {
73
+ console.error('✗ 测试失败:', error.message);
74
+ console.error(error.stack);
75
+ return false;
76
+ }
77
+ }
78
+
79
+ // 测试执行模式
80
+ async function testExecuteMode() {
81
+ console.log('\n========== 测试执行模式 (execute) ==========\n');
82
+
83
+ try {
84
+ const yamlInput = `tool: tool://filesystem
85
+ mode: execute
86
+ parameters:
87
+ method: list_allowed_directories`;
88
+
89
+ console.log('YAML 输入:');
90
+ console.log(yamlInput);
91
+ console.log('\n执行 handleToolM...');
92
+
93
+ const result = await handleToolM({ yaml: yamlInput });
94
+
95
+ console.log('\n✓ 执行模式测试成功');
96
+ console.log('返回结果:');
97
+ console.log(result.content[0].text);
98
+
99
+ return true;
100
+ } catch (error) {
101
+ console.error('✗ 测试失败:', error.message);
102
+ console.error(error.stack);
103
+ return false;
104
+ }
105
+ }
106
+
107
+ // 测试配置模式
108
+ async function testConfigureMode() {
109
+ console.log('\n========== 测试配置模式 (configure) ==========\n');
110
+
111
+ try {
112
+ const yamlInput = `tool: tool://filesystem
113
+ mode: configure
114
+ parameters:
115
+ ALLOWED_DIRECTORIES: '["~/.prompt-manager", "/tmp"]'`;
116
+
117
+ console.log('YAML 输入:');
118
+ console.log(yamlInput);
119
+ console.log('\n执行 handleToolM...');
120
+
121
+ const result = await handleToolM({ yaml: yamlInput });
122
+
123
+ console.log('\n✓ 配置模式测试成功');
124
+ console.log('返回结果:');
125
+ console.log(result.content[0].text);
126
+
127
+ return true;
128
+ } catch (error) {
129
+ console.error('✗ 测试失败:', error.message);
130
+ console.error(error.stack);
131
+ return false;
132
+ }
133
+ }
134
+
135
+ // 测试日志模式
136
+ async function testLogMode() {
137
+ console.log('\n========== 测试日志模式 (log) ==========\n');
138
+
139
+ try {
140
+ const yamlInput = `tool: tool://filesystem
141
+ mode: log
142
+ parameters:
143
+ action: tail
144
+ lines: 50`;
145
+
146
+ console.log('YAML 输入:');
147
+ console.log(yamlInput);
148
+ console.log('\n执行 handleToolM...');
149
+
150
+ const result = await handleToolM({ yaml: yamlInput });
151
+
152
+ console.log('\n✓ 日志模式测试成功');
153
+ console.log('返回结果:');
154
+ console.log(result.content[0].text);
155
+
156
+ return true;
157
+ } catch (error) {
158
+ console.error('✗ 测试失败:', error.message);
159
+ console.error(error.stack);
160
+ return false;
161
+ }
162
+ }
163
+
164
+ // 测试错误处理
165
+ async function testErrorHandling() {
166
+ console.log('\n========== 测试错误处理 ==========\n');
167
+
168
+ try {
169
+ console.log('1. 测试不存在的工具...');
170
+ try {
171
+ const yamlInput = `tool: tool://nonexistent
172
+ mode: execute`;
173
+ await handleToolM({ yaml: yamlInput });
174
+ console.log('✗ 应该抛出错误但没有');
175
+ return false;
176
+ } catch (error) {
177
+ console.log('✓ 正确抛出错误:', error.message.split('\n')[0]);
178
+ }
179
+
180
+ console.log('\n2. 测试缺少必需参数...');
181
+ try {
182
+ await handleToolM({});
183
+ console.log('✗ 应该抛出错误但没有');
184
+ return false;
185
+ } catch (error) {
186
+ console.log('✓ 正确抛出错误:', error.message);
187
+ }
188
+
189
+ console.log('\n3. 测试无效的工具格式...');
190
+ try {
191
+ const yamlInput = `tool: filesystem
192
+ mode: execute`;
193
+ await handleToolM({ yaml: yamlInput });
194
+ console.log('✗ 应该抛出错误但没有');
195
+ return false;
196
+ } catch (error) {
197
+ console.log('✓ 正确抛出错误:', error.message.split('\n')[0]);
198
+ }
199
+
200
+ return true;
201
+ } catch (error) {
202
+ console.error('✗ 测试失败:', error.message);
203
+ console.error(error.stack);
204
+ return false;
205
+ }
206
+ }
207
+
208
+ // 主测试函数
209
+ async function runAllTests() {
210
+ console.log('\n');
211
+ console.log('╔════════════════════════════════════════════════╗');
212
+ console.log('║ Prompt Manager 工具系统测试套件 ║');
213
+ console.log('╚════════════════════════════════════════════════╝');
214
+
215
+ const results = [];
216
+
217
+ // 运行所有测试
218
+ results.push({ name: '工具加载服务', passed: await testToolLoader() });
219
+ results.push({ name: '手册模式', passed: await testManualMode() });
220
+ results.push({ name: '执行模式', passed: await testExecuteMode() });
221
+ results.push({ name: '配置模式', passed: await testConfigureMode() });
222
+ results.push({ name: '日志模式', passed: await testLogMode() });
223
+ results.push({ name: '错误处理', passed: await testErrorHandling() });
224
+
225
+ // 输出测试总结
226
+ console.log('\n');
227
+ console.log('╔════════════════════════════════════════════════╗');
228
+ console.log('║ 测试结果总结 ║');
229
+ console.log('╚════════════════════════════════════════════════╝');
230
+ console.log('\n');
231
+
232
+ const passed = results.filter(r => r.passed).length;
233
+ const failed = results.filter(r => !r.passed).length;
234
+
235
+ results.forEach(result => {
236
+ const status = result.passed ? '✓ PASS' : '✗ FAIL';
237
+ console.log(`${status} - ${result.name}`);
238
+ });
239
+
240
+ console.log('\n');
241
+ console.log(`总计: ${results.length} 个测试`);
242
+ console.log(`通过: ${passed} 个`);
243
+ console.log(`失败: ${failed} 个`);
244
+ console.log('\n');
245
+
246
+ if (failed === 0) {
247
+ console.log('🎉 所有测试通过!工具系统运行正常。');
248
+ } else {
249
+ console.log('⚠️ 部分测试失败,请检查错误信息。');
250
+ }
251
+
252
+ return failed === 0;
253
+ }
254
+
255
+ // 运行测试
256
+ runAllTests()
257
+ .then(success => {
258
+ process.exit(success ? 0 : 1);
259
+ })
260
+ .catch(error => {
261
+ console.error('测试运行失败:', error);
262
+ process.exit(1);
263
+ });
264
+
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Vitest 测试设置文件
3
+ */
4
+
5
+ import { vi } from 'vitest';
6
+
7
+ // Mock node-pty 模块
8
+ vi.mock('node-pty', () => {
9
+ const mockPty = {
10
+ on: vi.fn(),
11
+ write: vi.fn(),
12
+ resize: vi.fn(),
13
+ kill: vi.fn(),
14
+ pid: 12345,
15
+ cols: 80,
16
+ rows: 24
17
+ };
18
+
19
+ return {
20
+ default: {
21
+ spawn: vi.fn(() => mockPty)
22
+ },
23
+ spawn: vi.fn(() => mockPty)
24
+ };
25
+ });
26
+
27
+ // Mock fs-extra 的某些方法(如果需要)
28
+ vi.mock('fs-extra', async () => {
29
+ const actual = await vi.importActual('fs-extra');
30
+ return {
31
+ ...actual,
32
+ // 可以在这里添加特定的mock
33
+ };
34
+ });
35
+
36
+ // 全局测试设置
37
+ global.console = {
38
+ ...console,
39
+ // 在测试中减少日志输出
40
+ log: vi.fn(),
41
+ debug: vi.fn(),
42
+ info: vi.fn(),
43
+ warn: vi.fn(),
44
+ error: vi.fn(),
45
+ };