From 172606c7a3fc222ee24baf42d555addd6e43046b Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:40:07 +0200 Subject: [PATCH] UnitTests --- doc/ToDo_7_Tests.md | 6 +- logs/gcode_commands.log | 24 +++++++ logs/pings.log | 24 +++++++ robot/GCode.js | 2 + robot/WSSenderGrbl.js | 3 +- test/GCode.receiveGCode.test.js | 4 +- test/InfoServer.test.js | 121 ++++++++++++++++++++++++++++++++ test/InputWS.test.js | 96 +++++++++++++++++++++++++ 8 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 test/InfoServer.test.js create mode 100644 test/InputWS.test.js diff --git a/doc/ToDo_7_Tests.md b/doc/ToDo_7_Tests.md index ffe7986..2605abf 100644 --- a/doc/ToDo_7_Tests.md +++ b/doc/ToDo_7_Tests.md @@ -6,17 +6,17 @@ Testabdeckung und Fehlerbehandlung sollen die Stabilität der Architektur erhöh ## Aufgaben -- [ ] Unit-Tests für `GCodeParser` +- [x] Unit-Tests für `GCodeParser` - [ ] Unit-Tests für `RobotController` - [ ] Unit-Tests für `TelnetSenderGRBL` - Verbindungsstatus - Fehlerfälle - korrektes Sendeformat -- [ ] Tests für `InputWS.js` +- [x] Tests für `InputWS.js` - gültige G-Code-Nachrichten - Ping-Verarbeitung - Statusabfragen -- [ ] Tests für `InfoServer` +- [x] Tests für `InfoServer` - `/api/status` - `/api/position` - [ ] Fehlerfälle explizit prüfen diff --git a/logs/gcode_commands.log b/logs/gcode_commands.log index fe98b09..d9f84e9 100644 --- a/logs/gcode_commands.log +++ b/logs/gcode_commands.log @@ -10156,3 +10156,27 @@ 2026-04-26T15:31:01.738Z ::ffff:172.19.0.5: FShow 2026-04-26T15:31:13.510Z ::ffff:172.19.0.5: FShow 2026-04-26T15:31:51.957Z ::ffff:172.19.0.5: FShow +2026-06-08T13:20:36.489Z ::ffff:127.0.0.1: M114 +2026-06-08T13:20:50.858Z ::ffff:127.0.0.1: M114 +2026-06-08T13:20:58.330Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:01.672Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:07.335Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:17.293Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:30.307Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:32.687Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:35.774Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:38.922Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:41.654Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:48.470Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:48.899Z ::ffff:127.0.0.1: M114 +2026-06-08T13:21:55.506Z ::ffff:127.0.0.1: M114 +2026-06-08T13:29:57.533Z ::ffff:127.0.0.1: M114 +2026-06-08T13:34:40.388Z ::ffff:127.0.0.1: M114 +2026-06-08T13:34:41.782Z ::ffff:127.0.0.1: M114 +2026-06-08T13:38:28.611Z ::ffff:127.0.0.1: M114 +2026-06-08T13:38:33.693Z ::ffff:127.0.0.1: M114 +2026-06-08T13:38:38.138Z ::ffff:127.0.0.1: M114 +2026-06-08T13:38:46.655Z ::ffff:127.0.0.1: M114 +2026-06-08T13:38:50.956Z ::ffff:127.0.0.1: M114 +2026-06-08T13:39:19.725Z ::ffff:127.0.0.1: M114 +2026-06-08T13:39:55.037Z ::ffff:127.0.0.1: M114 diff --git a/logs/pings.log b/logs/pings.log index 8c63443..e2b93ae 100644 --- a/logs/pings.log +++ b/logs/pings.log @@ -14496,3 +14496,27 @@ 2026-04-26T18:07:00.267Z ::ffff:172.19.0.5 : Ping 2026-04-26T18:08:00.272Z ::ffff:172.19.0.5 : Ping 2026-04-26T18:09:00.271Z ::ffff:172.19.0.5 : Ping +2026-06-08T13:20:36.465Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:20:50.833Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:20:58.312Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:01.655Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:07.322Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:17.286Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:30.286Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:32.672Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:35.757Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:38.905Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:41.638Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:48.463Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:48.874Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:21:55.492Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:29:57.521Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:34:40.369Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:34:41.774Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:38:28.593Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:38:33.669Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:38:38.122Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:38:46.632Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:38:50.951Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:39:19.719Z ::ffff:127.0.0.1 : Ping +2026-06-08T13:39:55.032Z ::ffff:127.0.0.1 : Ping diff --git a/robot/GCode.js b/robot/GCode.js index fc56227..bb05cab 100755 --- a/robot/GCode.js +++ b/robot/GCode.js @@ -111,6 +111,8 @@ class GCode{ robot.theta = Math.PI / 2; robot.psi = 0; robot.e = 0; + robot.calculateAngles3D(); + robot.sendCommand(); return; } diff --git a/robot/WSSenderGrbl.js b/robot/WSSenderGrbl.js index f300192..f0263ed 100755 --- a/robot/WSSenderGrbl.js +++ b/robot/WSSenderGrbl.js @@ -19,6 +19,7 @@ module.exports = class TelnetSenderGRBL{ this.receiver = null; this.urlGRBLstr = urlGRBL; + this.maxSpeedF = maxSpeedF; this.xAxisGrbl = xAxisGrbl; this.yAxisGrbl = yAxisGrbl; this.zAxisGrbl = zAxisGrbl; @@ -329,7 +330,7 @@ module.exports = class TelnetSenderGRBL{ } } - data += " f"+(maxSpeedF.toFixed(2).toString()) + data += " f"+(this.maxSpeedF.toFixed(2).toString()) if(this.tSocket && data.length > 3){ diff --git a/test/GCode.receiveGCode.test.js b/test/GCode.receiveGCode.test.js index 5550dc8..6e65323 100644 --- a/test/GCode.receiveGCode.test.js +++ b/test/GCode.receiveGCode.test.js @@ -90,7 +90,7 @@ describe('GCode.receiveGCode', () => { expect(robot.sendCommand).toHaveBeenCalled() }) - test('G28 setzt Home-Position', () => { + test('G28 setzt Home-Position und löst Bewegung aus', () => { const robot = createDummyRobot() GCode.receiveGCode(robot, 'G28') @@ -100,6 +100,8 @@ describe('GCode.receiveGCode', () => { expect(robot.y).toBe(robot.l1 + robot.l2 + robot.l3) expect(robot.phi).toBeCloseTo(-Math.PI / 2) expect(robot.theta).toBeCloseTo(Math.PI / 2) + expect(robot.calculateAngles3D).toHaveBeenCalledTimes(1) + expect(robot.sendCommand).toHaveBeenCalledTimes(1) }) test('Sende falsches Format', () =>{ diff --git a/test/InfoServer.test.js b/test/InfoServer.test.js new file mode 100644 index 0000000..f2f7766 --- /dev/null +++ b/test/InfoServer.test.js @@ -0,0 +1,121 @@ +const fs = require('fs'); +const https = require('https'); +const createInfoServer = require('../server/InfoServer'); +const GCode = require('../robot/GCode'); + +function listen(server) { + return new Promise((resolve, reject) => { + server.listen(0, () => { + const address = server.address(); + if (address && address.port) { + resolve(address.port); + } else { + reject(new Error('Failed to get server port')); + } + }); + server.on('error', reject); + }); +} + +function request(url) { + return new Promise((resolve, reject) => { + const agent = new https.Agent({ rejectUnauthorized: false }); + https.get(url, { agent }, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk.toString(); + }); + res.on('end', () => { + resolve({ statusCode: res.statusCode, body: data }); + }); + }).on('error', reject); + }); +} + +describe('InfoServer', () => { + let server; + let port; + + afterEach(async () => { + if (server) { + await new Promise((resolve) => server.close(resolve)); + server = null; + } + }); + + test('returns status JSON with sender and shared state information', async () => { + const key = fs.readFileSync('https/localhost.key'); + const cert = fs.readFileSync('https/localhost.pem'); + const httpsOptions = { key, cert, passphrase: 'abcd' }; + + const sharedState = { + connectedClients: ['127.0.0.1'], + lastCommands: ['G1 X10 Y10'], + lastPings: ['Ping'] + }; + + const robot = { + x: 1, + y: 2, + z: 3, + phi: 0, + theta: 0, + psi: 0 + }; + + const senders = [ + { name: 'Base', instance: { tSocket: {} } }, + { name: 'Hand', instance: null } + ]; + + server = createInfoServer(httpsOptions, sharedState, robot, GCode, senders); + port = await listen(server); + + const { statusCode, body } = await request(`https://127.0.0.1:${port}/api/status`); + expect(statusCode).toBe(200); + + const status = JSON.parse(body); + expect(status.clients).toEqual(['127.0.0.1']); + expect(status.lastCommands).toEqual(['G1 X10 Y10']); + expect(status.lastPings).toEqual(['Ping']); + expect(status.senders).toEqual([ + { name: 'Base', status: 'connected' }, + { name: 'Hand', status: 'disconnected' } + ]); + }); + + test('returns position JSON from GCode.getM114', async () => { + const key = fs.readFileSync('https/localhost.key'); + const cert = fs.readFileSync('https/localhost.pem'); + const httpsOptions = { key, cert, passphrase: 'abcd' }; + + const sharedState = { connectedClients: [], lastCommands: [], lastPings: [] }; + const robot = { x: 10, y: 20, z: 30, phi: 0.1, theta: 0.2, psi: 0.3, xMotor: 0, alpha: 0, beta: 0, a: 0, b: 0, c: 0 }; + const senders = []; + + server = createInfoServer(httpsOptions, sharedState, robot, GCode, senders); + port = await listen(server); + + const { statusCode, body } = await request(`https://127.0.0.1:${port}/api/position`); + expect(statusCode).toBe(200); + + const json = JSON.parse(body); + expect(json.position).toEqual({ x: 10, y: 20, z: 30, a: 0.1, b: 0.2, c: 0.3 }); + }); + + test('returns 404 for unknown endpoints', async () => { + const key = fs.readFileSync('https/localhost.key'); + const cert = fs.readFileSync('https/localhost.pem'); + const httpsOptions = { key, cert, passphrase: 'abcd' }; + + const sharedState = { connectedClients: [], lastCommands: [], lastPings: [] }; + const robot = { x: 0, y: 0, z: 0, phi: 0, theta: 0, psi: 0, xMotor: 0, alpha: 0, beta: 0, a: 0, b: 0, c: 0 }; + const senders = []; + + server = createInfoServer(httpsOptions, sharedState, robot, GCode, senders); + port = await listen(server); + + const { statusCode } = await request(`https://127.0.0.1:${port}/api/unknown`); + expect(statusCode).toBe(404); + }); +}); diff --git a/test/InputWS.test.js b/test/InputWS.test.js new file mode 100644 index 0000000..76a9c65 --- /dev/null +++ b/test/InputWS.test.js @@ -0,0 +1,96 @@ +const http = require('http'); +const WebSocket = require('ws'); +const initInputWS = require('../server/InputWS'); +const GCode = require('../robot/GCode'); +const createDummyRobot = require('./helpers/createDummyRobot'); + +function listen(server) { + return new Promise((resolve, reject) => { + server.listen(0, () => { + const address = server.address(); + if (address && address.port) { + resolve(address.port); + } else { + reject(new Error('Failed to get server port')); + } + }); + server.on('error', reject); + }); +} + +function connectWebSocket(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 waitForMessage(ws) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('Timeout waiting for message')), 2000); + ws.on('message', (data) => { + clearTimeout(timer); + resolve(data.toString()); + }); + }); +} + +describe('InputWS', () => { + let server; + let port; + let wss; + + afterEach(async () => { + if (server) { + await new Promise((resolve) => server.close(resolve)); + server = null; + } + }); + + test('responds to Ping and updates sharedState', async () => { + server = http.createServer(); + const sharedState = { connectedClients: [], lastCommands: [], lastPings: [] }; + const robot = createDummyRobot(); + wss = initInputWS(server, robot, GCode, sharedState); + + port = await listen(server); + const client = await connectWebSocket(port); + + const messagePromise = waitForMessage(client); + client.send('Ping'); + + const message = await messagePromise; + expect(message).toBe('Ping'); + expect(sharedState.lastPings.length).toBe(1); + expect(sharedState.connectedClients.length).toBe(1); + + client.close(); + }); + + test('responds to M114 with current robot position JSON', async () => { + server = http.createServer(); + const sharedState = { connectedClients: [], lastCommands: [], lastPings: [] }; + const robot = createDummyRobot(); + robot.x = 12; + robot.y = 34; + robot.z = 56; + robot.phi = 1; + robot.theta = 2; + robot.psi = 3; + + wss = initInputWS(server, robot, GCode, sharedState); + port = await listen(server); + const client = await connectWebSocket(port); + + const messagePromise = waitForMessage(client); + client.send('M114'); + + const message = await messagePromise; + const parsed = JSON.parse(message); + expect(parsed.position).toEqual({ x: 12, y: 34, z: 56, a: 1, b: 2, c: 3 }); + expect(parsed.motorCounts).toBeDefined(); + + client.close(); + }); +});