UnitTests
This commit is contained in:
@@ -6,17 +6,17 @@ Testabdeckung und Fehlerbehandlung sollen die Stabilität der Architektur erhöh
|
|||||||
|
|
||||||
## Aufgaben
|
## Aufgaben
|
||||||
|
|
||||||
- [ ] Unit-Tests für `GCodeParser`
|
- [x] Unit-Tests für `GCodeParser`
|
||||||
- [ ] Unit-Tests für `RobotController`
|
- [ ] Unit-Tests für `RobotController`
|
||||||
- [ ] Unit-Tests für `TelnetSenderGRBL`
|
- [ ] Unit-Tests für `TelnetSenderGRBL`
|
||||||
- Verbindungsstatus
|
- Verbindungsstatus
|
||||||
- Fehlerfälle
|
- Fehlerfälle
|
||||||
- korrektes Sendeformat
|
- korrektes Sendeformat
|
||||||
- [ ] Tests für `InputWS.js`
|
- [x] Tests für `InputWS.js`
|
||||||
- gültige G-Code-Nachrichten
|
- gültige G-Code-Nachrichten
|
||||||
- Ping-Verarbeitung
|
- Ping-Verarbeitung
|
||||||
- Statusabfragen
|
- Statusabfragen
|
||||||
- [ ] Tests für `InfoServer`
|
- [x] Tests für `InfoServer`
|
||||||
- `/api/status`
|
- `/api/status`
|
||||||
- `/api/position`
|
- `/api/position`
|
||||||
- [ ] Fehlerfälle explizit prüfen
|
- [ ] Fehlerfälle explizit prüfen
|
||||||
|
|||||||
@@ -10156,3 +10156,27 @@
|
|||||||
2026-04-26T15:31:01.738Z ::ffff:172.19.0.5: FShow
|
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:13.510Z ::ffff:172.19.0.5: FShow
|
||||||
2026-04-26T15:31:51.957Z ::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
|
||||||
|
|||||||
@@ -14496,3 +14496,27 @@
|
|||||||
2026-04-26T18:07:00.267Z ::ffff:172.19.0.5 : Ping
|
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:08:00.272Z ::ffff:172.19.0.5 : Ping
|
||||||
2026-04-26T18:09:00.271Z ::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
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ class GCode{
|
|||||||
robot.theta = Math.PI / 2;
|
robot.theta = Math.PI / 2;
|
||||||
robot.psi = 0;
|
robot.psi = 0;
|
||||||
robot.e = 0;
|
robot.e = 0;
|
||||||
|
robot.calculateAngles3D();
|
||||||
|
robot.sendCommand();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ module.exports = class TelnetSenderGRBL{
|
|||||||
this.receiver = null;
|
this.receiver = null;
|
||||||
|
|
||||||
this.urlGRBLstr = urlGRBL;
|
this.urlGRBLstr = urlGRBL;
|
||||||
|
this.maxSpeedF = maxSpeedF;
|
||||||
this.xAxisGrbl = xAxisGrbl;
|
this.xAxisGrbl = xAxisGrbl;
|
||||||
this.yAxisGrbl = yAxisGrbl;
|
this.yAxisGrbl = yAxisGrbl;
|
||||||
this.zAxisGrbl = zAxisGrbl;
|
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){
|
if(this.tSocket && data.length > 3){
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ describe('GCode.receiveGCode', () => {
|
|||||||
expect(robot.sendCommand).toHaveBeenCalled()
|
expect(robot.sendCommand).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('G28 setzt Home-Position', () => {
|
test('G28 setzt Home-Position und löst Bewegung aus', () => {
|
||||||
const robot = createDummyRobot()
|
const robot = createDummyRobot()
|
||||||
|
|
||||||
GCode.receiveGCode(robot, 'G28')
|
GCode.receiveGCode(robot, 'G28')
|
||||||
@@ -100,6 +100,8 @@ describe('GCode.receiveGCode', () => {
|
|||||||
expect(robot.y).toBe(robot.l1 + robot.l2 + robot.l3)
|
expect(robot.y).toBe(robot.l1 + robot.l2 + robot.l3)
|
||||||
expect(robot.phi).toBeCloseTo(-Math.PI / 2)
|
expect(robot.phi).toBeCloseTo(-Math.PI / 2)
|
||||||
expect(robot.theta).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', () =>{
|
test('Sende falsches Format', () =>{
|
||||||
|
|||||||
121
test/InfoServer.test.js
Normal file
121
test/InfoServer.test.js
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
96
test/InputWS.test.js
Normal file
96
test/InputWS.test.js
Normal file
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user