# Testing ## Test stack | Piece | Tool | |-------|------| | Runner | [brittle](https://github.com/holepunchto/brittle) via `brittle-node` | | Location | `test/*.test.js` | | Command | `npm test` | ## Suite map | File | Coverage | |------|----------| | `test/acl.test.js` | Role hierarchy, `assertAllowed` | | `test/crypto-auth.test.js` | MAC key, capabilities, admin proof, invites, classify input | | `test/protocol.test.js` | Constants, monitoring `MethodRoles`, schema validators | | `test/store.test.js` | Metric ring buffer ingest + query | | `test/weights.test.js` | Metric Correlations engine (`volume` / `ks2` / windows / errors) | | `test/logs.test.js` | Log query engine (roles, filters, journal argv, audit file) | | `test/retention.test.js` | Retention presets, store trim, warm prune delete, Data Manager RPC roles | | `test/format.test.js` | KPI unit scaling (`formatMib`, `formatKilobitsPerSec`, `formatBytes`) | | `test/container-names.test.js` | Docker/Compose display names, cgroup resolve, card subtitles | | `test/docker-collector.test.js` | Docker chart helpers + socket resolve | | `test/hyperdb.test.js` | HyperDB model (node, links, warm points, alerts) | | `test/rest.test.js` | agent-style `/api/v3` routes | | `test/integration.test.js` | Live HyperDHT agent + handshake + metrics query | ```bash npm test npm run test:integration # Skip live DHT (CI runners without UDP, offline laptops) SKIP_INTEGRATION=1 npm test ``` ## Integration test behavior 1. Starts metrics pipeline + ephemeral HyperDHT agent in-process 2. Sets `PEARDATA_INSECURE_OPEN_ADMIN=1` for the process (restored in teardown) 3. Dials as a client, handshakes, lists charts, queries `system.cpu` 4. Tears down collector / sockets / DHT Requires outbound/inbound UDP for HyperDHT. If the test hangs or fails on a locked-down network, use `SKIP_INTEGRATION=1`. ## Writing tests ### Unit (preferred for pure logic) - Put pure helpers in `shared/` or thin `server/core` modules - Assert without networking - Cover: validation failures, role denials, crypto tampering, expiry Example pattern: ```js import test from 'brittle' import { roleAllows, Roles } from '../shared/protocol.js' test('operator cannot admin-only methods', (t) => { t.ok(roleAllows(Roles.operator, Roles.viewer)) t.absent(roleAllows(Roles.operator, Roles.admin)) }) ``` ### Integration (critical paths) - Boot real `PeerSession` stack or full server when AuthZ + wire encoding matter - Always clean up DHT / sockets in `t.teardown` - Prefer one happy-path + one auth-failure path over many flaky cases ### Schema / protocol When adding an RPC method: 1. `MethodRoles` entry 2. `validateMethodArgs` case (if args matter) 3. Unit test for validator 4. Optional integration call for the happy path ## Manual checks | Check | How | |-------|-----| | Server boots | `npm run start:server` — prints public key | | Invite mint | `npm run mint-invite -- operator` | | Desktop chrome | `npm start` — drag titlebar, resize edges, min/max/close | | Admin connect | Paste key + `SERVER_SEED` in UI | | Invite connect | Paste `pd1.…` without seed | | Viewer denial | Public key only → `runJob` / `mintInvite` fail with permission error | | Health | With server up: `SERVER_PUBLIC_KEY=… npm run healthcheck` | | Soak | `SERVER_PUBLIC_KEY=… SERVER_SEED=… npm run soak` | | Metric Correlations | Charts → Correlate → brush ≥15s → Find Correlations → wall filters; Clear restores; Related ⇢ still works | | Charts Filters | Filters closed → full-width wall; `/` opens search; chip when filter active; Esc closes panel | | Weights REST | `curl -sG 'http://127.0.0.1:18888/api/v3/weights' --data-urlencode 'method=volume' --data-urlencode 'after=-60' --data-urlencode 'before=0'` | | Logs | Journal default (admin); Follow / Refresh / search highlight; Anomalies for viewers; Audit admin-only | | Logs REST | `curl -sG 'http://127.0.0.1:18888/api/v3/logs' --data-urlencode 'source=journal' --data-urlencode 'limit=20'` | | Data Manager | Settings → Data (admin): usage cards, 3m warm + 1 GiB budget default, Save, dry-run prune | | Settings REST | `curl -s http://127.0.0.1:18888/api/v3/settings \| jq '.retention,.storage'` | | Docker names | With `PEARDATA_DOCKER=1` + socket access: Charts → Containers shows `dozzle · CPU` (not bare hashes) | ## Soak test ```bash # default 60s SERVER_PUBLIC_KEY= SERVER_SEED= npm run soak SOAK_DURATION_MS=300000 SOAK_INTERVAL_MS=250 npm run soak ``` Reports sent / received / errors. Use before release when changing session or room code. ## CI GitHub CI runs `npm test` on Node 20 and 22. Gitea CI runs `npm test` on Node 22 plus a liveness `healthcheck`. See [CI.md](./CI.md). ## Coverage philosophy This template prioritizes **critical pure paths + one live DHT smoke test** over heavy mock frameworks. When productizing: - Add domain unit tests next to new services - Keep integration tests few and deterministic - Gate flaky network tests behind `SKIP_INTEGRATION`