pi-remote-control/scripts/smoke
jay a7dad86901 feat(t-1.0a): smoke test harness MVP
- scripts/smoke/helpers.mjs — spawn-pi (via python3 pty.spawn), wait-for-port,
  fetch, WebSocket helpers; createSmokeHome/removeSmokeHome for isolated HOME
- scripts/smoke/smoke.mjs — 6 node:test assertions:
    GET /manifest.json → 200 + JSON shape
    GET /icon.svg → 200 + <svg body
    GET / (with token) → 302→200 + HTML marker
    GET / (no token) → 403
    WS /ws (with token) → 101 upgrade
    pi process alive check
- scripts/smoke/README.md — usage, design notes, extension guide
- package.json: add 'smoke' script
- docs/SYNC.md: add scripts/smoke/** ownership row + History entry

All 6 tests pass in ~1.4 s locally.

Key finding: pi requires a PTY to enter interactive mode and fire
session_start. Spawning without a TTY causes immediate exit. Workaround:
python3 pty.spawn() — allocates a PTY with no additional deps.
2026-05-15 11:18:20 +02:00
..
README.md feat(t-1.0a): smoke test harness MVP 2026-05-15 11:18:20 +02:00
helpers.mjs feat(t-1.0a): smoke test harness MVP 2026-05-15 11:18:20 +02:00
smoke.mjs feat(t-1.0a): smoke test harness MVP 2026-05-15 11:18:20 +02:00

README.md

Smoke Test Harness

End-to-end smoke tests for the remote-control extension. These tests spawn a real pi subprocess with the extension loaded and hit the HTTP/WebSocket endpoints to verify they respond correctly.

Running

npm run smoke

Or directly:

node --test scripts/smoke/smoke.mjs

What is tested (MVP — T-1.0a)

# Test Description
1 Server starts pi spawns with --remote-control; TCP port opens within 12 s
2 GET /manifest.json 200, application/manifest+json, parses, has name/short_name/start_url
3 GET /icon.svg 200, image/svg+xml, body starts with <svg
4 GET / (with token) 302→200, text/html, contains HTML marker
5 GET / (no token) 403 Forbidden
6 WS /ws (with token) 101 upgrade, WebSocket enters OPEN state
7 Process alive pi hasn't crashed during the test run
T Teardown SIGTERM pi, wait for exit, remove temp HOME

Port

Default port: 19876. Override with the SMOKE_PORT environment variable:

SMOKE_PORT=20000 npm run smoke

How it works

The harness creates a temporary HOME directory containing:

  • $HOME/.pi/remote-control/config.jsonpublicBaseUrl + bindAddress pointing at SMOKE_PORT
  • $HOME/.pi/remote-control/token — a known deterministic token for auth

pi is spawned with HOME=<tmpdir> so no real ~/.pi files are read or written. The temp directory is deleted in the after() teardown hook even if tests fail.

Authentication

The server requires either a valid session cookie or ?token=<value> query parameter. The smoke tests pre-seed a known token (smoke-test-token-deterministic-1234) and pass it in the ?token= parameter. No production auth logic is modified.

Adding new tests

  1. Create a new file scripts/smoke/<feature>.test.mjs
  2. Import helpers from ./helpers.mjs
  3. Import your test file from smoke.mjs (or run it standalone with node --test)

Example:

// scripts/smoke/stream.test.mjs
import { describe, it } from "node:test";
import { openWebSocket, closeWebSocket } from "./helpers.mjs";

export function registerStreamTests(port, token) {
  describe("stream tests", () => {
    it("WS /sessions/:id/stream → 101", async () => {
      // ...
    });
  });
}

Flags used

Same flags as make dev:

pi -nt -ne -ns -np -nc --no-session --offline -e extensions/remote-control --remote-control

Troubleshooting

Port busy: if SMOKE_PORT is already in use, the test will fail at the waitForPort step. Change the port: SMOKE_PORT=20001 npm run smoke.

pi not found: ensure pi is on your PATH. Check with which pi.

Server not starting: run with verbose output to see what pi logs:

node --test --reporter spec scripts/smoke/smoke.mjs

The port-wait error will include captured stdout/stderr from pi.