Skip to content

Commit 1a977aa

Browse files
committed
Add comprehensive test suite for MCP server
- Graph operations testing with Neo4j integration - MCP protocol compliance testing - Health server monitoring tests - Mock Neo4j driver with comprehensive record simulation - Real database integration testing with proper setup/teardown - Test utilities and validation frameworks - Mock validation to ensure test integrity
1 parent 0529a23 commit 1a977aa

8 files changed

Lines changed: 2265 additions & 0 deletions

packages/mcp-server/tests/graph-operations.test.ts

Lines changed: 451 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2+
import { startHealthServer } from '../src/health-server';
3+
import { Server } from 'http';
4+
5+
describe('MCP Health Server', () => {
6+
let server: Server;
7+
const testPort = 3129; // Use different port for tests
8+
9+
beforeAll(async () => {
10+
server = startHealthServer(testPort);
11+
// Wait for server to start
12+
await new Promise(resolve => setTimeout(resolve, 100));
13+
});
14+
15+
afterAll(async () => {
16+
if (server) {
17+
server.close();
18+
}
19+
});
20+
21+
describe('Health Endpoint', () => {
22+
it('should return health status', async () => {
23+
const response = await fetch(`http://localhost:${testPort}/health`);
24+
expect(response.ok).toBe(true);
25+
26+
const health = await response.json();
27+
expect(health).toBeDefined();
28+
expect(health.status).toBe('healthy');
29+
expect(health.server).toBe('graphdone-mcp');
30+
expect(health.version).toBe('0.2.1-alpha');
31+
expect(health.capabilities).toBeDefined();
32+
expect(Array.isArray(health.capabilities)).toBe(true);
33+
expect(health.capabilities.length).toBeGreaterThan(0);
34+
});
35+
36+
it('should include all expected capabilities', async () => {
37+
const response = await fetch(`http://localhost:${testPort}/health`);
38+
const health = await response.json();
39+
40+
const expectedCapabilities = [
41+
'browse_graph',
42+
'create_node',
43+
'update_node',
44+
'delete_node',
45+
'create_edge',
46+
'delete_edge',
47+
'get_node_details',
48+
'find_path',
49+
'update_priorities',
50+
'bulk_update_priorities',
51+
'get_priority_insights',
52+
'analyze_graph_health',
53+
'get_bottlenecks',
54+
'bulk_operations',
55+
'get_workload_analysis',
56+
'get_contributor_priorities',
57+
'get_contributor_workload',
58+
'find_contributors_by_project',
59+
'get_project_team',
60+
'get_contributor_expertise',
61+
'get_collaboration_network',
62+
'get_contributor_availability'
63+
];
64+
65+
expectedCapabilities.forEach(capability => {
66+
expect(health.capabilities).toContain(capability);
67+
});
68+
});
69+
70+
it('should return valid timestamps', async () => {
71+
const response = await fetch(`http://localhost:${testPort}/health`);
72+
const health = await response.json();
73+
74+
expect(health.timestamp).toBeDefined();
75+
expect(new Date(health.timestamp).getTime()).not.toBeNaN();
76+
expect(Date.now() - new Date(health.timestamp).getTime()).toBeLessThan(5000); // Within 5 seconds
77+
});
78+
79+
it('should return process information', async () => {
80+
const response = await fetch(`http://localhost:${testPort}/health`);
81+
const health = await response.json();
82+
83+
expect(health.uptime).toBeDefined();
84+
expect(typeof health.uptime).toBe('number');
85+
expect(health.uptime).toBeGreaterThan(0);
86+
87+
expect(health.pid).toBeDefined();
88+
expect(typeof health.pid).toBe('number');
89+
expect(health.pid).toBeGreaterThan(0);
90+
});
91+
});
92+
93+
describe('Status Endpoint', () => {
94+
it('should return status information', async () => {
95+
const response = await fetch(`http://localhost:${testPort}/status`);
96+
expect(response.ok).toBe(true);
97+
98+
const status = await response.json();
99+
expect(status).toBeDefined();
100+
expect(status.active).toBe(true);
101+
expect(status.connectedClients).toBeDefined();
102+
expect(status.totalRequests).toBeDefined();
103+
expect(status.neo4j).toBeDefined();
104+
expect(status.neo4j.connected).toBeDefined();
105+
expect(status.neo4j.uri).toBeDefined();
106+
});
107+
});
108+
109+
describe('CORS Headers', () => {
110+
it('should include CORS headers', async () => {
111+
const response = await fetch(`http://localhost:${testPort}/health`);
112+
113+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*');
114+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe('GET, OPTIONS');
115+
expect(response.headers.get('Access-Control-Allow-Headers')).toBe('Content-Type');
116+
});
117+
118+
it('should handle OPTIONS requests', async () => {
119+
const response = await fetch(`http://localhost:${testPort}/health`, {
120+
method: 'OPTIONS'
121+
});
122+
123+
expect(response.status).toBe(200);
124+
});
125+
});
126+
127+
describe('Error Handling', () => {
128+
it('should return 404 for unknown endpoints', async () => {
129+
const response = await fetch(`http://localhost:${testPort}/unknown`);
130+
expect(response.status).toBe(404);
131+
132+
const error = await response.json();
133+
expect(error.error).toBe('Not found');
134+
});
135+
136+
it('should handle malformed requests gracefully', async () => {
137+
const response = await fetch(`http://localhost:${testPort}/health`, {
138+
method: 'POST',
139+
body: 'invalid-json'
140+
});
141+
142+
// Should still respond to POST on health endpoint
143+
expect(response.status).toBe(404);
144+
});
145+
});
146+
147+
describe('Performance', () => {
148+
it('should respond to health checks quickly', async () => {
149+
const start = Date.now();
150+
const response = await fetch(`http://localhost:${testPort}/health`);
151+
const duration = Date.now() - start;
152+
153+
expect(response.ok).toBe(true);
154+
expect(duration).toBeLessThan(1000); // Should respond within 1 second
155+
});
156+
157+
it('should handle concurrent requests', async () => {
158+
const requests = Array(10).fill(0).map(() =>
159+
fetch(`http://localhost:${testPort}/health`)
160+
);
161+
162+
const responses = await Promise.all(requests);
163+
164+
responses.forEach(response => {
165+
expect(response.ok).toBe(true);
166+
});
167+
});
168+
});
169+
170+
describe('Content Types', () => {
171+
it('should return JSON content type', async () => {
172+
const response = await fetch(`http://localhost:${testPort}/health`);
173+
174+
expect(response.headers.get('Content-Type')).toBe('application/json');
175+
});
176+
177+
it('should return properly formatted JSON', async () => {
178+
const response = await fetch(`http://localhost:${testPort}/health`);
179+
const text = await response.text();
180+
181+
expect(() => JSON.parse(text)).not.toThrow();
182+
const parsed = JSON.parse(text);
183+
expect(typeof parsed).toBe('object');
184+
});
185+
});
186+
});

0 commit comments

Comments
 (0)