Skip to content

Commit 150cc82

Browse files
committed
Inject stopLogCapture for unit tests
1 parent 2560ace commit 150cc82

2 files changed

Lines changed: 115 additions & 127 deletions

File tree

src/mcp/tools/logging/__tests__/stop_sim_log_cap.test.ts

Lines changed: 102 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,17 @@
1111
* Converted to pure dependency injection without vitest mocking.
1212
*/
1313

14-
import { describe, it, expect, beforeEach } from 'vitest';
14+
import { describe, it, expect } from 'vitest';
1515
import * as z from 'zod';
1616
import stopSimLogCap, { stop_sim_log_capLogic } from '../stop_sim_log_cap.ts';
17-
import { activeLogSessions } from '../../../../utils/log_capture.ts';
18-
import { createMockFileSystemExecutor } from '../../../../test-utils/mock-executors.ts';
17+
import {
18+
createMockExecutor,
19+
createMockFileSystemExecutor,
20+
} from '../../../../test-utils/mock-executors.ts';
1921

2022
describe('stop_sim_log_cap plugin', () => {
21-
beforeEach(() => {
22-
// Clear any active sessions before each test
23-
activeLogSessions.clear();
24-
});
25-
26-
// Helper function to create a test log session
27-
async function createTestLogSession(sessionId: string, logContent: string = '') {
28-
const mockProcess = {
29-
pid: 12345,
30-
killed: false,
31-
exitCode: null,
32-
kill: () => {},
33-
};
34-
35-
const logFilePath = `/tmp/xcodemcp_sim_log_test_${sessionId}.log`;
36-
const fileSystem = createMockFileSystemExecutor({
37-
existsSync: (path) => path === logFilePath,
38-
readFile: async (path, _encoding) => {
39-
if (path !== logFilePath) {
40-
throw new Error(`ENOENT: no such file or directory, open '${path}'`);
41-
}
42-
return logContent;
43-
},
44-
});
45-
46-
activeLogSessions.set(sessionId, {
47-
processes: [mockProcess as any],
48-
logFilePath: logFilePath,
49-
simulatorUuid: 'test-simulator-uuid',
50-
bundleId: 'com.example.TestApp',
51-
});
52-
53-
return { fileSystem, logFilePath };
54-
}
23+
const mockExecutor = createMockExecutor({ success: true, output: '' });
24+
const mockFileSystem = createMockFileSystemExecutor();
5525

5626
describe('Export Field Validation (Literal)', () => {
5727
it('should have correct plugin structure', () => {
@@ -95,13 +65,18 @@ describe('stop_sim_log_cap plugin', () => {
9565
it('should handle null logSessionId (validation handled by framework)', async () => {
9666
// With typed tool factory, invalid params won't reach the logic function
9767
// This test now validates that the logic function works with valid empty strings
98-
const { fileSystem } = await createTestLogSession('', 'Log content for empty session');
68+
const stopLogCaptureStub = async () => ({
69+
logContent: 'Log content for empty session',
70+
error: undefined,
71+
});
9972

10073
const result = await stop_sim_log_capLogic(
10174
{
10275
logSessionId: '',
10376
},
104-
fileSystem,
77+
mockExecutor,
78+
stopLogCaptureStub,
79+
mockFileSystem,
10580
);
10681

10782
expect(result.isError).toBeUndefined();
@@ -113,13 +88,18 @@ describe('stop_sim_log_cap plugin', () => {
11388
it('should handle undefined logSessionId (validation handled by framework)', async () => {
11489
// With typed tool factory, invalid params won't reach the logic function
11590
// This test now validates that the logic function works with valid empty strings
116-
const { fileSystem } = await createTestLogSession('', 'Log content for empty session');
91+
const stopLogCaptureStub = async () => ({
92+
logContent: 'Log content for empty session',
93+
error: undefined,
94+
});
11795

11896
const result = await stop_sim_log_capLogic(
11997
{
12098
logSessionId: '',
12199
},
122-
fileSystem,
100+
mockExecutor,
101+
stopLogCaptureStub,
102+
mockFileSystem,
123103
);
124104

125105
expect(result.isError).toBeUndefined();
@@ -129,13 +109,18 @@ describe('stop_sim_log_cap plugin', () => {
129109
});
130110

131111
it('should handle empty string logSessionId', async () => {
132-
const { fileSystem } = await createTestLogSession('', 'Log content for empty session');
112+
const stopLogCaptureStub = async () => ({
113+
logContent: 'Log content for empty session',
114+
error: undefined,
115+
});
133116

134117
const result = await stop_sim_log_capLogic(
135118
{
136119
logSessionId: '',
137120
},
138-
fileSystem,
121+
mockExecutor,
122+
stopLogCaptureStub,
123+
mockFileSystem,
139124
);
140125

141126
expect(result.isError).toBeUndefined();
@@ -147,37 +132,45 @@ describe('stop_sim_log_cap plugin', () => {
147132

148133
describe('Function Call Generation', () => {
149134
it('should call stopLogCapture with correct parameters', async () => {
150-
const { fileSystem } = await createTestLogSession(
151-
'test-session-id',
152-
'Mock log content from file',
153-
);
135+
let capturedSessionId = '';
136+
const stopLogCaptureStub = async (logSessionId: string) => {
137+
capturedSessionId = logSessionId;
138+
return { logContent: 'Mock log content from file', error: undefined };
139+
};
154140

155141
const result = await stop_sim_log_capLogic(
156142
{
157143
logSessionId: 'test-session-id',
158144
},
159-
fileSystem,
145+
mockExecutor,
146+
stopLogCaptureStub,
147+
mockFileSystem,
160148
);
161149

150+
expect(capturedSessionId).toBe('test-session-id');
162151
expect(result.isError).toBeUndefined();
163152
expect(result.content[0].text).toBe(
164153
'Log capture session test-session-id stopped successfully. Log content follows:\n\nMock log content from file',
165154
);
166155
});
167156

168157
it('should call stopLogCapture with different session ID', async () => {
169-
const { fileSystem } = await createTestLogSession(
170-
'different-session-id',
171-
'Different log content',
172-
);
158+
let capturedSessionId = '';
159+
const stopLogCaptureStub = async (logSessionId: string) => {
160+
capturedSessionId = logSessionId;
161+
return { logContent: 'Different log content', error: undefined };
162+
};
173163

174164
const result = await stop_sim_log_capLogic(
175165
{
176166
logSessionId: 'different-session-id',
177167
},
178-
fileSystem,
168+
mockExecutor,
169+
stopLogCaptureStub,
170+
mockFileSystem,
179171
);
180172

173+
expect(capturedSessionId).toBe('different-session-id');
181174
expect(result.isError).toBeUndefined();
182175
expect(result.content[0].text).toBe(
183176
'Log capture session different-session-id stopped successfully. Log content follows:\n\nDifferent log content',
@@ -187,16 +180,18 @@ describe('stop_sim_log_cap plugin', () => {
187180

188181
describe('Response Processing', () => {
189182
it('should handle successful log capture stop', async () => {
190-
const { fileSystem } = await createTestLogSession(
191-
'test-session-id',
192-
'Mock log content from file',
193-
);
183+
const stopLogCaptureStub = async () => ({
184+
logContent: 'Mock log content from file',
185+
error: undefined,
186+
});
194187

195188
const result = await stop_sim_log_capLogic(
196189
{
197190
logSessionId: 'test-session-id',
198191
},
199-
fileSystem,
192+
mockExecutor,
193+
stopLogCaptureStub,
194+
mockFileSystem,
200195
);
201196

202197
expect(result.isError).toBeUndefined();
@@ -206,13 +201,18 @@ describe('stop_sim_log_cap plugin', () => {
206201
});
207202

208203
it('should handle empty log content', async () => {
209-
const { fileSystem } = await createTestLogSession('test-session-id', '');
204+
const stopLogCaptureStub = async () => ({
205+
logContent: '',
206+
error: undefined,
207+
});
210208

211209
const result = await stop_sim_log_capLogic(
212210
{
213211
logSessionId: 'test-session-id',
214212
},
215-
fileSystem,
213+
mockExecutor,
214+
stopLogCaptureStub,
215+
mockFileSystem,
216216
);
217217

218218
expect(result.isError).toBeUndefined();
@@ -222,16 +222,18 @@ describe('stop_sim_log_cap plugin', () => {
222222
});
223223

224224
it('should handle multiline log content', async () => {
225-
const { fileSystem } = await createTestLogSession(
226-
'test-session-id',
227-
'Line 1\nLine 2\nLine 3',
228-
);
225+
const stopLogCaptureStub = async () => ({
226+
logContent: 'Line 1\nLine 2\nLine 3',
227+
error: undefined,
228+
});
229229

230230
const result = await stop_sim_log_capLogic(
231231
{
232232
logSessionId: 'test-session-id',
233233
},
234-
fileSystem,
234+
mockExecutor,
235+
stopLogCaptureStub,
236+
mockFileSystem,
235237
);
236238

237239
expect(result.isError).toBeUndefined();
@@ -241,11 +243,18 @@ describe('stop_sim_log_cap plugin', () => {
241243
});
242244

243245
it('should handle log capture stop errors for non-existent session', async () => {
246+
const stopLogCaptureStub = async () => ({
247+
logContent: '',
248+
error: 'Log capture session not found: non-existent-session',
249+
});
250+
244251
const result = await stop_sim_log_capLogic(
245252
{
246253
logSessionId: 'non-existent-session',
247254
},
248-
createMockFileSystemExecutor(),
255+
mockExecutor,
256+
stopLogCaptureStub,
257+
mockFileSystem,
249258
);
250259

251260
expect(result.isError).toBe(true);
@@ -255,26 +264,18 @@ describe('stop_sim_log_cap plugin', () => {
255264
});
256265

257266
it('should handle file read errors', async () => {
258-
// Create session but make file reading fail in the log_capture utility
259-
const mockProcess = {
260-
pid: 12345,
261-
killed: false,
262-
exitCode: null,
263-
kill: () => {},
264-
};
265-
266-
activeLogSessions.set('test-session-id', {
267-
processes: [mockProcess as any],
268-
logFilePath: `/tmp/test_file_not_found.log`,
269-
simulatorUuid: 'test-simulator-uuid',
270-
bundleId: 'com.example.TestApp',
267+
const stopLogCaptureStub = async () => ({
268+
logContent: '',
269+
error: 'ENOENT: no such file or directory',
271270
});
272271

273272
const result = await stop_sim_log_capLogic(
274-
{ logSessionId: 'test-session-id' },
275-
createMockFileSystemExecutor({
276-
existsSync: () => false,
277-
}),
273+
{
274+
logSessionId: 'test-session-id',
275+
},
276+
mockExecutor,
277+
stopLogCaptureStub,
278+
mockFileSystem,
278279
);
279280

280281
expect(result.isError).toBe(true);
@@ -284,29 +285,18 @@ describe('stop_sim_log_cap plugin', () => {
284285
});
285286

286287
it('should handle permission errors', async () => {
287-
// Create session but make file reading fail in the log_capture utility
288-
const mockProcess = {
289-
pid: 12345,
290-
killed: false,
291-
exitCode: null,
292-
kill: () => {},
293-
};
294-
295-
activeLogSessions.set('test-session-id', {
296-
processes: [mockProcess as any],
297-
logFilePath: `/tmp/test_permission_denied.log`,
298-
simulatorUuid: 'test-simulator-uuid',
299-
bundleId: 'com.example.TestApp',
288+
const stopLogCaptureStub = async () => ({
289+
logContent: '',
290+
error: 'EACCES: permission denied',
300291
});
301292

302293
const result = await stop_sim_log_capLogic(
303-
{ logSessionId: 'test-session-id' },
304-
createMockFileSystemExecutor({
305-
existsSync: () => true,
306-
readFile: async () => {
307-
throw new Error('Permission denied');
308-
},
309-
}),
294+
{
295+
logSessionId: 'test-session-id',
296+
},
297+
mockExecutor,
298+
stopLogCaptureStub,
299+
mockFileSystem,
310300
);
311301

312302
expect(result.isError).toBe(true);
@@ -316,29 +306,18 @@ describe('stop_sim_log_cap plugin', () => {
316306
});
317307

318308
it('should handle various error types', async () => {
319-
// Create session but make file reading fail in the log_capture utility
320-
const mockProcess = {
321-
pid: 12345,
322-
killed: false,
323-
exitCode: null,
324-
kill: () => {},
325-
};
326-
327-
activeLogSessions.set('test-session-id', {
328-
processes: [mockProcess as any],
329-
logFilePath: `/tmp/test_generic_error.log`,
330-
simulatorUuid: 'test-simulator-uuid',
331-
bundleId: 'com.example.TestApp',
309+
const stopLogCaptureStub = async () => ({
310+
logContent: '',
311+
error: 'Unexpected error',
332312
});
333313

334314
const result = await stop_sim_log_capLogic(
335-
{ logSessionId: 'test-session-id' },
336-
createMockFileSystemExecutor({
337-
existsSync: () => true,
338-
readFile: async () => {
339-
throw new Error('Something went wrong');
340-
},
341-
}),
315+
{
316+
logSessionId: 'test-session-id',
317+
},
318+
mockExecutor,
319+
stopLogCaptureStub,
320+
mockFileSystem,
342321
);
343322

344323
expect(result.isError).toBe(true);

0 commit comments

Comments
 (0)