velxio/frontend/src/__tests__/pi-linux-mode-restart.test.ts

78 lines
2.7 KiB
TypeScript

/**
* Turning on Linux mode must actually boot the guest.
*
* The button pins the mode and restarts the board: stopBoard, then
* startBoard a moment later. That second call has to reach the board's
* bridge and open its WebSocket — when it silently did not, the user got
* no guest, no error, and a toolbar still showing Stop.
*/
import { describe, it, expect, beforeEach, vi } from 'vitest';
// The bridge opens a real WebSocket; capture the instances instead.
const opened: string[] = [];
class FakeSocket {
static OPEN = 1;
static CONNECTING = 0;
static CLOSING = 2;
static CLOSED = 3;
readyState = 0;
onopen: (() => void) | null = null;
onclose: (() => void) | null = null;
onerror: (() => void) | null = null;
onmessage: ((e: { data: string }) => void) | null = null;
sent: string[] = [];
constructor(public url: string) {
opened.push(url);
}
send(d: string) {
this.sent.push(d);
}
close() {
this.readyState = FakeSocket.CLOSING;
}
}
beforeEach(() => {
opened.length = 0;
(globalThis as unknown as { WebSocket: unknown }).WebSocket = FakeSocket;
});
describe('Linux mode restart', () => {
it('opens the guest WebSocket when the board is pinned to linux', async () => {
const { useSimulatorStore, getBoardBridge } = await import(
'../store/useSimulatorStore'
);
const id = useSimulatorStore.getState().addBoard('raspberry-pi-3', 10, 10);
expect(getBoardBridge(id), 'the board must get a bridge on add').toBeTruthy();
// What the Linux-mode button does: pin, stop, start.
useSimulatorStore.getState().updateBoard(id, { enginePinned: 'linux' });
useSimulatorStore.getState().stopBoard(id);
useSimulatorStore.getState().startBoard(id);
expect(opened.filter((u) => u.includes('/simulation/ws/')).length).toBe(1);
const board = useSimulatorStore.getState().boards.find((b) => b.id === id);
expect(board?.engineMode).toBe('linux');
expect(board?.running, 'the toolbar must show a running board').toBe(true);
});
it('reconnects even when the previous socket is still closing', async () => {
const { useSimulatorStore, getBoardBridge } = await import(
'../store/useSimulatorStore'
);
const id = useSimulatorStore.getState().addBoard('raspberry-pi-4', 10, 10);
const bridge = getBoardBridge(id)!;
useSimulatorStore.getState().updateBoard(id, { enginePinned: 'linux' });
useSimulatorStore.getState().startBoard(id);
expect(opened.length).toBe(1);
// Stop closes the socket; a CLOSING socket used to look "alive" and
// the next start became a no-op.
useSimulatorStore.getState().stopBoard(id);
bridge.connect();
expect(opened.length).toBe(2);
});
});