diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/e2e/captive-portal.spec.mjs (renamed from tests/captive-portal.spec.mjs) | 2 | ||||
| -rw-r--r-- | tests/e2e/interop-happy-path.spec.mjs (renamed from tests/interop-happy-path.spec.mjs) | 0 | ||||
| -rw-r--r-- | tests/e2e/playwright.config.mjs (renamed from tests/playwright.config.mjs) | 2 | ||||
| -rw-r--r-- | tests/helpers/network.mjs | 2 | ||||
| -rw-r--r-- | tests/integration/api.mjs (renamed from tests/api.mjs) | 0 | ||||
| -rw-r--r-- | tests/integration/network.mjs (renamed from tests/network.mjs) | 2 | ||||
| -rw-r--r-- | tests/integration/phase2.mjs (renamed from tests/phase2.mjs) | 2 | ||||
| -rw-r--r-- | tests/integration/smoke.mjs (renamed from tests/smoke.mjs) | 2 | ||||
| -rw-r--r-- | tests/integration/test-dns-firewall.mjs | 123 | ||||
| -rw-r--r-- | tests/integration/test-reset-auth.mjs | 101 | ||||
| -rw-r--r-- | tests/integration/test-session-expiry.mjs | 103 |
11 files changed, 333 insertions, 6 deletions
diff --git a/tests/captive-portal.spec.mjs b/tests/e2e/captive-portal.spec.mjs index 9411183..ab9d4f1 100644 --- a/tests/captive-portal.spec.mjs +++ b/tests/e2e/captive-portal.spec.mjs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | import { test, expect } from '@playwright/test'; | 1 | import { test, expect } from '@playwright/test'; |
| 2 | 2 | ||
| 3 | const PORTAL_IP = process.env.TOLLGATE_IP || '192.168.4.1'; | 3 | const PORTAL_IP = process.env.TOLLGATE_IP || '10.192.45.1'; |
| 4 | const PORTAL_URL = `http://${PORTAL_IP}`; | 4 | const PORTAL_URL = `http://${PORTAL_IP}`; |
| 5 | const API_URL = `http://${PORTAL_IP}:2121`; | 5 | const API_URL = `http://${PORTAL_IP}:2121`; |
| 6 | 6 | ||
diff --git a/tests/interop-happy-path.spec.mjs b/tests/e2e/interop-happy-path.spec.mjs index fe4fd78..fe4fd78 100644 --- a/tests/interop-happy-path.spec.mjs +++ b/tests/e2e/interop-happy-path.spec.mjs | |||
diff --git a/tests/playwright.config.mjs b/tests/e2e/playwright.config.mjs index d4118b8..f4cbe01 100644 --- a/tests/playwright.config.mjs +++ b/tests/e2e/playwright.config.mjs | |||
| @@ -9,7 +9,7 @@ export default defineConfig({ | |||
| 9 | headless: true, | 9 | headless: true, |
| 10 | viewport: { width: 1280, height: 900 }, | 10 | viewport: { width: 1280, height: 900 }, |
| 11 | screenshot: 'on', | 11 | screenshot: 'on', |
| 12 | video: 'on', | 12 | video: 'retain-on-failure', |
| 13 | trace: 'on-first-retry', | 13 | trace: 'on-first-retry', |
| 14 | }, | 14 | }, |
| 15 | reporter: [['list'], ['html', { open: 'never' }]], | 15 | reporter: [['list'], ['html', { open: 'never' }]], |
diff --git a/tests/helpers/network.mjs b/tests/helpers/network.mjs index e4d5086..a2d889e 100644 --- a/tests/helpers/network.mjs +++ b/tests/helpers/network.mjs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | import { execSync } from 'child_process'; | 1 | import { execSync } from 'child_process'; |
| 2 | 2 | ||
| 3 | const ESP32_IP = process.env.TOLLGATE_IP || '192.168.4.1'; | 3 | const ESP32_IP = process.env.TOLLGATE_IP || '10.192.45.1'; |
| 4 | const TIMEOUT = 5000; | 4 | const TIMEOUT = 5000; |
| 5 | 5 | ||
| 6 | export function curl(args, expectStatus = null) { | 6 | export function curl(args, expectStatus = null) { |
diff --git a/tests/api.mjs b/tests/integration/api.mjs index 5218d7b..5218d7b 100644 --- a/tests/api.mjs +++ b/tests/integration/api.mjs | |||
diff --git a/tests/network.mjs b/tests/integration/network.mjs index 2d302ef..dcd7a9a 100644 --- a/tests/network.mjs +++ b/tests/integration/network.mjs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | import { execSync } from 'child_process'; | 1 | import { execSync } from 'child_process'; |
| 2 | 2 | ||
| 3 | const IP = process.env.TOLLGATE_IP || '192.168.4.1'; | 3 | const IP = process.env.TOLLGATE_IP || '10.192.45.1'; |
| 4 | let passed = 0, failed = 0; | 4 | let passed = 0, failed = 0; |
| 5 | 5 | ||
| 6 | function assert(condition, test) { | 6 | function assert(condition, test) { |
diff --git a/tests/phase2.mjs b/tests/integration/phase2.mjs index 91891e7..9eaa7d7 100644 --- a/tests/phase2.mjs +++ b/tests/integration/phase2.mjs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | import { execSync } from 'child_process'; | 1 | import { execSync } from 'child_process'; |
| 2 | 2 | ||
| 3 | const IP = process.env.TOLLGATE_IP || '192.168.4.1'; | 3 | const IP = process.env.TOLLGATE_IP || '10.192.45.1'; |
| 4 | const API = `http://${IP}:2121`; | 4 | const API = `http://${IP}:2121`; |
| 5 | let passed = 0, failed = 0; | 5 | let passed = 0, failed = 0; |
| 6 | 6 | ||
diff --git a/tests/smoke.mjs b/tests/integration/smoke.mjs index 19f96de..f89eeac 100644 --- a/tests/smoke.mjs +++ b/tests/integration/smoke.mjs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | import { execSync } from 'child_process'; | 1 | import { execSync } from 'child_process'; |
| 2 | 2 | ||
| 3 | const PORT = process.argv[2] || '/dev/ttyACM0'; | 3 | const PORT = process.argv[2] || '/dev/ttyACM0'; |
| 4 | const IP = process.env.TOLLGATE_IP || '192.168.4.1'; | 4 | const IP = process.env.TOLLGATE_IP || '10.192.45.1'; |
| 5 | const SSID = process.env.AP_SSID || 'TollGate'; | 5 | const SSID = process.env.AP_SSID || 'TollGate'; |
| 6 | 6 | ||
| 7 | console.log(`\n=== Smoke Test (30s) ===`); | 7 | console.log(`\n=== Smoke Test (30s) ===`); |
diff --git a/tests/integration/test-dns-firewall.mjs b/tests/integration/test-dns-firewall.mjs new file mode 100644 index 0000000..b69b524 --- /dev/null +++ b/tests/integration/test-dns-firewall.mjs | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | import { execSync } from 'child_process'; | ||
| 2 | |||
| 3 | const IP = process.env.TOLLGATE_IP || '10.192.45.1'; | ||
| 4 | const API = `http://${IP}:2121`; | ||
| 5 | let passed = 0, failed = 0; | ||
| 6 | |||
| 7 | function assert(cond, msg) { | ||
| 8 | if (cond) { console.log(` ✓ ${msg}`); passed++; } | ||
| 9 | else { console.log(` ✗ ${msg}`); failed++; } | ||
| 10 | } | ||
| 11 | |||
| 12 | function run(cmd) { | ||
| 13 | try { return execSync(cmd, { encoding: 'utf8', timeout: 15000 }); } | ||
| 14 | catch { return null; } | ||
| 15 | } | ||
| 16 | |||
| 17 | function runJson(cmd) { | ||
| 18 | const out = run(cmd); | ||
| 19 | try { return out ? JSON.parse(out) : null; } | ||
| 20 | catch { return null; } | ||
| 21 | } | ||
| 22 | |||
| 23 | function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } | ||
| 24 | |||
| 25 | function mintToken(amount = 21) { | ||
| 26 | run('cashu -h https://testnut.cashu.space invoice ' + amount + ' 2>&1'); | ||
| 27 | const out = run('cashu -h https://testnut.cashu.space send --legacy ' + amount + ' 2>&1'); | ||
| 28 | const match = out && out.match(/cashuA[a-zA-Z0-9_-]+/); | ||
| 29 | return match ? match[0] : null; | ||
| 30 | } | ||
| 31 | |||
| 32 | function dnsResolves(domain, server) { | ||
| 33 | const result = run(`nslookup -timeout=3 ${domain} ${server} 2>&1`); | ||
| 34 | return result && result.includes('Address') && !result.includes('NXDOMAIN'); | ||
| 35 | } | ||
| 36 | |||
| 37 | function dnsResolvesToSelf(domain) { | ||
| 38 | try { | ||
| 39 | const result = run(`nslookup ${domain} ${IP} 2>&1`); | ||
| 40 | return result && result.includes(IP); | ||
| 41 | } catch { | ||
| 42 | return false; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | function canPing(host = '8.8.8.8') { | ||
| 47 | const result = run(`ping -c 1 -W 2 -I wlp59s0 ${host} 2>/dev/null`); | ||
| 48 | return result && !result.includes('100% packet loss'); | ||
| 49 | } | ||
| 50 | |||
| 51 | console.log(`\n=== DNS + Firewall Integration Test (target: ${IP}) ===\n`); | ||
| 52 | |||
| 53 | console.log('--- Part 1: Before Authentication ---\n'); | ||
| 54 | |||
| 55 | console.log('1. DNS hijack: resolves to ESP32 AP IP'); | ||
| 56 | assert(dnsResolvesToSelf('google.com'), 'google.com resolves to AP IP'); | ||
| 57 | assert(dnsResolvesToSelf('random-test.example.com'), 'random domain resolves to AP IP'); | ||
| 58 | |||
| 59 | console.log('\n2. DNS hijack: upstream DNS not reachable'); | ||
| 60 | const upstreamResolve = run(`nslookup -timeout=3 google.com 8.8.8.8 2>&1`); | ||
| 61 | assert(!upstreamResolve || upstreamResolve.includes('connection timed out') || upstreamResolve.includes('no servers'), 'Upstream DNS unreachable before auth'); | ||
| 62 | |||
| 63 | console.log('\n3. Per-client NAT filter: ping blocked'); | ||
| 64 | assert(!canPing(), 'Ping to 8.8.8.8 blocked by NAT filter'); | ||
| 65 | |||
| 66 | console.log('\n4. Per-client NAT filter: HTTP blocked'); | ||
| 67 | const httpBefore = run(`curl -s --connect-timeout 5 -m 5 --interface wlp59s0 http://1.1.1.1/ 2>/dev/null`); | ||
| 68 | assert(!httpBefore || httpBefore.length === 0, 'HTTP blocked before auth'); | ||
| 69 | |||
| 70 | console.log('\n5. Captive portal and API still accessible'); | ||
| 71 | const portal = run(`curl -s --connect-timeout 5 http://${IP}/`); | ||
| 72 | assert(portal && portal.includes('TollGate'), 'Portal HTML accessible'); | ||
| 73 | const apiDisc = runJson(`curl -s --connect-timeout 5 ${API}/`); | ||
| 74 | assert(apiDisc && apiDisc.kind === 10021, 'API discovery accessible'); | ||
| 75 | |||
| 76 | console.log('\n--- Part 2: After Authentication ---\n'); | ||
| 77 | |||
| 78 | console.log('6. Reset + Pay'); | ||
| 79 | run(`curl -s --connect-timeout 10 http://${IP}/reset_authentication`); | ||
| 80 | await sleep(1000); | ||
| 81 | |||
| 82 | const token = mintToken(21); | ||
| 83 | assert(token !== null, 'Token generated'); | ||
| 84 | if (token) { | ||
| 85 | const payResult = runJson(`curl -s --connect-timeout 20 -X POST --data-binary '${token}' -H "Content-Type: application/cashu" ${API}/`); | ||
| 86 | assert(payResult && payResult.kind === 1022, 'Payment accepted'); | ||
| 87 | } | ||
| 88 | |||
| 89 | await sleep(1000); | ||
| 90 | |||
| 91 | console.log('\n7. DNS now forwards to upstream'); | ||
| 92 | assert(dnsResolveWorks('google.com'), 'DNS resolves to real IPs after auth'); | ||
| 93 | |||
| 94 | console.log('\n8. Per-client NAT filter: ping allowed'); | ||
| 95 | assert(canPing(), 'Ping to 8.8.8.8 allowed after auth'); | ||
| 96 | |||
| 97 | console.log('\n9. Per-client NAT filter: HTTP allowed'); | ||
| 98 | const httpAfter = run(`curl -s --connect-timeout 10 -m 10 --interface wlp59s0 http://1.1.1.1/ 2>/dev/null`); | ||
| 99 | assert(httpAfter && httpAfter.length > 0, 'HTTP allowed after auth'); | ||
| 100 | |||
| 101 | console.log('\n--- Part 3: After Revocation ---\n'); | ||
| 102 | |||
| 103 | console.log('10. Reset auth'); | ||
| 104 | run(`curl -s --connect-timeout 10 http://${IP}/reset_authentication`); | ||
| 105 | await sleep(1000); | ||
| 106 | |||
| 107 | console.log('\n11. DNS goes back to hijack'); | ||
| 108 | assert(dnsResolvesToSelf('google.com'), 'DNS hijack restored after revoke'); | ||
| 109 | |||
| 110 | console.log('\n12. Per-client NAT filter: ping blocked again'); | ||
| 111 | assert(!canPing(), 'Ping blocked after revoke'); | ||
| 112 | |||
| 113 | console.log('\n13. Per-client NAT filter: HTTP blocked again'); | ||
| 114 | const httpRevoke = run(`curl -s --connect-timeout 5 -m 5 --interface wlp59s0 http://1.1.1.1/ 2>/dev/null`); | ||
| 115 | assert(!httpRevoke || httpRevoke.length === 0, 'HTTP blocked after revoke'); | ||
| 116 | |||
| 117 | function dnsResolveWorks(domain) { | ||
| 118 | const result = run(`nslookup -timeout=3 ${domain} 2>&1`); | ||
| 119 | return result && result.includes('Address') && !result.includes(IP) && !result.includes('NXDOMAIN'); | ||
| 120 | } | ||
| 121 | |||
| 122 | console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`); | ||
| 123 | process.exit(failed > 0 ? 1 : 0); | ||
diff --git a/tests/integration/test-reset-auth.mjs b/tests/integration/test-reset-auth.mjs new file mode 100644 index 0000000..279b2f9 --- /dev/null +++ b/tests/integration/test-reset-auth.mjs | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | import { execSync } from 'child_process'; | ||
| 2 | |||
| 3 | const IP = process.env.TOLLGATE_IP || '10.192.45.1'; | ||
| 4 | const API = `http://${IP}:2121`; | ||
| 5 | const SUDO_PW = process.env.SUDO_PW || 'c03rad0r123'; | ||
| 6 | let passed = 0, failed = 0; | ||
| 7 | |||
| 8 | function assert(cond, msg) { | ||
| 9 | if (cond) { console.log(` ✓ ${msg}`); passed++; } | ||
| 10 | else { console.log(` ✗ ${msg}`); failed++; } | ||
| 11 | } | ||
| 12 | |||
| 13 | function run(cmd) { | ||
| 14 | try { return execSync(cmd, { encoding: 'utf8', timeout: 15000 }); } | ||
| 15 | catch { return null; } | ||
| 16 | } | ||
| 17 | |||
| 18 | function runJson(cmd) { | ||
| 19 | const out = run(cmd); | ||
| 20 | try { return out ? JSON.parse(out) : null; } | ||
| 21 | catch { return null; } | ||
| 22 | } | ||
| 23 | |||
| 24 | function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } | ||
| 25 | |||
| 26 | function mintToken(amount = 21) { | ||
| 27 | run('cashu -h https://testnut.cashu.space invoice ' + amount + ' 2>&1'); | ||
| 28 | const out = run('cashu -h https://testnut.cashu.space send --legacy ' + amount + ' 2>&1'); | ||
| 29 | const match = out && out.match(/cashuA[a-zA-Z0-9_-]+/); | ||
| 30 | return match ? match[0] : null; | ||
| 31 | } | ||
| 32 | |||
| 33 | function canPing(host = '8.8.8.8') { | ||
| 34 | const result = run(`ping -c 1 -W 2 -I wlp59s0 ${host} 2>/dev/null`); | ||
| 35 | return result && !result.includes('100% packet loss'); | ||
| 36 | } | ||
| 37 | |||
| 38 | console.log(`\n=== Reset Auth Integration Test (target: ${IP}) ===\n`); | ||
| 39 | |||
| 40 | console.log('1. Reset auth to clear state'); | ||
| 41 | const reset1 = run(`curl -s --connect-timeout 10 http://${IP}/reset_authentication`); | ||
| 42 | assert(reset1 && reset1.includes('reset'), 'Reset returns {"status":"reset"}'); | ||
| 43 | |||
| 44 | await sleep(1000); | ||
| 45 | |||
| 46 | console.log('\n2. Verify no session'); | ||
| 47 | const usage1 = run(`curl -s --connect-timeout 10 ${API}/usage`); | ||
| 48 | assert(usage1 && usage1.includes('-1/-1'), 'Usage is -1/-1 before payment'); | ||
| 49 | |||
| 50 | console.log('\n3. Verify internet blocked'); | ||
| 51 | assert(!canPing(), 'Ping blocked before payment'); | ||
| 52 | |||
| 53 | console.log('\n4. Pay with valid token'); | ||
| 54 | const token = mintToken(21); | ||
| 55 | assert(token !== null, 'Token generated'); | ||
| 56 | if (token) { | ||
| 57 | const payResult = runJson(`curl -s --connect-timeout 20 -X POST --data-binary '${token}' -H "Content-Type: application/cashu" ${API}/`); | ||
| 58 | assert(payResult && payResult.kind === 1022, 'Payment accepted (kind=1022)'); | ||
| 59 | const allotment = payResult && payResult.tags && payResult.tags.find(t => t[0] === 'allotment'); | ||
| 60 | assert(allotment && parseInt(allotment[1]) > 0, `Allotment: ${allotment ? allotment[1] : 'N/A'}ms`); | ||
| 61 | } | ||
| 62 | |||
| 63 | await sleep(1000); | ||
| 64 | |||
| 65 | console.log('\n5. Verify session active'); | ||
| 66 | const usage2 = run(`curl -s --connect-timeout 10 ${API}/usage`); | ||
| 67 | assert(usage2 && !usage2.includes('-1/-1'), `Usage: ${usage2}`); | ||
| 68 | |||
| 69 | console.log('\n6. Verify internet allowed'); | ||
| 70 | assert(canPing(), 'Ping works with active session'); | ||
| 71 | |||
| 72 | console.log('\n7. Reset auth while session active'); | ||
| 73 | const reset2 = run(`curl -s --connect-timeout 10 http://${IP}/reset_authentication`); | ||
| 74 | assert(reset2 && reset2.includes('reset'), 'Reset returns {"status":"reset"}'); | ||
| 75 | |||
| 76 | await sleep(1000); | ||
| 77 | |||
| 78 | console.log('\n8. Verify session cleared'); | ||
| 79 | const usage3 = run(`curl -s --connect-timeout 10 ${API}/usage`); | ||
| 80 | assert(usage3 && usage3.includes('-1/-1'), 'Usage is -1/-1 after reset'); | ||
| 81 | |||
| 82 | console.log('\n9. Verify internet blocked again'); | ||
| 83 | assert(!canPing(), 'Ping blocked after reset'); | ||
| 84 | |||
| 85 | console.log('\n10. Pay again (new token)'); | ||
| 86 | const token2 = mintToken(21); | ||
| 87 | if (token2) { | ||
| 88 | const pay2 = runJson(`curl -s --connect-timeout 20 -X POST --data-binary '${token2}' -H "Content-Type: application/cashu" ${API}/`); | ||
| 89 | assert(pay2 && pay2.kind === 1022, 'Second payment accepted'); | ||
| 90 | } | ||
| 91 | |||
| 92 | await sleep(1000); | ||
| 93 | |||
| 94 | console.log('\n11. Verify internet works again'); | ||
| 95 | assert(canPing(), 'Ping works with new session'); | ||
| 96 | |||
| 97 | console.log('\n12. Final reset'); | ||
| 98 | run(`curl -s --connect-timeout 10 http://${IP}/reset_authentication`); | ||
| 99 | |||
| 100 | console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`); | ||
| 101 | process.exit(failed > 0 ? 1 : 0); | ||
diff --git a/tests/integration/test-session-expiry.mjs b/tests/integration/test-session-expiry.mjs new file mode 100644 index 0000000..c8334ab --- /dev/null +++ b/tests/integration/test-session-expiry.mjs | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | import { execSync } from 'child_process'; | ||
| 2 | |||
| 3 | const IP = process.env.TOLLGATE_IP || '10.192.45.1'; | ||
| 4 | const API = `http://${IP}:2121`; | ||
| 5 | let passed = 0, failed = 0; | ||
| 6 | |||
| 7 | function assert(cond, msg) { | ||
| 8 | if (cond) { console.log(` ✓ ${msg}`); passed++; } | ||
| 9 | else { console.log(` ✗ ${msg}`); failed++; } | ||
| 10 | } | ||
| 11 | |||
| 12 | function run(cmd) { | ||
| 13 | try { return execSync(cmd, { encoding: 'utf8', timeout: 15000 }); } | ||
| 14 | catch { return null; } | ||
| 15 | } | ||
| 16 | |||
| 17 | function runJson(cmd) { | ||
| 18 | const out = run(cmd); | ||
| 19 | try { return out ? JSON.parse(out) : null; } | ||
| 20 | catch { return null; } | ||
| 21 | } | ||
| 22 | |||
| 23 | function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } | ||
| 24 | |||
| 25 | function mintToken(amount = 21) { | ||
| 26 | run('cashu -h https://testnut.cashu.space invoice ' + amount + ' 2>&1'); | ||
| 27 | const out = run('cashu -h https://testnut.cashu.space send --legacy ' + amount + ' 2>&1'); | ||
| 28 | const match = out && out.match(/cashuA[a-zA-Z0-9_-]+/); | ||
| 29 | return match ? match[0] : null; | ||
| 30 | } | ||
| 31 | |||
| 32 | function canPing(host = '8.8.8.8') { | ||
| 33 | const result = run(`ping -c 1 -W 2 -I wlp59s0 ${host} 2>/dev/null`); | ||
| 34 | return result && !result.includes('100% packet loss'); | ||
| 35 | } | ||
| 36 | |||
| 37 | console.log(`\n=== Session Expiry Integration Test (target: ${IP}) ===`); | ||
| 38 | console.log(`NOTE: This test waits 65s for session expiry. Total runtime ~80s.\n`); | ||
| 39 | |||
| 40 | console.log('1. Reset auth'); | ||
| 41 | run(`curl -s --connect-timeout 10 http://${IP}/reset_authentication`); | ||
| 42 | |||
| 43 | await sleep(1000); | ||
| 44 | |||
| 45 | console.log('\n2. Verify blocked before payment'); | ||
| 46 | assert(!canPing(), 'Ping blocked before payment'); | ||
| 47 | |||
| 48 | const usage0 = run(`curl -s --connect-timeout 10 ${API}/usage`); | ||
| 49 | assert(usage0 && usage0.includes('-1/-1'), 'Usage is -1/-1'); | ||
| 50 | |||
| 51 | console.log('\n3. Pay with valid token (21 sats = 60000ms)'); | ||
| 52 | const token = mintToken(21); | ||
| 53 | assert(token !== null, 'Token generated'); | ||
| 54 | if (token) { | ||
| 55 | const payResult = runJson(`curl -s --connect-timeout 20 -X POST --data-binary '${token}' -H "Content-Type: application/cashu" ${API}/`); | ||
| 56 | assert(payResult && payResult.kind === 1022, 'Payment accepted'); | ||
| 57 | } | ||
| 58 | |||
| 59 | await sleep(1000); | ||
| 60 | |||
| 61 | console.log('\n4. Verify session active'); | ||
| 62 | const usage1 = run(`curl -s --connect-timeout 10 ${API}/usage`); | ||
| 63 | assert(usage1 && !usage1.includes('-1/-1'), `Usage: ${usage1}`); | ||
| 64 | |||
| 65 | console.log('\n5. Verify internet works'); | ||
| 66 | assert(canPing(), 'Ping works with active session'); | ||
| 67 | |||
| 68 | const httpResult = run(`curl -s --connect-timeout 10 -m 10 --interface wlp59s0 http://1.1.1.1/ 2>/dev/null`); | ||
| 69 | assert(httpResult && httpResult.length > 0, 'HTTP request reaches internet'); | ||
| 70 | |||
| 71 | console.log('\n6. Waiting 65s for session expiry (allotment=60000ms)...'); | ||
| 72 | for (let i = 65; i > 0; i -= 5) { | ||
| 73 | process.stdout.write(`\r ${i}s remaining...`); | ||
| 74 | await sleep(Math.min(5000, i * 1000)); | ||
| 75 | } | ||
| 76 | console.log('\r Session should be expired now. '); | ||
| 77 | |||
| 78 | console.log('\n7. Verify session expired'); | ||
| 79 | const usage2 = run(`curl -s --connect-timeout 10 ${API}/usage`); | ||
| 80 | assert(usage2 && usage2.includes('-1/-1'), `Usage after expiry: ${usage2}`); | ||
| 81 | |||
| 82 | console.log('\n8. Verify internet blocked after expiry'); | ||
| 83 | assert(!canPing(), 'Ping blocked after session expiry'); | ||
| 84 | |||
| 85 | const httpResult2 = run(`curl -s --connect-timeout 5 -m 5 --interface wlp59s0 http://1.1.1.1/ 2>/dev/null`); | ||
| 86 | assert(!httpResult2 || httpResult2.length === 0, 'HTTP blocked after expiry'); | ||
| 87 | |||
| 88 | console.log('\n9. Pay again to verify renewal works'); | ||
| 89 | const token2 = mintToken(21); | ||
| 90 | if (token2) { | ||
| 91 | const pay2 = runJson(`curl -s --connect-timeout 20 -X POST --data-binary '${token2}' -H "Content-Type: application/cashu" ${API}/`); | ||
| 92 | assert(pay2 && pay2.kind === 1022, 'Renewal payment accepted'); | ||
| 93 | } | ||
| 94 | |||
| 95 | await sleep(1000); | ||
| 96 | |||
| 97 | console.log('\n10. Verify internet works after renewal'); | ||
| 98 | assert(canPing(), 'Ping works after renewal'); | ||
| 99 | |||
| 100 | run(`curl -s --connect-timeout 10 http://${IP}/reset_authentication`); | ||
| 101 | |||
| 102 | console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`); | ||
| 103 | process.exit(failed > 0 ? 1 : 0); | ||