diff options
| author | Your Name <you@example.com> | 2026-05-15 17:03:40 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-15 17:03:40 +0530 |
| commit | a7d0a672d59bf8985a6fc0e61b49015fabd96513 (patch) | |
| tree | 46814d1757649a640f53805a8d9dfc1b0f354289 /tests/api.mjs | |
| parent | 8a2307a5ced6da94cc674602219d5a68a1246264 (diff) | |
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
Diffstat (limited to 'tests/api.mjs')
| -rw-r--r-- | tests/api.mjs | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/tests/api.mjs b/tests/api.mjs new file mode 100644 index 0000000..5218d7b --- /dev/null +++ b/tests/api.mjs | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | import { curl, curlBody, getPortalIP, canPing, canResolve, dnsResolvesToSelf } from './helpers/network.mjs'; | ||
| 2 | |||
| 3 | const IP = getPortalIP(); | ||
| 4 | let passed = 0, failed = 0; | ||
| 5 | |||
| 6 | function assert(condition, test) { | ||
| 7 | if (condition) { console.log(` ✓ ${test}`); passed++; } | ||
| 8 | else { console.log(` ✗ ${test}`); failed++; } | ||
| 9 | } | ||
| 10 | |||
| 11 | async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } | ||
| 12 | |||
| 13 | console.log(`\n=== API Tests (target: ${IP}) ===\n`); | ||
| 14 | |||
| 15 | // Test 3: Captive portal serves HTML | ||
| 16 | console.log('Test 3: GET / returns portal HTML'); | ||
| 17 | const body3 = curlBody(`http://${IP}/`); | ||
| 18 | assert(body3 && body3.includes('TollGate'), 'Portal HTML contains "TollGate"'); | ||
| 19 | assert(body3 && body3.includes('Grant Free Access'), 'Portal has Grant Access button'); | ||
| 20 | |||
| 21 | // Test 4: Captive detection URIs | ||
| 22 | console.log('\nTest 4: Captive detection URIs'); | ||
| 23 | for (const uri of ['/generate_204', '/hotspot-detect.html', '/canonical.html', '/success.txt', '/ncsi.txt', '/connecttest.txt', '/wpad.dat', '/redirect']) { | ||
| 24 | const code = curl(`http://${IP}${uri}`); | ||
| 25 | assert(code === '200', `${uri} → 200`); | ||
| 26 | } | ||
| 27 | |||
| 28 | // Test 7: /whoami returns MAC | ||
| 29 | console.log('\nTest 7: GET /whoami'); | ||
| 30 | const body7 = curlBody(`http://${IP}/whoami`); | ||
| 31 | assert(body7 && body7.startsWith('mac='), '/whoami returns mac=...'); | ||
| 32 | |||
| 33 | // Test 8: /usage returns no session | ||
| 34 | console.log('\nTest 8: GET /usage'); | ||
| 35 | const body8 = curlBody(`http://${IP}/usage`); | ||
| 36 | assert(body8 && body8.includes('-1/-1'), '/usage returns -1/-1 before auth'); | ||
| 37 | |||
| 38 | // Test 5: DNS hijack before auth | ||
| 39 | console.log('\nTest 5: DNS hijack before auth'); | ||
| 40 | assert(dnsResolvesToSelf('google.com'), 'DNS resolves google.com to AP IP'); | ||
| 41 | |||
| 42 | // Test 6: No internet before auth | ||
| 43 | console.log('\nTest 6: No internet before auth'); | ||
| 44 | assert(!canPing('8.8.8.8', 1), 'ping 8.8.8.8 fails before auth'); | ||
| 45 | |||
| 46 | // Test 9: Grant access | ||
| 47 | console.log('\nTest 9: GET /grant_access'); | ||
| 48 | const body9 = curlBody(`http://${IP}/grant_access`); | ||
| 49 | assert(body9 && body9.includes('"granted"'), 'Grant access returns {"status":"granted"}'); | ||
| 50 | |||
| 51 | await sleep(2000); | ||
| 52 | |||
| 53 | // Test 10: DNS forward after auth | ||
| 54 | console.log('\nTest 10: DNS forward after auth'); | ||
| 55 | assert(canResolve('google.com'), 'DNS resolves normally after auth'); | ||
| 56 | |||
| 57 | // Test 11: Internet after auth | ||
| 58 | console.log('\nTest 11: Internet after auth'); | ||
| 59 | assert(canPing('8.8.8.8'), 'ping 8.8.8.8 succeeds after auth'); | ||
| 60 | |||
| 61 | // Test 12: HTTP browsing works | ||
| 62 | console.log('\nTest 12: HTTP browsing'); | ||
| 63 | const body12 = curlBody('http://example.com/'); | ||
| 64 | assert(body12 && (body12.includes('Example Domain') || body12.includes('example')), 'HTTP page loads'); | ||
| 65 | |||
| 66 | // Test 13: Reset auth | ||
| 67 | console.log('\nTest 13: GET /reset_authentication'); | ||
| 68 | const body13 = curlBody(`http://${IP}/reset_authentication`); | ||
| 69 | assert(body13 && body13.includes('"reset"'), 'Reset returns {"status":"reset"}'); | ||
| 70 | |||
| 71 | await sleep(2000); | ||
| 72 | |||
| 73 | // Test 14: Internet blocked after reset | ||
| 74 | console.log('\nTest 14: Internet blocked after reset'); | ||
| 75 | assert(!canPing('8.8.8.8', 1), 'ping fails after auth reset'); | ||
| 76 | |||
| 77 | // Summary | ||
| 78 | console.log(`\n=== Results: ${passed} passed, ${failed} failed ===\n`); | ||
| 79 | process.exit(failed > 0 ? 1 : 0); | ||