From a7d0a672d59bf8985a6fc0e61b49015fabd96513 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 15 May 2026 17:03:40 +0530 Subject: Phase 1 working: captive portal, DNS hijack, NAT-based access control - Fix WiFi init order: netif creation before esp_wifi_init, set mode before set_config - Replace broken netif input filter with NAPT on/off per authentication state - NAPT disabled by default, enabled when client granted, disabled on revoke - Fix test helpers: use -I wlp59s0 for ping, handle nslookup exit code 1 - All 20 API tests pass, all 6 smoke tests pass --- tests/helpers/serial.mjs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/helpers/serial.mjs (limited to 'tests/helpers/serial.mjs') diff --git a/tests/helpers/serial.mjs b/tests/helpers/serial.mjs new file mode 100644 index 0000000..306b552 --- /dev/null +++ b/tests/helpers/serial.mjs @@ -0,0 +1,82 @@ +import { SerialPort } from 'serialport'; +import { ReadlineParser } from '@serialport/parser-readline'; +import { execSync } from 'child_process'; + +const DEFAULT_BAUD = 115200; +const BOOT_TIMEOUT = 30000; + +export async function execSerial(portPath, command, timeoutMs = 5000) { + return new Promise((resolve, reject) => { + const port = new SerialPort({ path: portPath, baudRate: DEFAULT_BAUD }); + const parser = port.pipe(new ReadlineParser()); + const lines = []; + let resolved = false; + + const timer = setTimeout(() => { + if (!resolved) { resolved = true; port.close(); resolve(lines.join('\n')); } + }, timeoutMs); + + parser.on('data', (line) => { + lines.push(line); + if (line.includes('___END___') && !resolved) { + resolved = true; + clearTimeout(timer); + port.close(); + resolve(lines.join('\n')); + } + }); + + port.on('open', () => { + port.write(command + '\n'); + }); + + port.on('error', (err) => { + if (!resolved) { resolved = true; clearTimeout(timer); reject(err); } + }); + }); +} + +export async function waitForBoot(portPath, timeoutMs = BOOT_TIMEOUT) { + return new Promise((resolve, reject) => { + const port = new SerialPort({ path: portPath, baudRate: DEFAULT_BAUD }); + const parser = port.pipe(new ReadlineParser()); + const timer = setTimeout(() => { + port.close(); + reject(new Error('Boot timeout')); + }, timeoutMs); + + parser.on('data', (line) => { + if (line.includes('TollGate services started') || line.includes('WiFi AP+STA started')) { + clearTimeout(timer); + setTimeout(() => { port.close(); resolve(true); }, 500); + } + }); + + port.on('error', (err) => { + clearTimeout(timer); + reject(err); + }); + }); +} + +export async function readSerial(portPath, durationMs = 3000) { + return new Promise((resolve, reject) => { + const port = new SerialPort({ path: portPath, baudRate: DEFAULT_BAUD }); + const parser = port.pipe(new ReadlineParser()); + const lines = []; + + const timer = setTimeout(() => { + port.close(); + resolve(lines.join('\n')); + }, durationMs); + + parser.on('data', (line) => lines.push(line)); + port.on('error', (err) => { clearTimeout(timer); reject(err); }); + }); +} + +export function resetDevice(portPath) { + try { + execSync(`python3 -m esptool --port ${portPath} run 2>/dev/null`, { timeout: 5000 }); + } catch {} +} -- cgit v1.2.3