Skip to content

Commit 865086f

Browse files
test cases for resource visualization
1 parent e8a75a7 commit 865086f

File tree

1 file changed

+298
-0
lines changed

1 file changed

+298
-0
lines changed
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2+
import { execa } from 'execa';
3+
import fs from 'fs';
4+
import path from 'path';
5+
import http from 'http';
6+
7+
describe('Resource Monitoring Integration', () => {
8+
const testWorkspace = path.join(process.cwd(), 'test-workspace', 'integration-test');
9+
let adminProcess = null;
10+
11+
beforeEach(async () => {
12+
// Clean up previous test workspace
13+
if (fs.existsSync(testWorkspace)) {
14+
fs.rmSync(testWorkspace, { recursive: true, force: true });
15+
}
16+
17+
// Create test workspace
18+
fs.mkdirSync(testWorkspace, { recursive: true });
19+
20+
// Create a simple polyglot project
21+
const polyglotConfig = {
22+
name: 'integration-test',
23+
services: [
24+
{ name: 'test-node', type: 'node', port: 4001, path: 'services/test-node' }
25+
]
26+
};
27+
28+
fs.writeFileSync(
29+
path.join(testWorkspace, 'polyglot.json'),
30+
JSON.stringify(polyglotConfig, null, 2)
31+
);
32+
33+
// Create service directory structure
34+
const serviceDir = path.join(testWorkspace, 'services', 'test-node');
35+
fs.mkdirSync(serviceDir, { recursive: true });
36+
37+
// Create basic package.json for the service
38+
const packageJson = {
39+
name: 'test-node',
40+
version: '1.0.0',
41+
scripts: {
42+
dev: 'node index.js',
43+
start: 'node index.js'
44+
},
45+
dependencies: {}
46+
};
47+
48+
fs.writeFileSync(
49+
path.join(serviceDir, 'package.json'),
50+
JSON.stringify(packageJson, null, 2)
51+
);
52+
53+
// Create basic service file
54+
const serviceCode = `
55+
const http = require('http');
56+
const port = process.env.PORT || 4001;
57+
58+
const server = http.createServer((req, res) => {
59+
if (req.url === '/health') {
60+
res.writeHead(200, { 'Content-Type': 'application/json' });
61+
res.end(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() }));
62+
} else {
63+
res.writeHead(200, { 'Content-Type': 'text/plain' });
64+
res.end('Test Node Service Running');
65+
}
66+
});
67+
68+
server.listen(port, () => {
69+
console.log(\`Test service running on port \${port}\`);
70+
});
71+
72+
process.on('SIGTERM', () => {
73+
console.log('Received SIGTERM, shutting down gracefully');
74+
server.close(() => {
75+
process.exit(0);
76+
});
77+
});
78+
`;
79+
80+
fs.writeFileSync(path.join(serviceDir, 'index.js'), serviceCode);
81+
});
82+
83+
afterEach(async () => {
84+
// Stop admin dashboard if running
85+
if (adminProcess && !adminProcess.killed) {
86+
try {
87+
adminProcess.kill('SIGTERM');
88+
// Give the process time to shut down gracefully
89+
await Promise.race([
90+
adminProcess.catch(() => {}), // Ignore errors during cleanup
91+
new Promise(resolve => setTimeout(resolve, 2000))
92+
]);
93+
} catch (error) {
94+
// Force kill if graceful shutdown fails
95+
try {
96+
adminProcess.kill('SIGKILL');
97+
} catch (e) {
98+
// Ignore errors during force kill
99+
}
100+
}
101+
}
102+
adminProcess = null;
103+
104+
// Clean up test workspace
105+
if (fs.existsSync(testWorkspace)) {
106+
fs.rmSync(testWorkspace, { recursive: true, force: true });
107+
}
108+
});
109+
110+
it('should start admin dashboard with resource monitoring enabled', async () => {
111+
// Start admin dashboard
112+
adminProcess = execa('node', [
113+
path.join(process.cwd(), 'bin', 'index.js'),
114+
'admin',
115+
'--port', '9999'
116+
], {
117+
cwd: testWorkspace,
118+
stdio: 'pipe'
119+
});
120+
121+
// Wait for dashboard to start
122+
await new Promise(resolve => setTimeout(resolve, 3000));
123+
124+
expect(adminProcess.killed).toBe(false);
125+
126+
// Test if dashboard is responding
127+
const response = await makeRequest('GET', 'http://localhost:9999/', {}, 5000);
128+
expect(response.statusCode).toBe(200);
129+
expect(response.body).toContain('Polyglot Admin Dashboard');
130+
131+
// Test if resource monitoring UI is included
132+
expect(response.body).toContain('Resource Monitoring');
133+
expect(response.body).toContain('CPU Usage');
134+
expect(response.body).toContain('Memory Usage');
135+
expect(response.body).toContain('Network I/O');
136+
expect(response.body).toContain('Disk I/O');
137+
}, 15000);
138+
139+
it('should provide metrics API endpoint', async () => {
140+
// Start admin dashboard
141+
adminProcess = execa('node', [
142+
path.join(process.cwd(), 'bin', 'index.js'),
143+
'admin',
144+
'--port', '9998'
145+
], {
146+
cwd: testWorkspace,
147+
stdio: 'pipe'
148+
});
149+
150+
// Wait for dashboard to start
151+
await new Promise(resolve => setTimeout(resolve, 3000));
152+
153+
// Test metrics API endpoint
154+
const response = await makeRequest('GET', 'http://localhost:9998/api/metrics');
155+
expect(response.statusCode).toBe(200);
156+
157+
const data = JSON.parse(response.body);
158+
expect(data).toHaveProperty('metrics');
159+
expect(data).toHaveProperty('systemInfo');
160+
161+
if (data.systemInfo) {
162+
expect(data.systemInfo).toHaveProperty('cpu');
163+
expect(data.systemInfo).toHaveProperty('memory');
164+
}
165+
}, 15000);
166+
167+
it('should provide service status API with resource monitoring integration', async () => {
168+
// Start admin dashboard
169+
adminProcess = execa('node', [
170+
path.join(process.cwd(), 'bin', 'index.js'),
171+
'admin',
172+
'--port', '9997'
173+
], {
174+
cwd: testWorkspace,
175+
stdio: 'pipe'
176+
});
177+
178+
// Wait for dashboard to start
179+
await new Promise(resolve => setTimeout(resolve, 3000));
180+
181+
// Test service status API
182+
const response = await makeRequest('GET', 'http://localhost:9997/api/status');
183+
expect(response.statusCode).toBe(200);
184+
185+
const services = JSON.parse(response.body);
186+
expect(Array.isArray(services)).toBe(true);
187+
expect(services.length).toBeGreaterThan(0);
188+
189+
const service = services[0];
190+
expect(service).toHaveProperty('name');
191+
expect(service).toHaveProperty('type');
192+
expect(service).toHaveProperty('port');
193+
expect(service).toHaveProperty('status');
194+
}, 15000);
195+
196+
it('should handle graceful shutdown without errors', async () => {
197+
// Start admin dashboard
198+
adminProcess = execa('node', [
199+
path.join(process.cwd(), 'bin', 'index.js'),
200+
'admin',
201+
'--port', '9996'
202+
], {
203+
cwd: testWorkspace,
204+
stdio: 'pipe'
205+
});
206+
207+
// Wait for dashboard to start
208+
await new Promise(resolve => setTimeout(resolve, 3000));
209+
210+
// Send SIGTERM to gracefully shut down
211+
adminProcess.kill('SIGTERM');
212+
213+
// Wait for process to exit with timeout
214+
try {
215+
const result = await Promise.race([
216+
adminProcess,
217+
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 10000))
218+
]);
219+
220+
// Process should exit (either gracefully or terminated)
221+
expect(adminProcess.killed || result.exitCode !== undefined).toBe(true);
222+
} catch (error) {
223+
// If timeout or termination, that's acceptable in test environment
224+
expect(error.message === 'Timeout' || error.signal === 'SIGTERM' || error.signal === 'SIGKILL').toBe(true);
225+
}
226+
}, 15000);
227+
228+
it('should include Chart.js library for metrics visualization', async () => {
229+
// Start admin dashboard
230+
adminProcess = execa('node', [
231+
path.join(process.cwd(), 'bin', 'index.js'),
232+
'admin',
233+
'--port', '9995'
234+
], {
235+
cwd: testWorkspace,
236+
stdio: 'pipe'
237+
});
238+
239+
// Wait for dashboard to start
240+
await new Promise(resolve => setTimeout(resolve, 3000));
241+
242+
// Test if Chart.js is included in the HTML
243+
const response = await makeRequest('GET', 'http://localhost:9995/');
244+
expect(response.statusCode).toBe(200);
245+
expect(response.body).toContain('chart.js');
246+
expect(response.body).toContain('canvas id="cpu-chart"');
247+
expect(response.body).toContain('canvas id="memory-chart"');
248+
expect(response.body).toContain('canvas id="network-chart"');
249+
expect(response.body).toContain('canvas id="disk-chart"');
250+
}, 15000);
251+
});
252+
253+
// Helper function to make HTTP requests with timeout
254+
function makeRequest(method, url, data = null, timeout = 10000) {
255+
return new Promise((resolve, reject) => {
256+
const urlObj = new URL(url);
257+
const options = {
258+
hostname: urlObj.hostname,
259+
port: urlObj.port,
260+
path: urlObj.pathname + urlObj.search,
261+
method: method,
262+
timeout: timeout,
263+
headers: {
264+
'User-Agent': 'test-client',
265+
}
266+
};
267+
268+
if (data && method !== 'GET') {
269+
const postData = typeof data === 'string' ? data : JSON.stringify(data);
270+
options.headers['Content-Type'] = 'application/json';
271+
options.headers['Content-Length'] = Buffer.byteLength(postData);
272+
}
273+
274+
const req = http.request(options, (res) => {
275+
let body = '';
276+
res.on('data', (chunk) => body += chunk);
277+
res.on('end', () => {
278+
resolve({
279+
statusCode: res.statusCode,
280+
headers: res.headers,
281+
body: body
282+
});
283+
});
284+
});
285+
286+
req.on('error', reject);
287+
req.on('timeout', () => {
288+
req.destroy();
289+
reject(new Error(`Request timeout after ${timeout}ms`));
290+
});
291+
292+
if (data && method !== 'GET') {
293+
req.write(typeof data === 'string' ? data : JSON.stringify(data));
294+
}
295+
296+
req.end();
297+
});
298+
}

0 commit comments

Comments
 (0)