upleb.uk

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

summaryrefslogtreecommitdiff
path: root/tests/integration
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integration')
-rw-r--r--tests/integration/test-market.mjs60
-rw-r--r--tests/integration/test-price-discovery.mjs138
2 files changed, 198 insertions, 0 deletions
diff --git a/tests/integration/test-market.mjs b/tests/integration/test-market.mjs
new file mode 100644
index 0000000..20f062f
--- /dev/null
+++ b/tests/integration/test-market.mjs
@@ -0,0 +1,60 @@
1import { execSync } from 'child_process';
2
3const API_URL = `http://${process.env.TOLLGATE_IP || '10.192.45.1'}:2121`;
4
5function run(cmd) {
6 try { return execSync(cmd, { encoding: 'utf8', timeout: 15000 }); }
7 catch (e) { return e.stdout || null; }
8}
9
10function runJson(cmd) {
11 const out = run(cmd);
12 try { return out ? JSON.parse(out) : null; }
13 catch { return null; }
14}
15
16let passed = 0, failed = 0;
17function assert(cond, msg) {
18 if (cond) { console.log(` PASS: ${msg}`); passed++; }
19 else { console.log(` FAIL: ${msg}`); failed++; }
20}
21
22console.log('=== test-market (GET /market) ===\n');
23
24console.log('--- /market endpoint responds ---');
25{
26 const data = runJson(`curl -s --connect-timeout 5 ${API_URL}/market`);
27 assert(data !== null, '/market returns valid JSON');
28 assert(typeof data.count === 'number', `count is number (got ${data?.count})`);
29 assert(Array.isArray(data.entries), 'entries is array');
30}
31
32console.log('\n--- /market entry structure ---');
33{
34 const data = runJson(`curl -s --connect-timeout 5 ${API_URL}/market`);
35 if (data && data.entries && data.entries.length > 0) {
36 const e = data.entries[0];
37 assert(typeof e.bssid === 'string', `bssid is string (got ${e.bssid})`);
38 assert(typeof e.ssid === 'string', `ssid is string (got ${e.ssid})`);
39 assert(typeof e.rssi === 'number', `rssi is number (got ${e.rssi})`);
40 assert(typeof e.price_per_step === 'number', `price_per_step is number (got ${e.price_per_step})`);
41 assert(typeof e.step_size === 'number', `step_size is number (got ${e.step_size})`);
42 assert(typeof e.metric === 'string', `metric is string (got ${e.metric})`);
43 } else {
44 console.log(' SKIP: no entries found (scan may not have run yet)');
45 }
46}
47
48console.log('\n--- /market with no discovered TollGates ---');
49{
50 const data = runJson(`curl -s --connect-timeout 5 ${API_URL}/market`);
51 if (data && data.count === 0) {
52 assert(data.entries.length === 0, 'empty entries array when count=0');
53 console.log(' INFO: no nearby TollGates discovered yet (expected if only one board)');
54 } else if (data && data.count > 0) {
55 console.log(` INFO: ${data.count} nearby TollGate(s) discovered`);
56 }
57}
58
59console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
60process.exit(failed > 0 ? 1 : 0);
diff --git a/tests/integration/test-price-discovery.mjs b/tests/integration/test-price-discovery.mjs
new file mode 100644
index 0000000..6762130
--- /dev/null
+++ b/tests/integration/test-price-discovery.mjs
@@ -0,0 +1,138 @@
1import { execSync } from 'child_process';
2
3const BOARD_A_IP = process.env.TOLLGATE_IP || '10.185.47.1';
4const BOARD_B_IP = process.env.TOLLGATE_B_IP || process.env.TOLLGATE_IP_B || '10.192.45.1';
5const API_A = `http://${BOARD_A_IP}:2121`;
6const API_B = `http://${BOARD_B_IP}:2121`;
7
8function run(cmd) {
9 try { return execSync(cmd, { encoding: 'utf8', timeout: 15000 }); }
10 catch (e) { return e.stdout || null; }
11}
12
13function runJson(cmd) {
14 const out = run(cmd);
15 try { return out ? JSON.parse(out) : null; }
16 catch { return null; }
17}
18
19let passed = 0, failed = 0;
20function assert(cond, msg) {
21 if (cond) { console.log(` PASS: ${msg}`); passed++; }
22 else { console.log(` FAIL: ${msg}`); failed++; }
23}
24
25function canReach(url) {
26 const result = run(`curl -s --connect-timeout 3 --max-time 5 -o /dev/null -w "%{http_code}" ${url}`);
27 return result && result.trim() !== '000' && result.trim() !== '';
28}
29
30console.log('=== test-price-discovery (two-board) ===\n');
31
32const reachA = canReach(`${API_A}/market`);
33const reachB = canReach(`${API_B}/market`);
34
35console.log(`Reachability: Board A=${reachA ? 'YES' : 'NO'}, Board B=${reachB ? 'YES' : 'NO'}\n`);
36
37if (!reachA && !reachB) {
38 console.log('FATAL: Neither board reachable. Check TOLLGATE_IP and TOLLGATE_B_IP');
39 process.exit(1);
40}
41
42console.log('--- Board A: market endpoint ---');
43{
44 if (reachA) {
45 const data = runJson(`curl -s --connect-timeout 5 --max-time 10 ${API_A}/market`);
46 assert(data !== null, 'Board A /market returns JSON');
47 assert(typeof data?.count === 'number', `Board A count is ${data?.count}`);
48 if (data && data.entries) {
49 console.log(` Board A sees ${data.count} nearby TollGate(s):`);
50 for (const e of data.entries) {
51 console.log(` ${e.ssid} (BSSID: ${e.bssid}) — ${e.price_per_step} sats/step, RSSI: ${e.rssi}`);
52 }
53 }
54 } else {
55 console.log(' SKIP: Board A not reachable');
56 }
57}
58
59console.log('\n--- Board B: market endpoint ---');
60{
61 if (reachB) {
62 const data = runJson(`curl -s --connect-timeout 5 --max-time 10 ${API_B}/market`);
63 assert(data !== null, 'Board B /market returns JSON');
64 assert(typeof data?.count === 'number', `Board B count is ${data?.count}`);
65 if (data && data.entries) {
66 console.log(` Board B sees ${data.count} nearby TollGate(s):`);
67 for (const e of data.entries) {
68 console.log(` ${e.ssid} (BSSID: ${e.bssid}) — ${e.price_per_step} sats/step, RSSI: ${e.rssi}`);
69 }
70 }
71 } else {
72 console.log(' SKIP: Board B not reachable');
73 }
74}
75
76console.log('\n--- Cross-discovery: Board A sees Board B ---');
77{
78 if (reachA) {
79 const mktA = runJson(`curl -s --connect-timeout 5 --max-time 10 ${API_A}/market`);
80 if (mktA && mktA.count > 0) {
81 const foundB = mktA.entries.some(e =>
82 e.ssid.startsWith('TollGate-') && e.bssid !== '' && e.price_per_step > 0
83 );
84 assert(foundB, `Board A discovered another TollGate (count=${mktA.count})`);
85 } else {
86 console.log(' INFO: Board A has 0 entries. Scan may need more time.');
87 }
88 } else {
89 console.log(' SKIP: Board A not reachable');
90 }
91}
92
93console.log('\n--- Cross-discovery: Board B sees Board A ---');
94{
95 if (reachB) {
96 const mktB = runJson(`curl -s --connect-timeout 5 --max-time 10 ${API_B}/market`);
97 if (mktB && mktB.count > 0) {
98 const foundA = mktB.entries.some(e =>
99 e.ssid.startsWith('TollGate-') && e.bssid !== '' && e.price_per_step > 0
100 );
101 assert(foundA, `Board B discovered another TollGate (count=${mktB.count})`);
102 } else {
103 console.log(' INFO: Board B has 0 entries. Scan may need more time.');
104 }
105 } else {
106 console.log(' SKIP: Board B not reachable');
107 }
108}
109
110console.log('\n--- Discovery data integrity ---');
111{
112 const boards = [];
113 if (reachA) {
114 const mktA = runJson(`curl -s --connect-timeout 5 --max-time 10 ${API_A}/market`);
115 if (mktA?.entries) boards.push({ name: 'A', data: mktA });
116 }
117 if (reachB) {
118 const mktB = runJson(`curl -s --connect-timeout 5 --max-time 10 ${API_B}/market`);
119 if (mktB?.entries) boards.push({ name: 'B', data: mktB });
120 }
121
122 for (const { name, data } of boards) {
123 for (const e of data.entries) {
124 assert(typeof e.price_per_step === 'number' && e.price_per_step > 0,
125 `Board ${name} entry has valid price (${e.price_per_step})`);
126 assert(typeof e.step_size === 'number' && e.step_size > 0,
127 `Board ${name} entry has valid step_size (${e.step_size})`);
128 assert(typeof e.metric === 'string' && e.metric.length > 0,
129 `Board ${name} entry has valid metric (${e.metric})`);
130 assert(typeof e.rssi === 'number',
131 `Board ${name} entry has valid RSSI (${e.rssi})`);
132 break;
133 }
134 }
135}
136
137console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
138process.exit(failed > 0 ? 1 : 0);