const { createApp } = require('../startRobot'); describe('startRobot orchestrator', () => { test('creates HTTPS and info servers and binds modules', () => { const readFileSync = jest.fn() .mockReturnValueOnce('fake-key') .mockReturnValueOnce('fake-cert'); const httpsServerMock = { listen: jest.fn() }; const httpsModuleMock = { createServer: jest.fn(() => httpsServerMock) }; const initInputWS = jest.fn(); const infoServerMock = { listen: jest.fn() }; const createInfoServer = jest.fn(() => infoServerMock); const TelnetSenderClass = jest.fn(() => ({ tSocket: null })); // Shelly-Mock: getStatus() liefert state='ready' (kein tSocket, kein isTestMode) const ShellyClass = jest.fn(() => ({ getStatus: () => ({ state: 'ready', url: null, error: null }) })); const robotInstances = []; class RobotClass { constructor() { this.cmdReceivers = []; robotInstances.push(this); } } const result = createApp({ fsModule: { readFileSync }, httpsModule: httpsModuleMock, processEnv: {}, RobotClass, GCodeModule: { dummy: true }, TelnetSenderClass, ShellyClass, initInputWSFn: initInputWS, createInfoServerFn: createInfoServer, setTimeoutFn: (fn) => fn(), consoleObj: { log: jest.fn(), warn: jest.fn(), error: jest.fn() } }); expect(readFileSync).toHaveBeenCalledTimes(3); // key, cert, data/robot/robot.json expect(httpsModuleMock.createServer).toHaveBeenCalledWith({ enable: true, key: 'fake-key', cert: 'fake-cert', passphrase: 'abcd' }); expect(initInputWS).toHaveBeenCalledWith(httpsServerMock, expect.any(RobotClass), { dummy: true }, expect.any(Object)); expect(createInfoServer).toHaveBeenCalledWith( expect.objectContaining({ key: 'fake-key', cert: 'fake-cert', passphrase: 'abcd' }), expect.any(Object), expect.any(RobotClass), { dummy: true }, expect.arrayContaining([ expect.objectContaining({ name: 'Base', instance: expect.any(Object) }), expect.objectContaining({ name: 'Elbow', instance: expect.any(Object) }), expect.objectContaining({ name: 'Hand', instance: expect.any(Object) }), expect.objectContaining({ name: 'EmergencyStop', instance: expect.any(Object) }) ]), expect.objectContaining({}) // options: { apiKey } ); expect(httpsServerMock.listen).toHaveBeenCalledWith(2095); expect(infoServerMock.listen).toHaveBeenCalledWith(2098); expect(result).toHaveProperty('httpsServer', httpsServerMock); expect(result).toHaveProperty('infoServer', infoServerMock); expect(result).toHaveProperty('senders'); expect(result.senders).toHaveLength(4); // base, elbow, hand, emergencyStop // Nur Telnet-Sender (3) landen in cmdReceivers — Shelly nicht expect(robotInstances[0].cmdReceivers).toHaveLength(3); expect(result.startupStatus).toEqual({ https: { ok: true }, senders: [ { name: 'Base', status: 'disconnected', reason: 'no active socket connection' }, { name: 'Elbow', status: 'disconnected', reason: 'no active socket connection' }, { name: 'Hand', status: 'disconnected', reason: 'no active socket connection' }, { name: 'EmergencyStop', status: 'ready', reason: undefined } ] }); expect(result.sharedState.connectedClients).toEqual([]); }); test('reports missing HTTPS certificates on startup', () => { const readFileSync = jest.fn().mockImplementation(() => { throw new Error('ENOENT: no such file or directory'); }); const httpsModuleMock = { createServer: jest.fn() }; const result = createApp({ fsModule: { readFileSync }, httpsModule: httpsModuleMock, processEnv: {}, RobotClass: class {}, GCodeModule: { dummy: true }, TelnetSenderClass: jest.fn(), initInputWSFn: jest.fn(), createInfoServerFn: jest.fn(), setTimeoutFn: jest.fn(), consoleObj: { log: jest.fn(), error: jest.fn(), warn: jest.fn() } }); expect(result.startupStatus.https.ok).toBe(false); expect(result.startupStatus.https.error).toMatch(/Failed to load HTTPS certificate\/key/); expect(httpsModuleMock.createServer).not.toHaveBeenCalled(); expect(result.httpsServer).toBeUndefined(); expect(result.infoServer).toBeUndefined(); expect(result.senders).toBeUndefined(); }); });