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