// Testet das FCode-Routing in InputWS (FCodeClient wird gemockt). jest.mock('../robot/FCodeClient', () => ({ isFCode: jest.fn((msg) => /^F[A-Z][a-z]+/.test(String(msg).trim())), handle: jest.fn(), })); const http = require('http'); const WebSocket = require('ws'); const FCodeClient = require('../robot/FCodeClient'); const initInputWS = require('../server/InputWS'); const GCode = require('../robot/GCode'); const createDummyRobot = require('./helpers/createDummyRobot'); /* ---------- helpers ---------- */ function listen(server) { return new Promise((resolve, reject) => { server.listen(0, () => { const { port } = server.address(); port ? resolve(port) : reject(new Error('no port')); }); server.on('error', reject); }); } function connect(port) { return new Promise((resolve, reject) => { const ws = new WebSocket(`ws://127.0.0.1:${port}`); ws.on('open', () => resolve(ws)); ws.on('error', reject); }); } function nextMessage(ws, timeoutMs = 2000) { return new Promise((resolve, reject) => { const t = setTimeout(() => reject(new Error('Timeout')), timeoutMs); ws.once('message', (d) => { clearTimeout(t); resolve(d.toString()); }); }); } /* ---------- suite ---------- */ describe('InputWS FCode-Routing', () => { let server, ws; beforeEach(() => { FCodeClient.handle.mockReset(); FCodeClient.isFCode.mockClear(); }); afterEach(async () => { if (ws) { ws.close(); ws = null; } if (server) { await new Promise(r => server.close(r)); server = null; } }); async function setup(robot) { server = http.createServer(); initInputWS(server, robot || createDummyRobot(), GCode, { connectedClients: [], lastCommands: [], lastPings: [] }); const port = await listen(server); ws = await connect(port); return ws; } test('FList → broadcastet die Antwortdaten vom Fileservice', async () => { FCodeClient.handle.mockResolvedValue({ type: 'list', data: JSON.stringify([{ id: 'a' }]) }); await setup(); const replyP = nextMessage(ws); ws.send('FList'); expect(JSON.parse(await replyP)).toEqual([{ id: 'a' }]); }); test('FPlus (step) → GCode-Zeile ausführen + M114 broadcasten', async () => { const line = 'G90 G1 x10 y300 z0 a1.5708 b-1.5708 c0 e0 f1000'; FCodeClient.handle.mockResolvedValue({ type: 'step', line }); const robot = createDummyRobot(); await setup(robot); const replyP = nextMessage(ws); ws.send('FPlus'); const msg = JSON.parse(await replyP); expect(msg.position).toBeDefined(); expect(robot.sendCommand).toHaveBeenCalled(); }); test('Fileservice-Fehler → machine-readable FILE_ERROR an Sender', async () => { const err = Object.assign(new Error('not found'), { code: 'PROGRAM_NOT_FOUND' }); FCodeClient.handle.mockRejectedValue(err); await setup(); const replyP = nextMessage(ws); ws.send('FLoad nichtda'); const msg = JSON.parse(await replyP); expect(msg.type).toBe('error'); expect(msg.code).toBe('FILE_ERROR'); expect(msg.input).toBe('FLoad nichtda'); }); });