// Tests für src/driverClient.js // Mockt das 'ws'-Modul vollständig — keine echte Netzwerkverbindung. jest.mock('ws'); function makeMockWs(readyState = 1 /*OPEN*/) { return { readyState, send: jest.fn(), once: jest.fn(), on: jest.fn(), }; } describe('driverClient', () => { // Nach jedem resetModules() zeigt 'WebSocket' auf die frische Auto-Mock-Instanz. // Deshalb wird MockWebSocket in beforeEach neu geladen, nicht oben auf dem Modul. let MockWebSocket; let cfg; let dc; beforeEach(() => { jest.resetModules(); jest.clearAllMocks(); MockWebSocket = require('ws'); MockWebSocket.OPEN = 1; MockWebSocket.CLOSED = 3; cfg = require('../src/config'); cfg.driverWsUrl = 'wss://test-driver:9999'; cfg.driverTimeoutMs = 500; dc = require('../src/driverClient'); }); // ─── Fehler: nicht konfiguriert ──────────────────────────────────────────── test('send() rejects mit NOT_CONFIGURED wenn DRIVER_WS_URL leer ist', async () => { cfg.driverWsUrl = ''; await expect(dc.send('G90 G1 x0')).rejects.toMatchObject({ driverCode: 'NOT_CONFIGURED', }); }); // ─── Erfolg: M114-Antwort ────────────────────────────────────────────────── test('send() resolved mit M114-Objekt bei Erfolg', async () => { const mockWs = makeMockWs(); MockWebSocket.mockImplementation(() => mockWs); let capturedHandler; mockWs.once.mockImplementation((event, handler) => { if (event === 'message') capturedHandler = handler; }); const promise = dc.send('G90 G1 x10'); const m114 = { position: { x: 10, y: 300, z: 0, a: 0 }, motorCounts: { x: 1 } }; capturedHandler(Buffer.from(JSON.stringify(m114))); const result = await promise; expect(result).toMatchObject({ position: { x: 10 } }); expect(mockWs.send).toHaveBeenCalledWith('G90 G1 x10'); }); // ─── Fehler: Driver meldet GCODE_ERROR ──────────────────────────────────── test('send() rejects mit driverCode=GCODE_ERROR wenn Driver Fehler schickt', async () => { const mockWs = makeMockWs(); MockWebSocket.mockImplementation(() => mockWs); let capturedHandler; mockWs.once.mockImplementation((event, handler) => { if (event === 'message') capturedHandler = handler; }); const promise = dc.send('UNGUELTIG'); const errMsg = { type: 'error', code: 'GCODE_ERROR', message: 'inverse kinematics failed', input: 'UNGUELTIG' }; capturedHandler(Buffer.from(JSON.stringify(errMsg))); await expect(promise).rejects.toMatchObject({ message: 'inverse kinematics failed', driverCode: 'GCODE_ERROR', }); }); // ─── Fehler: Timeout ─────────────────────────────────────────────────────── test('send() rejects mit TIMEOUT wenn Driver nicht antwortet', async () => { jest.useFakeTimers(); const mockWs = makeMockWs(); MockWebSocket.mockImplementation(() => mockWs); mockWs.once.mockImplementation(() => {}); // handler nie aufrufen → Timeout tritt ein cfg.driverTimeoutMs = 5000; const promise = dc.send('G90 G1 x0'); // Fake-Timer vorschiessen damit der Timeout feuert jest.runAllTimers(); await expect(promise).rejects.toMatchObject({ driverCode: 'TIMEOUT' }); jest.useRealTimers(); }); // ─── Unbekanntes Antwort-Format → trotzdem Erfolg ──────────────────────── test('send() resolved auch bei unbekanntem (nicht-JSON) Antwort-Format', async () => { const mockWs = makeMockWs(); MockWebSocket.mockImplementation(() => mockWs); let capturedHandler; mockWs.once.mockImplementation((event, handler) => { if (event === 'message') capturedHandler = handler; }); const promise = dc.send('G28'); capturedHandler(Buffer.from('ok')); const result = await promise; expect(result).toMatchObject({ raw: 'ok' }); }); // ─── configured() ────────────────────────────────────────────────────────── test('configured() ist true wenn DRIVER_WS_URL gesetzt', () => { expect(dc.configured()).toBe(true); }); test('configured() ist false wenn DRIVER_WS_URL leer', () => { cfg.driverWsUrl = ''; expect(dc.configured()).toBe(false); }); });