Emergency Stop

This commit is contained in:
chk
2026-06-12 18:16:15 +02:00
parent 6fc6605080
commit 59d4cf7df4
17 changed files with 1098 additions and 540 deletions

View File

@@ -0,0 +1,119 @@
'use strict';
// Tests für TelnetSenderGRBL.emergencyStop() und alarmUnlock()
const { EventEmitter } = require('events');
const TelnetSenderGRBL = require('../robot/TelnetSenderGRBL');
function makeTelnetSocket() {
const em = new EventEmitter();
return Object.assign(em, {
write: jest.fn(),
end: jest.fn(),
destroy: jest.fn(),
});
}
/** Sender im Test-Modus (URL "test.test") — tSocket ist gesetzt. */
function makeConnectedSender() {
const sender = new TelnetSenderGRBL('test.test');
sender.tSocket = makeTelnetSocket();
return sender;
}
/** Sender ohne tSocket (disconnected). */
function makeDisconnectedSender() {
const sender = new TelnetSenderGRBL('test.test');
sender.tSocket = null;
return sender;
}
// ── emergencyStop() ──────────────────────────────────────────────────────────
describe('TelnetSenderGRBL.emergencyStop()', () => {
test('sendet "!" wenn verbunden', async () => {
const sender = makeConnectedSender();
const result = await sender.emergencyStop();
expect(result.ok).toBe(true);
expect(sender.tSocket.write).toHaveBeenCalledWith('!');
});
test('sendet nur "!" — kein Zeilenende (FluidNC Realtime-Byte)', async () => {
const sender = makeConnectedSender();
await sender.emergencyStop();
const arg = sender.tSocket.write.mock.calls[0][0];
expect(arg).toBe('!');
expect(arg).not.toContain('\r\n');
});
test('gibt {ok:false} zurück wenn nicht verbunden', async () => {
const sender = makeDisconnectedSender();
const result = await sender.emergencyStop();
expect(result.ok).toBe(false);
expect(result.error).toBe('not connected');
});
test('gibt {ok:false} zurück wenn write() wirft', async () => {
const sender = makeConnectedSender();
sender.tSocket.write.mockImplementation(() => { throw new Error('socket closed'); });
const result = await sender.emergencyStop();
expect(result.ok).toBe(false);
expect(result.error).toBe('socket closed');
});
});
// ── alarmUnlock() ────────────────────────────────────────────────────────────
describe('TelnetSenderGRBL.alarmUnlock()', () => {
test('sendet "$X\\r\\n" wenn verbunden', async () => {
const sender = makeConnectedSender();
const result = await sender.alarmUnlock();
expect(result.ok).toBe(true);
expect(sender.tSocket.write).toHaveBeenCalledWith('$X\r\n');
});
test('gibt {ok:false} zurück wenn nicht verbunden', async () => {
const sender = makeDisconnectedSender();
const result = await sender.alarmUnlock();
expect(result.ok).toBe(false);
expect(result.error).toBe('not connected');
});
test('gibt {ok:false} zurück wenn write() wirft', async () => {
const sender = makeConnectedSender();
sender.tSocket.write.mockImplementation(() => { throw new Error('write error'); });
const result = await sender.alarmUnlock();
expect(result.ok).toBe(false);
expect(result.error).toBe('write error');
});
});
// ── SenderInterface-Defaults ─────────────────────────────────────────────────
describe('SenderInterface — Default-Implementierungen (no-op)', () => {
test('SenderInterface.emergencyStop() gibt {ok:true, skipped:true}', async () => {
const SenderInterface = require('../robot/SenderInterface');
// Subclass, die nur emergencyStop von der Basis erbt
class MinimalSender extends SenderInterface {
async connect() { return this; }
send() { return false; }
getStatus() { return {}; }
disconnect() {}
}
const s = new MinimalSender();
await expect(s.emergencyStop()).resolves.toEqual({ ok: true, skipped: true });
});
test('SenderInterface.alarmUnlock() gibt {ok:true, skipped:true}', async () => {
const SenderInterface = require('../robot/SenderInterface');
class MinimalSender extends SenderInterface {
async connect() { return this; }
send() { return false; }
getStatus() { return {}; }
disconnect() {}
}
const s = new MinimalSender();
await expect(s.alarmUnlock()).resolves.toEqual({ ok: true, skipped: true });
});
});