upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/tests/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'tests/helpers')
-rw-r--r--tests/helpers/network.mjs89
-rw-r--r--tests/helpers/serial.mjs82
2 files changed, 171 insertions, 0 deletions
diff --git a/tests/helpers/network.mjs b/tests/helpers/network.mjs
new file mode 100644
index 0000000..e4d5086
--- /dev/null
+++ b/tests/helpers/network.mjs
@@ -0,0 +1,89 @@
1import { execSync } from 'child_process';
2
3const ESP32_IP = process.env.TOLLGATE_IP || '192.168.4.1';
4const TIMEOUT = 5000;
5
6export function curl(args, expectStatus = null) {
7 const cmd = `curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time ${TIMEOUT/1000} ${args}`;
8 try {
9 const result = execSync(cmd, { encoding: 'utf8', timeout: TIMEOUT + 2000 }).trim();
10 if (expectStatus && result !== String(expectStatus)) {
11 throw new Error(`Expected HTTP ${expectStatus}, got ${result}`);
12 }
13 return result;
14 } catch (e) {
15 if (e.status === 'ETIMEDOUT' || e.killed) return 'TIMEOUT';
16 throw e;
17 }
18}
19
20export function curlBody(url) {
21 const cmd = `curl -s --connect-timeout 5 --max-time ${TIMEOUT/1000} "${url}"`;
22 try {
23 return execSync(cmd, { encoding: 'utf8', timeout: TIMEOUT + 2000 });
24 } catch {
25 return null;
26 }
27}
28
29export function getPortalIP() { return ESP32_IP; }
30
31export function canPing(host = '8.8.8.8', count = 2) {
32 try {
33 const result = execSync(`ping -c ${count} -W 2 -I wlp59s0 ${host}`, { encoding: 'utf8', timeout: 10000 });
34 return result.includes('0% packet loss') || result.includes('1 packets transmitted');
35 } catch {
36 return false;
37 }
38}
39
40export function canResolve(domain = 'google.com') {
41 try {
42 const result = execSync(`nslookup ${domain} ${ESP32_IP}`, { encoding: 'utf8', timeout: 10000 });
43 return result.includes('Address') && !result.includes('NXDOMAIN');
44 } catch (e) {
45 const result = e.stdout || '';
46 return result.includes('Address') && !result.includes('NXDOMAIN');
47 }
48}
49
50export function dnsResolvesToSelf(domain = 'google.com') {
51 try {
52 const result = execSync(`nslookup ${domain} ${ESP32_IP}`, { encoding: 'utf8', timeout: 10000 });
53 return result.includes(ESP32_IP);
54 } catch (e) {
55 return e.stdout && e.stdout.includes(ESP32_IP);
56 }
57}
58
59export function connectToAP(ssid, password = '') {
60 try {
61 if (password) {
62 execSync(`nmcli dev wifi connect "${ssid}" password "${password}" ifname wlan0`, { timeout: 30000 });
63 } else {
64 execSync(`nmcli dev wifi connect "${ssid}" ifname wlan0`, { timeout: 30000 });
65 }
66 return true;
67 } catch {
68 return false;
69 }
70}
71
72export function disconnectAP() {
73 try {
74 execSync('nmcli dev disconnect wlan0 2>/dev/null || true', { timeout: 10000 });
75 return true;
76 } catch {
77 return false;
78 }
79}
80
81export function getWifiInterface() {
82 try {
83 const result = execSync('nmcli -t -f DEVICE,TYPE dev status', { encoding: 'utf8' });
84 const line = result.split('\n').find(l => l.includes('wifi'));
85 return line ? line.split(':')[0] : null;
86 } catch {
87 return null;
88 }
89}
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 @@
1import { SerialPort } from 'serialport';
2import { ReadlineParser } from '@serialport/parser-readline';
3import { execSync } from 'child_process';
4
5const DEFAULT_BAUD = 115200;
6const BOOT_TIMEOUT = 30000;
7
8export async function execSerial(portPath, command, timeoutMs = 5000) {
9 return new Promise((resolve, reject) => {
10 const port = new SerialPort({ path: portPath, baudRate: DEFAULT_BAUD });
11 const parser = port.pipe(new ReadlineParser());
12 const lines = [];
13 let resolved = false;
14
15 const timer = setTimeout(() => {
16 if (!resolved) { resolved = true; port.close(); resolve(lines.join('\n')); }
17 }, timeoutMs);
18
19 parser.on('data', (line) => {
20 lines.push(line);
21 if (line.includes('___END___') && !resolved) {
22 resolved = true;
23 clearTimeout(timer);
24 port.close();
25 resolve(lines.join('\n'));
26 }
27 });
28
29 port.on('open', () => {
30 port.write(command + '\n');
31 });
32
33 port.on('error', (err) => {
34 if (!resolved) { resolved = true; clearTimeout(timer); reject(err); }
35 });
36 });
37}
38
39export async function waitForBoot(portPath, timeoutMs = BOOT_TIMEOUT) {
40 return new Promise((resolve, reject) => {
41 const port = new SerialPort({ path: portPath, baudRate: DEFAULT_BAUD });
42 const parser = port.pipe(new ReadlineParser());
43 const timer = setTimeout(() => {
44 port.close();
45 reject(new Error('Boot timeout'));
46 }, timeoutMs);
47
48 parser.on('data', (line) => {
49 if (line.includes('TollGate services started') || line.includes('WiFi AP+STA started')) {
50 clearTimeout(timer);
51 setTimeout(() => { port.close(); resolve(true); }, 500);
52 }
53 });
54
55 port.on('error', (err) => {
56 clearTimeout(timer);
57 reject(err);
58 });
59 });
60}
61
62export async function readSerial(portPath, durationMs = 3000) {
63 return new Promise((resolve, reject) => {
64 const port = new SerialPort({ path: portPath, baudRate: DEFAULT_BAUD });
65 const parser = port.pipe(new ReadlineParser());
66 const lines = [];
67
68 const timer = setTimeout(() => {
69 port.close();
70 resolve(lines.join('\n'));
71 }, durationMs);
72
73 parser.on('data', (line) => lines.push(line));
74 port.on('error', (err) => { clearTimeout(timer); reject(err); });
75 });
76}
77
78export function resetDevice(portPath) {
79 try {
80 execSync(`python3 -m esptool --port ${portPath} run 2>/dev/null`, { timeout: 5000 });
81 } catch {}
82}