Committed by Hermes autonomous QA sprint 2026-08-13. These files were modified during the Aug 12 sprint but never committed.
138 lines
3.6 KiB
JavaScript
138 lines
3.6 KiB
JavaScript
/**
|
|
* DC-076: Tests for the dashboard WebSocket server
|
|
*/
|
|
const http = require('http');
|
|
const WebSocket = require('ws');
|
|
const EventEmitter = require('events');
|
|
const createDashboardWS = require('../../src/websocket/dashboard-ws');
|
|
|
|
function createMockServer() {
|
|
return http.createServer((req, res) => {
|
|
res.writeHead(404);
|
|
res.end();
|
|
});
|
|
}
|
|
|
|
describe('DC-076: Dashboard WebSocket', () => {
|
|
let server, wsServer, port;
|
|
|
|
beforeEach((done) => {
|
|
server = createMockServer();
|
|
server.listen(0, () => {
|
|
port = server.address().port;
|
|
|
|
const resourceMonitor = new EventEmitter();
|
|
const healthChecker = new EventEmitter();
|
|
const updateManager = new EventEmitter();
|
|
|
|
wsServer = createDashboardWS(server, {
|
|
resourceMonitor,
|
|
healthChecker,
|
|
updateManager,
|
|
log: { info: jest.fn(), error: jest.fn() },
|
|
});
|
|
done();
|
|
});
|
|
});
|
|
|
|
afterEach((done) => {
|
|
wsServer.close();
|
|
server.close(done);
|
|
});
|
|
|
|
it('accepts connections at the upgrade path', (done) => {
|
|
const ws = new WebSocket(`ws://localhost:${port}/api/v1/ws`);
|
|
ws.on('open', () => {
|
|
ws.close();
|
|
});
|
|
ws.on('close', () => {
|
|
done();
|
|
});
|
|
ws.on('error', done);
|
|
});
|
|
|
|
it('sends a connected event on join', (done) => {
|
|
const ws = new WebSocket(`ws://localhost:${port}/api/v1/ws`);
|
|
ws.on('message', (raw) => {
|
|
const msg = JSON.parse(raw.toString());
|
|
if (msg.type === 'connected') {
|
|
expect(msg.data).toHaveProperty('clients');
|
|
ws.close();
|
|
done();
|
|
}
|
|
});
|
|
ws.on('error', done);
|
|
});
|
|
|
|
it('responds to ping with pong', (done) => {
|
|
const ws = new WebSocket(`ws://localhost:${port}/api/v1/ws`);
|
|
ws.on('open', () => {
|
|
ws.send(JSON.stringify({ type: 'ping' }));
|
|
});
|
|
ws.on('message', (raw) => {
|
|
const msg = JSON.parse(raw.toString());
|
|
if (msg.type === 'pong') {
|
|
ws.close();
|
|
done();
|
|
}
|
|
});
|
|
ws.on('error', done);
|
|
});
|
|
|
|
it('responds to subscribe with subscribed confirmation', (done) => {
|
|
const ws = new WebSocket(`ws://localhost:${port}/api/v1/ws`);
|
|
ws.on('open', () => {
|
|
ws.send(JSON.stringify({ type: 'subscribe', events: ['resource-alert', 'incident'] }));
|
|
});
|
|
ws.on('message', (raw) => {
|
|
const msg = JSON.parse(raw.toString());
|
|
if (msg.type === 'subscribed') {
|
|
expect(msg.events).toEqual(['resource-alert', 'incident']);
|
|
ws.close();
|
|
done();
|
|
}
|
|
});
|
|
ws.on('error', done);
|
|
});
|
|
|
|
it('responds to client-count request', (done) => {
|
|
const ws = new WebSocket(`ws://localhost:${port}/api/v1/ws`);
|
|
ws.on('open', () => {
|
|
ws.send(JSON.stringify({ type: 'client-count' }));
|
|
});
|
|
ws.on('message', (raw) => {
|
|
const msg = JSON.parse(raw.toString());
|
|
if (msg.type === 'client-count') {
|
|
expect(msg.count).toBeGreaterThanOrEqual(1);
|
|
ws.close();
|
|
done();
|
|
}
|
|
});
|
|
ws.on('error', done);
|
|
});
|
|
|
|
it('returns error for invalid JSON', (done) => {
|
|
const ws = new WebSocket(`ws://localhost:${port}/api/v1/ws`);
|
|
ws.on('open', () => {
|
|
ws.send('not json');
|
|
});
|
|
ws.on('message', (raw) => {
|
|
const msg = JSON.parse(raw.toString());
|
|
if (msg.type === 'error') {
|
|
expect(msg.error).toContain('Invalid JSON');
|
|
ws.close();
|
|
done();
|
|
}
|
|
});
|
|
ws.on('error', done);
|
|
});
|
|
|
|
it('tracks client count', () => {
|
|
expect(wsServer.getClientCount()).toBe(0);
|
|
});
|
|
|
|
it('broadcast method does not throw with no clients', () => {
|
|
expect(() => wsServer.broadcast('test', { foo: 'bar' })).not.toThrow();
|
|
});
|
|
});
|