ai-cli-mcp 2.1.0 → 2.3.0

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.
@@ -1,665 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { spawn } from 'node:child_process';
3
- import { existsSync } from 'node:fs';
4
- import { homedir } from 'node:os';
5
- import { resolve as pathResolve } from 'node:path';
6
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
- import { EventEmitter } from 'node:events';
8
- // Mock dependencies
9
- vi.mock('node:child_process');
10
- vi.mock('node:fs');
11
- vi.mock('node:os');
12
- vi.mock('node:path', () => ({
13
- resolve: vi.fn((path) => path),
14
- join: vi.fn((...args) => args.join('/')),
15
- isAbsolute: vi.fn((path) => path.startsWith('/'))
16
- }));
17
- vi.mock('@modelcontextprotocol/sdk/server/stdio.js');
18
- vi.mock('@modelcontextprotocol/sdk/types.js', () => ({
19
- ListToolsRequestSchema: { name: 'listTools' },
20
- CallToolRequestSchema: { name: 'callTool' },
21
- ErrorCode: {
22
- InternalError: 'InternalError',
23
- MethodNotFound: 'MethodNotFound',
24
- InvalidParams: 'InvalidParams'
25
- },
26
- McpError: vi.fn().mockImplementation((code, message) => {
27
- const error = new Error(message);
28
- error.code = code;
29
- return error;
30
- })
31
- }));
32
- vi.mock('@modelcontextprotocol/sdk/server/index.js', () => ({
33
- Server: vi.fn().mockImplementation(function () {
34
- this.setRequestHandler = vi.fn();
35
- this.connect = vi.fn();
36
- this.close = vi.fn();
37
- this.onerror = undefined;
38
- return this;
39
- }),
40
- }));
41
- // Mock package.json
42
- vi.mock('../../package.json', () => ({
43
- default: { version: '1.0.0-test' }
44
- }));
45
- // Re-import after mocks
46
- const mockExistsSync = vi.mocked(existsSync);
47
- const mockSpawn = vi.mocked(spawn);
48
- const mockHomedir = vi.mocked(homedir);
49
- const mockPathResolve = vi.mocked(pathResolve);
50
- // Module loading will happen in tests
51
- describe('ClaudeCodeServer Unit Tests', () => {
52
- let consoleErrorSpy;
53
- let consoleWarnSpy;
54
- let originalEnv;
55
- beforeEach(() => {
56
- vi.clearAllMocks();
57
- vi.resetModules();
58
- vi.unmock('../server.js');
59
- consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
60
- consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
61
- originalEnv = { ...process.env };
62
- // Reset env
63
- process.env = { ...originalEnv };
64
- });
65
- afterEach(() => {
66
- consoleErrorSpy.mockRestore();
67
- consoleWarnSpy.mockRestore();
68
- process.env = originalEnv;
69
- });
70
- describe('debugLog function', () => {
71
- it('should log when debug mode is enabled', async () => {
72
- process.env.MCP_CLAUDE_DEBUG = 'true';
73
- const module = await import('../server.js');
74
- // @ts-ignore - accessing private function for testing
75
- const { debugLog } = module;
76
- debugLog('Test message');
77
- expect(consoleErrorSpy).toHaveBeenCalledWith('Test message');
78
- });
79
- it('should not log when debug mode is disabled', async () => {
80
- // Reset modules to clear cache
81
- vi.resetModules();
82
- consoleErrorSpy.mockClear();
83
- process.env.MCP_CLAUDE_DEBUG = 'false';
84
- const module = await import('../server.js');
85
- // @ts-ignore
86
- const { debugLog } = module;
87
- debugLog('Test message');
88
- expect(consoleErrorSpy).not.toHaveBeenCalled();
89
- });
90
- });
91
- describe('findClaudeCli function', () => {
92
- it('should return local path when it exists', async () => {
93
- mockHomedir.mockReturnValue('/home/user');
94
- mockExistsSync.mockImplementation((path) => {
95
- // Mock returns true for real CLI path
96
- if (path === '/home/user/.claude/local/claude')
97
- return true;
98
- return false;
99
- });
100
- const module = await import('../server.js');
101
- // @ts-ignore
102
- const findClaudeCli = module.default?.findClaudeCli || module.findClaudeCli;
103
- const result = findClaudeCli();
104
- expect(result).toBe('/home/user/.claude/local/claude');
105
- });
106
- it('should fallback to PATH when local does not exist', async () => {
107
- mockHomedir.mockReturnValue('/home/user');
108
- mockExistsSync.mockReturnValue(false);
109
- const module = await import('../server.js');
110
- // @ts-ignore
111
- const findClaudeCli = module.default?.findClaudeCli || module.findClaudeCli;
112
- const result = findClaudeCli();
113
- expect(result).toBe('claude');
114
- expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Claude CLI not found at ~/.claude/local/claude'));
115
- });
116
- it('should use custom name from CLAUDE_CLI_NAME', async () => {
117
- process.env.CLAUDE_CLI_NAME = 'my-claude';
118
- mockHomedir.mockReturnValue('/home/user');
119
- mockExistsSync.mockReturnValue(false);
120
- const module = await import('../server.js');
121
- // @ts-ignore
122
- const findClaudeCli = module.default?.findClaudeCli || module.findClaudeCli;
123
- const result = findClaudeCli();
124
- expect(result).toBe('my-claude');
125
- });
126
- it('should use absolute path from CLAUDE_CLI_NAME', async () => {
127
- process.env.CLAUDE_CLI_NAME = '/absolute/path/to/claude';
128
- const module = await import('../server.js');
129
- // @ts-ignore
130
- const findClaudeCli = module.default?.findClaudeCli || module.findClaudeCli;
131
- const result = findClaudeCli();
132
- expect(result).toBe('/absolute/path/to/claude');
133
- });
134
- it('should throw error for relative paths in CLAUDE_CLI_NAME', async () => {
135
- process.env.CLAUDE_CLI_NAME = './relative/path/claude';
136
- const module = await import('../server.js');
137
- // @ts-ignore
138
- const findClaudeCli = module.default?.findClaudeCli || module.findClaudeCli;
139
- expect(() => findClaudeCli()).toThrow('Invalid CLAUDE_CLI_NAME: Relative paths are not allowed');
140
- });
141
- it('should throw error for paths with ../ in CLAUDE_CLI_NAME', async () => {
142
- process.env.CLAUDE_CLI_NAME = '../relative/path/claude';
143
- const module = await import('../server.js');
144
- // @ts-ignore
145
- const findClaudeCli = module.default?.findClaudeCli || module.findClaudeCli;
146
- expect(() => findClaudeCli()).toThrow('Invalid CLAUDE_CLI_NAME: Relative paths are not allowed');
147
- });
148
- });
149
- describe('spawnAsync function', () => {
150
- let mockProcess;
151
- beforeEach(() => {
152
- // Create a mock process
153
- mockProcess = new EventEmitter();
154
- mockProcess.stdout = new EventEmitter();
155
- mockProcess.stderr = new EventEmitter();
156
- mockProcess.stdout.on = vi.fn((event, handler) => {
157
- mockProcess.stdout[event] = handler;
158
- });
159
- mockProcess.stderr.on = vi.fn((event, handler) => {
160
- mockProcess.stderr[event] = handler;
161
- });
162
- mockSpawn.mockReturnValue(mockProcess);
163
- });
164
- it('should execute command successfully', async () => {
165
- const module = await import('../server.js');
166
- // @ts-ignore
167
- const { spawnAsync } = module;
168
- // mockProcess is already defined in the outer scope
169
- // Start the async operation
170
- const promise = spawnAsync('echo', ['test']);
171
- // Simulate successful execution
172
- setTimeout(() => {
173
- mockProcess.stdout['data']('test output');
174
- mockProcess.stderr['data']('');
175
- mockProcess.emit('close', 0);
176
- }, 10);
177
- const result = await promise;
178
- expect(result).toEqual({
179
- stdout: 'test output',
180
- stderr: ''
181
- });
182
- });
183
- it('should handle command failure', async () => {
184
- const module = await import('../server.js');
185
- // @ts-ignore
186
- const { spawnAsync } = module;
187
- // mockProcess is already defined in the outer scope
188
- // Start the async operation
189
- const promise = spawnAsync('false', []);
190
- // Simulate failed execution
191
- setTimeout(() => {
192
- mockProcess.stderr['data']('error output');
193
- mockProcess.emit('close', 1);
194
- }, 10);
195
- await expect(promise).rejects.toThrow('Command failed with exit code 1');
196
- });
197
- it('should handle spawn error', async () => {
198
- const module = await import('../server.js');
199
- // @ts-ignore
200
- const { spawnAsync } = module;
201
- // mockProcess is already defined in the outer scope
202
- // Start the async operation
203
- const promise = spawnAsync('nonexistent', []);
204
- // Simulate spawn error
205
- setTimeout(() => {
206
- const error = new Error('spawn error');
207
- error.code = 'ENOENT';
208
- error.path = 'nonexistent';
209
- error.syscall = 'spawn';
210
- mockProcess.emit('error', error);
211
- }, 10);
212
- await expect(promise).rejects.toThrow('Spawn error');
213
- });
214
- it('should respect timeout option', async () => {
215
- const module = await import('../server.js');
216
- // @ts-ignore
217
- const { spawnAsync } = module;
218
- const result = spawnAsync('sleep', ['10'], { timeout: 100 });
219
- expect(mockSpawn).toHaveBeenCalledWith('sleep', ['10'], expect.objectContaining({
220
- timeout: 100
221
- }));
222
- });
223
- it('should use provided cwd option', async () => {
224
- const module = await import('../server.js');
225
- // @ts-ignore
226
- const { spawnAsync } = module;
227
- const result = spawnAsync('ls', [], { cwd: '/tmp' });
228
- expect(mockSpawn).toHaveBeenCalledWith('ls', [], expect.objectContaining({
229
- cwd: '/tmp'
230
- }));
231
- });
232
- });
233
- describe('ClaudeCodeServer class', () => {
234
- it('should initialize with correct settings', async () => {
235
- mockHomedir.mockReturnValue('/home/user');
236
- mockExistsSync.mockReturnValue(true);
237
- // Set up Server mock before resetting modules
238
- vi.mocked(Server).mockImplementation(function () {
239
- this.setRequestHandler = vi.fn();
240
- this.connect = vi.fn();
241
- this.close = vi.fn();
242
- this.onerror = undefined;
243
- return this;
244
- });
245
- const module = await import('../server.js');
246
- // @ts-ignore
247
- const { ClaudeCodeServer } = module;
248
- const server = new ClaudeCodeServer();
249
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('[Setup] Using Claude CLI command/path:'));
250
- });
251
- it('should set up tool handlers', async () => {
252
- mockHomedir.mockReturnValue('/home/user');
253
- mockExistsSync.mockReturnValue(true);
254
- const { Server } = await import('@modelcontextprotocol/sdk/server/index.js');
255
- const mockSetRequestHandler = vi.fn();
256
- vi.mocked(Server).mockImplementation(function () {
257
- this.setRequestHandler = mockSetRequestHandler;
258
- this.connect = vi.fn();
259
- this.close = vi.fn();
260
- this.onerror = undefined;
261
- return this;
262
- });
263
- const module = await import('../server.js');
264
- // @ts-ignore
265
- const { ClaudeCodeServer } = module;
266
- const server = new ClaudeCodeServer();
267
- expect(mockSetRequestHandler).toHaveBeenCalled();
268
- });
269
- it('should set up error handler', async () => {
270
- mockHomedir.mockReturnValue('/home/user');
271
- mockExistsSync.mockReturnValue(true);
272
- const { Server } = await import('@modelcontextprotocol/sdk/server/index.js');
273
- let errorHandler = null;
274
- vi.mocked(Server).mockImplementation(function () {
275
- this.setRequestHandler = vi.fn();
276
- this.connect = vi.fn();
277
- this.close = vi.fn();
278
- Object.defineProperty(this, 'onerror', {
279
- get() { return errorHandler; },
280
- set(handler) { errorHandler = handler; },
281
- enumerable: true,
282
- configurable: true
283
- });
284
- return this;
285
- });
286
- const module = await import('../server.js');
287
- // @ts-ignore
288
- const { ClaudeCodeServer } = module;
289
- const server = new ClaudeCodeServer();
290
- // Test error handler
291
- errorHandler(new Error('Test error'));
292
- expect(consoleErrorSpy).toHaveBeenCalledWith('[Error]', expect.any(Error));
293
- });
294
- it('should handle SIGINT', async () => {
295
- mockHomedir.mockReturnValue('/home/user');
296
- mockExistsSync.mockReturnValue(true);
297
- // Set up Server mock first
298
- vi.mocked(Server).mockImplementation(function () {
299
- this.setRequestHandler = vi.fn();
300
- this.connect = vi.fn();
301
- this.close = vi.fn();
302
- this.onerror = undefined;
303
- return this;
304
- });
305
- const module = await import('../server.js');
306
- // @ts-ignore
307
- const { ClaudeCodeServer } = module;
308
- const exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined);
309
- const server = new ClaudeCodeServer();
310
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
311
- // Emit SIGINT
312
- const sigintHandler = process.listeners('SIGINT').slice(-1)[0];
313
- await sigintHandler();
314
- expect(mockServerInstance.close).toHaveBeenCalled();
315
- expect(exitSpy).toHaveBeenCalledWith(0);
316
- exitSpy.mockRestore();
317
- });
318
- });
319
- describe('Tool handler implementation', () => {
320
- // Define setupServerMock for this describe block
321
- let errorHandler = null;
322
- function setupServerMock() {
323
- errorHandler = null;
324
- vi.mocked(Server).mockImplementation(function () {
325
- this.setRequestHandler = vi.fn();
326
- this.connect = vi.fn();
327
- this.close = vi.fn();
328
- Object.defineProperty(this, 'onerror', {
329
- get() { return errorHandler; },
330
- set(handler) { errorHandler = handler; },
331
- enumerable: true,
332
- configurable: true
333
- });
334
- return this;
335
- });
336
- }
337
- it('should handle ListToolsRequest', async () => {
338
- mockHomedir.mockReturnValue('/home/user');
339
- mockExistsSync.mockReturnValue(true);
340
- // Use the setupServerMock function from the beginning of the file
341
- setupServerMock();
342
- const module = await import('../server.js');
343
- // @ts-ignore
344
- const { ClaudeCodeServer } = module;
345
- const server = new ClaudeCodeServer();
346
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
347
- // Find the ListToolsRequest handler
348
- const listToolsCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'listTools');
349
- expect(listToolsCall).toBeDefined();
350
- // Test the handler
351
- const handler = listToolsCall[1];
352
- const result = await handler();
353
- expect(result.tools).toHaveLength(4);
354
- expect(result.tools[0].name).toBe('claude_code');
355
- expect(result.tools[0].description).toContain('Claude Code Agent');
356
- expect(result.tools[1].name).toBe('list_claude_processes');
357
- expect(result.tools[2].name).toBe('get_claude_result');
358
- expect(result.tools[3].name).toBe('kill_claude_process');
359
- });
360
- it('should handle CallToolRequest', async () => {
361
- mockHomedir.mockReturnValue('/home/user');
362
- mockExistsSync.mockReturnValue(true);
363
- // Set up Server mock
364
- setupServerMock();
365
- const module = await import('../server.js');
366
- // @ts-ignore
367
- const { ClaudeCodeServer } = module;
368
- const server = new ClaudeCodeServer();
369
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
370
- // Find the CallToolRequest handler
371
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
372
- expect(callToolCall).toBeDefined();
373
- // Create a mock process for the tool execution
374
- const mockProcess = new EventEmitter();
375
- mockProcess.pid = 12345;
376
- mockProcess.stdout = new EventEmitter();
377
- mockProcess.stderr = new EventEmitter();
378
- mockProcess.stdout.on = vi.fn();
379
- mockProcess.stderr.on = vi.fn();
380
- mockProcess.kill = vi.fn();
381
- mockSpawn.mockReturnValue(mockProcess);
382
- // Test the handler
383
- const handler = callToolCall[1];
384
- const result = await handler({
385
- params: {
386
- name: 'claude_code',
387
- arguments: {
388
- prompt: 'test prompt',
389
- workFolder: '/tmp'
390
- }
391
- }
392
- });
393
- // claude_code now returns PID immediately
394
- expect(result.content[0].type).toBe('text');
395
- const response = JSON.parse(result.content[0].text);
396
- expect(response.pid).toBe(12345);
397
- expect(response.status).toBe('started');
398
- });
399
- it('should require workFolder parameter', async () => {
400
- mockHomedir.mockReturnValue('/home/user');
401
- mockExistsSync.mockReturnValue(true);
402
- // Set up Server mock
403
- setupServerMock();
404
- const module = await import('../server.js');
405
- // @ts-ignore
406
- const { ClaudeCodeServer } = module;
407
- const server = new ClaudeCodeServer();
408
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
409
- // Find the CallToolRequest handler
410
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
411
- const handler = callToolCall[1];
412
- // Test missing workFolder
413
- await expect(handler({
414
- params: {
415
- name: 'claude_code',
416
- arguments: {
417
- prompt: 'test'
418
- }
419
- }
420
- })).rejects.toThrow('Missing or invalid required parameter: workFolder');
421
- });
422
- it('should handle non-existent workFolder', async () => {
423
- mockHomedir.mockReturnValue('/home/user');
424
- mockExistsSync.mockImplementation((path) => {
425
- // Make the CLI path exist but the workFolder not exist
426
- if (String(path).includes('.claude'))
427
- return true;
428
- if (path === '/nonexistent')
429
- return false;
430
- return false;
431
- });
432
- // Set up Server mock
433
- setupServerMock();
434
- const module = await import('../server.js');
435
- // @ts-ignore
436
- const { ClaudeCodeServer } = module;
437
- const server = new ClaudeCodeServer();
438
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
439
- // Find the CallToolRequest handler
440
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
441
- const handler = callToolCall[1];
442
- // Should throw error for non-existent workFolder
443
- await expect(handler({
444
- params: {
445
- name: 'claude_code',
446
- arguments: {
447
- prompt: 'test',
448
- workFolder: '/nonexistent'
449
- }
450
- }
451
- })).rejects.toThrow('Working folder does not exist');
452
- });
453
- it('should handle session_id parameter', async () => {
454
- mockHomedir.mockReturnValue('/home/user');
455
- mockExistsSync.mockReturnValue(true);
456
- // Set up Server mock
457
- setupServerMock();
458
- const module = await import('../server.js');
459
- // @ts-ignore
460
- const { ClaudeCodeServer } = module;
461
- const server = new ClaudeCodeServer();
462
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
463
- // Find the CallToolRequest handler
464
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
465
- const handler = callToolCall[1];
466
- // Create mock process
467
- const mockProcess = new EventEmitter();
468
- mockProcess.pid = 12347;
469
- mockProcess.stdout = new EventEmitter();
470
- mockProcess.stderr = new EventEmitter();
471
- mockProcess.stdout.on = vi.fn();
472
- mockProcess.stderr.on = vi.fn();
473
- mockProcess.kill = vi.fn();
474
- mockSpawn.mockReturnValue(mockProcess);
475
- const result = await handler({
476
- params: {
477
- name: 'claude_code',
478
- arguments: {
479
- prompt: 'test prompt',
480
- workFolder: '/tmp',
481
- session_id: 'test-session-123'
482
- }
483
- }
484
- });
485
- // Verify spawn was called with -r flag
486
- expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['-r', 'test-session-123', '-p', 'test prompt']), expect.any(Object));
487
- });
488
- it('should handle prompt_file parameter', async () => {
489
- mockHomedir.mockReturnValue('/home/user');
490
- mockExistsSync.mockImplementation((path) => {
491
- if (String(path).includes('.claude'))
492
- return true;
493
- if (path === '/tmp')
494
- return true;
495
- if (path === '/tmp/prompt.txt')
496
- return true;
497
- return false;
498
- });
499
- // Mock readFileSync
500
- const readFileSyncMock = vi.fn().mockReturnValue('Content from file');
501
- vi.doMock('node:fs', () => ({
502
- existsSync: mockExistsSync,
503
- readFileSync: readFileSyncMock
504
- }));
505
- // Set up Server mock
506
- setupServerMock();
507
- const module = await import('../server.js');
508
- // @ts-ignore
509
- const { ClaudeCodeServer } = module;
510
- const server = new ClaudeCodeServer();
511
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
512
- // Find the CallToolRequest handler
513
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
514
- const handler = callToolCall[1];
515
- // Create mock process
516
- const mockProcess = new EventEmitter();
517
- mockProcess.pid = 12348;
518
- mockProcess.stdout = new EventEmitter();
519
- mockProcess.stderr = new EventEmitter();
520
- mockProcess.stdout.on = vi.fn();
521
- mockProcess.stderr.on = vi.fn();
522
- mockProcess.kill = vi.fn();
523
- mockSpawn.mockReturnValue(mockProcess);
524
- const result = await handler({
525
- params: {
526
- name: 'claude_code',
527
- arguments: {
528
- prompt_file: '/tmp/prompt.txt',
529
- workFolder: '/tmp'
530
- }
531
- }
532
- });
533
- // Verify file was read and spawn was called with content
534
- expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['-p', 'Content from file']), expect.any(Object));
535
- });
536
- it('should resolve model aliases when calling claude_code tool', async () => {
537
- mockHomedir.mockReturnValue('/home/user');
538
- mockExistsSync.mockReturnValue(true);
539
- // Set up spawn mock to return a process
540
- const mockProcess = new EventEmitter();
541
- mockProcess.stdout = new EventEmitter();
542
- mockProcess.stderr = new EventEmitter();
543
- mockProcess.pid = 12345;
544
- mockSpawn.mockReturnValue(mockProcess);
545
- // Set up Server mock
546
- setupServerMock();
547
- const module = await import('../server.js');
548
- // @ts-ignore
549
- const { ClaudeCodeServer } = module;
550
- const server = new ClaudeCodeServer();
551
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
552
- // Find the CallToolRequest handler
553
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
554
- const handler = callToolCall[1];
555
- // Test with haiku alias
556
- const result = await handler({
557
- params: {
558
- name: 'claude_code',
559
- arguments: {
560
- prompt: 'test prompt',
561
- workFolder: '/tmp',
562
- model: 'haiku'
563
- }
564
- }
565
- });
566
- // Verify spawn was called with resolved model name
567
- expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['--model', 'claude-3-5-haiku-20241022']), expect.any(Object));
568
- // Verify PID is returned
569
- expect(result.content[0].text).toContain('"pid": 12345');
570
- });
571
- it('should pass non-alias model names unchanged', async () => {
572
- mockHomedir.mockReturnValue('/home/user');
573
- mockExistsSync.mockReturnValue(true);
574
- // Set up spawn mock to return a process
575
- const mockProcess = new EventEmitter();
576
- mockProcess.stdout = new EventEmitter();
577
- mockProcess.stderr = new EventEmitter();
578
- mockProcess.pid = 12346;
579
- mockSpawn.mockReturnValue(mockProcess);
580
- // Set up Server mock
581
- setupServerMock();
582
- const module = await import('../server.js');
583
- // @ts-ignore
584
- const { ClaudeCodeServer } = module;
585
- const server = new ClaudeCodeServer();
586
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
587
- // Find the CallToolRequest handler
588
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
589
- const handler = callToolCall[1];
590
- // Test with non-alias model name
591
- const result = await handler({
592
- params: {
593
- name: 'claude_code',
594
- arguments: {
595
- prompt: 'test prompt',
596
- workFolder: '/tmp',
597
- model: 'sonnet'
598
- }
599
- }
600
- });
601
- // Verify spawn was called with unchanged model name
602
- expect(mockSpawn).toHaveBeenCalledWith(expect.any(String), expect.arrayContaining(['--model', 'sonnet']), expect.any(Object));
603
- });
604
- it('should reject when both prompt and prompt_file are provided', async () => {
605
- mockHomedir.mockReturnValue('/home/user');
606
- mockExistsSync.mockReturnValue(true);
607
- // Set up Server mock
608
- setupServerMock();
609
- const module = await import('../server.js');
610
- // @ts-ignore
611
- const { ClaudeCodeServer } = module;
612
- const server = new ClaudeCodeServer();
613
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
614
- // Find the CallToolRequest handler
615
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
616
- const handler = callToolCall[1];
617
- // Test both parameters provided
618
- try {
619
- await handler({
620
- params: {
621
- name: 'claude_code',
622
- arguments: {
623
- prompt: 'test prompt',
624
- prompt_file: '/tmp/prompt.txt',
625
- workFolder: '/tmp'
626
- }
627
- }
628
- });
629
- expect.fail('Should have thrown an error');
630
- }
631
- catch (error) {
632
- expect(error.message).toContain('Cannot specify both prompt and prompt_file');
633
- }
634
- });
635
- it('should reject when neither prompt nor prompt_file are provided', async () => {
636
- mockHomedir.mockReturnValue('/home/user');
637
- mockExistsSync.mockReturnValue(true);
638
- // Set up Server mock
639
- setupServerMock();
640
- const module = await import('../server.js');
641
- // @ts-ignore
642
- const { ClaudeCodeServer } = module;
643
- const server = new ClaudeCodeServer();
644
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
645
- // Find the CallToolRequest handler
646
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find((call) => call[0].name === 'callTool');
647
- const handler = callToolCall[1];
648
- // Test neither parameter provided
649
- try {
650
- await handler({
651
- params: {
652
- name: 'claude_code',
653
- arguments: {
654
- workFolder: '/tmp'
655
- }
656
- }
657
- });
658
- expect.fail('Should have thrown an error');
659
- }
660
- catch (error) {
661
- expect(error.message).toContain('Either prompt or prompt_file must be provided');
662
- }
663
- });
664
- });
665
- });
@@ -1,11 +0,0 @@
1
- // Global test setup
2
- import { beforeAll, afterAll } from 'vitest';
3
- import { getSharedMock, cleanupSharedMock } from './utils/persistent-mock.js';
4
- beforeAll(async () => {
5
- console.error('[TEST SETUP] Creating shared mock for all tests...');
6
- await getSharedMock();
7
- });
8
- afterAll(async () => {
9
- console.error('[TEST SETUP] Cleaning up shared mock...');
10
- await cleanupSharedMock();
11
- });