import { HocuspocusProvider, HocuspocusProviderWebsocket, } from '@hocuspocus/provider'; import request from 'supertest'; import WebSocket from 'ws'; const port = 5555; const portWS = 6666; const origin = 'http://localhost:3000'; jest.mock('../src/env', () => { return { PORT: port, COLLABORATION_SERVER_ORIGIN: origin, COLLABORATION_SERVER_SECRET: 'test-secret-api-key', }; }); console.error = jest.fn(); import { promiseDone } from '../src/helpers'; import { hocuspocusServer, initServer } from '../src/server'; // Adjust the path to your server file const { app, server } = initServer(); describe('Server Tests', () => { beforeAll(async () => { await hocuspocusServer.configure({ port: portWS }).listen(); }); afterAll(() => { server.close(); void hocuspocusServer.destroy(); }); test('Ping Pong', async () => { const response = await request(app as any).get('/ping'); expect(response.status).toBe(200); expect(response.body.message).toBe('pong'); }); test('POST /collaboration/api/reset-connections?room=[ROOM_ID] invalid origin', async () => { const response = await request(app as any) .post('/collaboration/api/reset-connections/?room=test-room') .set('Origin', 'http://invalid-origin.com') .send({ document_id: 'test-document' }); expect(response.status).toBe(403); expect(response.body.error).toBe('CORS policy violation: Invalid Origin'); }); test('POST /collaboration/api/reset-connections?room=[ROOM_ID] with incorrect API key should return 403', async () => { const response = await request(app as any) .post('/collaboration/api/reset-connections/?room=test-room') .set('Origin', origin) .set('Authorization', 'wrong-api-key'); expect(response.status).toBe(403); expect(response.body.error).toBe('Forbidden: Invalid API Key'); }); test('POST /collaboration/api/reset-connections?room=[ROOM_ID] failed if room not indicated', async () => { const response = await request(app as any) .post('/collaboration/api/reset-connections/') .set('Origin', origin) .set('Authorization', 'test-secret-api-key') .send({ document_id: 'test-document' }); expect(response.status).toBe(400); expect(response.body.error).toBe('Room name not provided'); }); test('POST /collaboration/api/reset-connections?room=[ROOM_ID] with correct API key should reset connections', async () => { // eslint-disable-next-line jest/unbound-method const { closeConnections } = hocuspocusServer; const mockHandleConnection = jest.fn(); (hocuspocusServer.closeConnections as jest.Mock) = mockHandleConnection; const response = await request(app as any) .post('/collaboration/api/reset-connections?room=test-room') .set('Origin', origin) .set('Authorization', 'test-secret-api-key'); expect(response.status).toBe(200); expect(response.body.message).toBe('Connections reset'); expect(mockHandleConnection).toHaveBeenCalled(); mockHandleConnection.mockClear(); hocuspocusServer.closeConnections = closeConnections; }); ['/collaboration/api/anything/', '/', '/anything'].forEach((path) => { test(`"${path}" endpoint should be forbidden`, async () => { const response = await request(app as any).post(path); expect(response.status).toBe(403); expect(response.body.error).toBe('Forbidden'); }); }); test('WebSocket connection with correct API key can connect', () => { const { promise, done } = promiseDone(); // eslint-disable-next-line jest/unbound-method const { handleConnection } = hocuspocusServer; const mockHandleConnection = jest.fn(); (hocuspocusServer.handleConnection as jest.Mock) = mockHandleConnection; const clientWS = new WebSocket( `ws://localhost:${port}/collaboration/ws/?room=test-room`, { headers: { authorization: 'test-secret-api-key', Origin: origin, }, }, ); clientWS.on('open', () => { expect(mockHandleConnection).toHaveBeenCalled(); clientWS.close(); mockHandleConnection.mockClear(); hocuspocusServer.handleConnection = handleConnection; done(); }); return promise; }); test('WebSocket connection with bad origin should be closed', () => { const { promise, done } = promiseDone(); const ws = new WebSocket( `ws://localhost:${port}/collaboration/ws/?room=test-room`, { headers: { Origin: 'http://bad-origin.com', }, }, ); ws.onclose = () => { expect(ws.readyState).toBe(ws.CLOSED); done(); }; return promise; }); test('WebSocket connection with incorrect API key should be closed', () => { const { promise, done } = promiseDone(); const ws = new WebSocket( `ws://localhost:${port}/collaboration/ws/?room=test-room`, { headers: { Authorization: 'wrong-api-key', Origin: origin, }, }, ); ws.onclose = () => { expect(ws.readyState).toBe(ws.CLOSED); done(); }; return promise; }); test('WebSocket connection not allowed if room not matching provider name', () => { const { promise, done } = promiseDone(); const wsHocus = new HocuspocusProviderWebsocket({ url: `ws://localhost:${portWS}/?room=my-test`, WebSocketPolyfill: WebSocket, maxAttempts: 1, quiet: true, }); const provider = new HocuspocusProvider({ websocketProvider: wsHocus, name: 'hocuspocus-test', broadcast: false, quiet: true, preserveConnection: false, onClose: (data) => { wsHocus.stopConnectionAttempt(); expect(data.event.reason).toBe('Forbidden'); wsHocus.webSocket?.close(); wsHocus.disconnect(); provider.destroy(); wsHocus.destroy(); done(); }, }); return promise; }); test('WebSocket connection read-only', () => { const { promise, done } = promiseDone(); const wsHocus = new HocuspocusProviderWebsocket({ url: `ws://localhost:${portWS}/?room=hocuspocus-test`, WebSocketPolyfill: WebSocket, }); const provider = new HocuspocusProvider({ websocketProvider: wsHocus, name: 'hocuspocus-test', broadcast: false, quiet: true, onConnect: () => { void hocuspocusServer .openDirectConnection('hocuspocus-test') .then((connection) => { connection.document?.getConnections().forEach((connection) => { expect(connection.readOnly).toBe(true); }); void connection.disconnect(); }); provider.destroy(); wsHocus.destroy(); done(); }, }); return promise; }); });