Skip to content

Commit f5660b6

Browse files
committed
refactor: Add test helper to decouple tests from internal implementation
- Add invokeToolForTest helper function in mcp-server.ts - Replace all direct _requestHandlers access with helper function - Remove unused MockAuthenticationError class - Clean up test file imports and eslint directives - Tests are now resilient to internal MCP Server refactors This improves test maintainability by isolating internal property access to a single helper function instead of scattering it across all test cases.
1 parent ce475b6 commit f5660b6

File tree

2 files changed

+94
-216
lines changed

2 files changed

+94
-216
lines changed

src/__tests__/read-only-mode.test.ts

Lines changed: 56 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
* Tests the enforcement of read-only mode when calling create_deployment
44
*/
55

6-
/* eslint-disable @typescript-eslint/no-explicit-any, no-unused-vars */
7-
86
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
9-
import { createMCPServer } from '../mcp-server.js';
7+
import { createMCPServer, invokeToolForTest } from '../mcp-server.js';
108
import type { ServerConfig } from '../config.js';
11-
import type { CallToolRequest } from '@modelcontextprotocol/sdk/types.js';
129

1310
// Mock the DeployHQClient to avoid making real API calls
1411
vi.mock('../api-client.js', () => {
@@ -25,21 +22,14 @@ vi.mock('../api-client.js', () => {
2522
getDeploymentLog = vi.fn().mockResolvedValue('');
2623
validateCredentials = vi.fn().mockResolvedValue(undefined);
2724

25+
// eslint-disable-next-line no-unused-vars
2826
constructor(_config: { username: string; password: string; account: string }) {
2927
// Mock constructor - config param prefixed with _ to indicate intentionally unused
3028
}
3129
}
3230

33-
class MockAuthenticationError extends Error {
34-
constructor(message: string) {
35-
super(message);
36-
this.name = 'AuthenticationError';
37-
}
38-
}
39-
4031
return {
4132
DeployHQClient: MockDeployHQClient,
42-
AuthenticationError: MockAuthenticationError,
4333
};
4434
});
4535

@@ -65,26 +55,12 @@ describe('Read-Only Mode Integration', () => {
6555
config
6656
);
6757

68-
const request: CallToolRequest = {
69-
method: 'tools/call',
70-
params: {
71-
name: 'create_deployment',
72-
arguments: {
73-
project: 'test-project',
74-
parent_identifier: 'server-uuid',
75-
start_revision: 'abc123',
76-
end_revision: 'def456',
77-
},
78-
},
79-
};
80-
81-
// Get the handler for CallToolRequest
82-
const handlers = (server as any)._requestHandlers;
83-
const handler = handlers.get('tools/call');
84-
85-
expect(handler).toBeDefined();
86-
87-
const response = await handler(request);
58+
const response = await invokeToolForTest(server, 'create_deployment', {
59+
project: 'test-project',
60+
parent_identifier: 'server-uuid',
61+
start_revision: 'abc123',
62+
end_revision: 'def456',
63+
});
8864

8965
// Should return an error response
9066
expect(response.isError).toBe(true);
@@ -102,26 +78,12 @@ describe('Read-Only Mode Integration', () => {
10278
config
10379
);
10480

105-
const request: CallToolRequest = {
106-
method: 'tools/call',
107-
params: {
108-
name: 'create_deployment',
109-
arguments: {
110-
project: 'test-project',
111-
parent_identifier: 'server-uuid',
112-
start_revision: 'abc123',
113-
end_revision: 'def456',
114-
},
115-
},
116-
};
117-
118-
// Get the handler for CallToolRequest
119-
const handlers = (server as any)._requestHandlers;
120-
const handler = handlers.get('tools/call');
121-
122-
expect(handler).toBeDefined();
123-
124-
const response = await handler(request);
81+
const response = await invokeToolForTest(server, 'create_deployment', {
82+
project: 'test-project',
83+
parent_identifier: 'server-uuid',
84+
start_revision: 'abc123',
85+
end_revision: 'def456',
86+
});
12587

12688
// Should return a successful response
12789
expect(response.isError).toBeUndefined();
@@ -137,26 +99,12 @@ describe('Read-Only Mode Integration', () => {
13799
// No config provided - should default to read-only
138100
);
139101

140-
const request: CallToolRequest = {
141-
method: 'tools/call',
142-
params: {
143-
name: 'create_deployment',
144-
arguments: {
145-
project: 'test-project',
146-
parent_identifier: 'server-uuid',
147-
start_revision: 'abc123',
148-
end_revision: 'def456',
149-
},
150-
},
151-
};
152-
153-
// Get the handler for CallToolRequest
154-
const handlers = (server as any)._requestHandlers;
155-
const handler = handlers.get('tools/call');
156-
157-
expect(handler).toBeDefined();
158-
159-
const response = await handler(request);
102+
const response = await invokeToolForTest(server, 'create_deployment', {
103+
project: 'test-project',
104+
parent_identifier: 'server-uuid',
105+
start_revision: 'abc123',
106+
end_revision: 'def456',
107+
});
160108

161109
// Should return an error response (read-only by default)
162110
expect(response.isError).toBe(true);
@@ -172,24 +120,12 @@ describe('Read-Only Mode Integration', () => {
172120
config
173121
);
174122

175-
const request: CallToolRequest = {
176-
method: 'tools/call',
177-
params: {
178-
name: 'create_deployment',
179-
arguments: {
180-
project: 'test-project',
181-
parent_identifier: 'server-uuid',
182-
start_revision: 'abc123',
183-
end_revision: 'def456',
184-
},
185-
},
186-
};
187-
188-
// Get the handler for CallToolRequest
189-
const handlers = (server as any)._requestHandlers;
190-
const handler = handlers.get('tools/call');
191-
192-
const response = await handler(request);
123+
const response = await invokeToolForTest(server, 'create_deployment', {
124+
project: 'test-project',
125+
parent_identifier: 'server-uuid',
126+
start_revision: 'abc123',
127+
end_revision: 'def456',
128+
});
193129

194130
const errorText = response.content[0].text;
195131

@@ -213,19 +149,7 @@ describe('Read-Only Mode Integration', () => {
213149
config
214150
);
215151

216-
const request: CallToolRequest = {
217-
method: 'tools/call',
218-
params: {
219-
name: 'list_projects',
220-
arguments: {},
221-
},
222-
};
223-
224-
// Get the handler for CallToolRequest
225-
const handlers = (server as any)._requestHandlers;
226-
const handler = handlers.get('tools/call');
227-
228-
const response = await handler(request);
152+
const response = await invokeToolForTest(server, 'list_projects', {});
229153

230154
// Should work normally
231155
expect(response.isError).toBeUndefined();
@@ -240,21 +164,9 @@ describe('Read-Only Mode Integration', () => {
240164
config
241165
);
242166

243-
const request: CallToolRequest = {
244-
method: 'tools/call',
245-
params: {
246-
name: 'get_project',
247-
arguments: {
248-
permalink: 'test-project',
249-
},
250-
},
251-
};
252-
253-
// Get the handler for CallToolRequest
254-
const handlers = (server as any)._requestHandlers;
255-
const handler = handlers.get('tools/call');
256-
257-
const response = await handler(request);
167+
const response = await invokeToolForTest(server, 'get_project', {
168+
permalink: 'test-project',
169+
});
258170

259171
// Should work normally
260172
expect(response.isError).toBeUndefined();
@@ -269,21 +181,9 @@ describe('Read-Only Mode Integration', () => {
269181
config
270182
);
271183

272-
const request: CallToolRequest = {
273-
method: 'tools/call',
274-
params: {
275-
name: 'list_servers',
276-
arguments: {
277-
project: 'test-project',
278-
},
279-
},
280-
};
281-
282-
// Get the handler for CallToolRequest
283-
const handlers = (server as any)._requestHandlers;
284-
const handler = handlers.get('tools/call');
285-
286-
const response = await handler(request);
184+
const response = await invokeToolForTest(server, 'list_servers', {
185+
project: 'test-project',
186+
});
287187

288188
// Should work normally
289189
expect(response.isError).toBeUndefined();
@@ -298,21 +198,9 @@ describe('Read-Only Mode Integration', () => {
298198
config
299199
);
300200

301-
const request: CallToolRequest = {
302-
method: 'tools/call',
303-
params: {
304-
name: 'list_deployments',
305-
arguments: {
306-
project: 'test-project',
307-
},
308-
},
309-
};
310-
311-
// Get the handler for CallToolRequest
312-
const handlers = (server as any)._requestHandlers;
313-
const handler = handlers.get('tools/call');
314-
315-
const response = await handler(request);
201+
const response = await invokeToolForTest(server, 'list_deployments', {
202+
project: 'test-project',
203+
});
316204

317205
// Should work normally
318206
expect(response.isError).toBeUndefined();
@@ -327,22 +215,10 @@ describe('Read-Only Mode Integration', () => {
327215
config
328216
);
329217

330-
const request: CallToolRequest = {
331-
method: 'tools/call',
332-
params: {
333-
name: 'get_deployment',
334-
arguments: {
335-
project: 'test-project',
336-
uuid: 'deployment-uuid',
337-
},
338-
},
339-
};
340-
341-
// Get the handler for CallToolRequest
342-
const handlers = (server as any)._requestHandlers;
343-
const handler = handlers.get('tools/call');
344-
345-
const response = await handler(request);
218+
const response = await invokeToolForTest(server, 'get_deployment', {
219+
project: 'test-project',
220+
uuid: 'deployment-uuid',
221+
});
346222

347223
// Should work normally
348224
expect(response.isError).toBeUndefined();
@@ -357,22 +233,10 @@ describe('Read-Only Mode Integration', () => {
357233
config
358234
);
359235

360-
const request: CallToolRequest = {
361-
method: 'tools/call',
362-
params: {
363-
name: 'get_deployment_log',
364-
arguments: {
365-
project: 'test-project',
366-
uuid: 'deployment-uuid',
367-
},
368-
},
369-
};
370-
371-
// Get the handler for CallToolRequest
372-
const handlers = (server as any)._requestHandlers;
373-
const handler = handlers.get('tools/call');
374-
375-
const response = await handler(request);
236+
const response = await invokeToolForTest(server, 'get_deployment_log', {
237+
project: 'test-project',
238+
uuid: 'deployment-uuid',
239+
});
376240

377241
// Should work normally
378242
expect(response.isError).toBeUndefined();
@@ -389,24 +253,12 @@ describe('Read-Only Mode Integration', () => {
389253
config
390254
);
391255

392-
const request: CallToolRequest = {
393-
method: 'tools/call',
394-
params: {
395-
name: 'create_deployment',
396-
arguments: {
397-
project: 'test-project',
398-
parent_identifier: 'server-uuid',
399-
start_revision: 'abc123',
400-
end_revision: 'def456',
401-
},
402-
},
403-
};
404-
405-
// Get the handler for CallToolRequest
406-
const handlers = (server as any)._requestHandlers;
407-
const handler = handlers.get('tools/call');
408-
409-
const response = await handler(request);
256+
const response = await invokeToolForTest(server, 'create_deployment', {
257+
project: 'test-project',
258+
parent_identifier: 'server-uuid',
259+
start_revision: 'abc123',
260+
end_revision: 'def456',
261+
});
410262

411263
expect(response.isError).toBe(true);
412264
expect(response.content).toBeDefined();
@@ -429,24 +281,12 @@ describe('Read-Only Mode Integration', () => {
429281
config
430282
);
431283

432-
const request: CallToolRequest = {
433-
method: 'tools/call',
434-
params: {
435-
name: 'create_deployment',
436-
arguments: {
437-
project: 'test-project',
438-
parent_identifier: 'server-uuid',
439-
start_revision: 'abc123',
440-
end_revision: 'def456',
441-
},
442-
},
443-
};
444-
445-
// Get the handler for CallToolRequest
446-
const handlers = (server as any)._requestHandlers;
447-
const handler = handlers.get('tools/call');
448-
449-
const response = await handler(request);
284+
const response = await invokeToolForTest(server, 'create_deployment', {
285+
project: 'test-project',
286+
parent_identifier: 'server-uuid',
287+
start_revision: 'abc123',
288+
end_revision: 'def456',
289+
});
450290

451291
const errorData = JSON.parse(response.content[0].text);
452292

0 commit comments

Comments
 (0)