Testing
HQ has automated tests across four runtimes and a manual integration test plan for Docker stack validation.Automated tests
Run the full automated suite locally:What’s tested
| Runtime | Framework | Tests | What it covers |
|---|---|---|---|
| TypeScript (UI) | Vitest + jsdom + React Testing Library | ~2,270 | Hooks, lib modules, components, server actions, API routes |
| Python (Gateway) | pytest | 208 | All gateway daemons (command runner, dispatcher, embedder, secrets sync, source sync, plugin runner, file processor) |
| Shell (Scripts) | bash runner | 11 | Gateway lifecycle scripts |
| Database | psql assertions (CI only) | ~50 | RLS policies, schema contracts, triggers |
CI pipeline
Every PR runs 8 parallel jobs:ui-lint, ui-test, ui-build, worker, python-lint, python-test, shellcheck, shell-test. Pushes to main add db-contracts (live Postgres) and coverage-report with artifact upload.
Coverage thresholds (statements: 40%, lines: 40%, branches: 35%, functions: 35%) are enforced in CI. If you add new source files, run make test-coverage locally to verify.
Writing tests
SeeTESTING.md in the repo root for the full guide: test structure, mock patterns, factories, and rules.
Manual integration testing
Staged test plan for validating the Docker stack. Each stage is independently testable; stop at the first failure, debug, retry that stage.Environment
Recommended: GitHub Codespaces on this repo’s default branch.- Open the repo on GitHub → Code → Codespaces → Create codespace.
- Wait ~3 min for the devcontainer to boot.
- The
postCreateCommandinstalls UI deps, copies.env.example→.env, and prints a quick-start banner.
3000 (UI). noVNC is proxied through the UI — no separate port needed.
You’ll also need a throwaway Supabase project — do not point this at production while testing. Go to supabase.com, create a free project, and run every SQL file in db/migrations/ in filename order. Then copy the URL, anon key, and service role key for browser onboarding.
Stage 1 — UI build and run
Prove the UI Dockerfile is correct and the standalone build works in a container.- Build completes without errors (first build: 3–5 min — Next.js compile + optimize).
uicontainer shows “Ready on http://0.0.0.0:3000” in logs.- Codespaces shows a notification for port 3000; click to open the URL.
- Browser loads the onboarding or login page. If the workspace registry is empty, onboarding is expected.
ENOENT ./public/...→ a COPY path in the Dockerfile is wrong.Cannot find module ...→ a build-time dep didn’t make it into the standalone bundle; checknext.config.tshasoutput: "standalone".
docker compose down ui
Stage 2 — UI backed by your throwaway Supabase
Prove the UI connects to Supabase and renders real data.- Start the UI and complete browser onboarding with the throwaway Supabase URL, anon key, and service role key.
- Create or sign in with a Supabase auth user when prompted. You can also create one manually in Supabase → Authentication → Users → Add user.
- Open the UI, log in with the user you just created.
- Onboarding wizard appears (fresh workspace).
- Complete the wizard (welcome → intent → infrastructure → provider → agent). Workspace marked initialized, first agent created.
- Navigates to Tasks page with first task pre-filled. Dashboard loads. Sidebar footer says “HQ”.
Stage 3 — Gateway stack against the same Supabase
Bring up gateway + dispatcher + runner. UI stays off for this stage.gateway logs:
First boot — initializing bare repo at /home/openclaw/.openclaw/repo.gitSeeding templates from /opt/templates ... ✓ seeded branch default, then 15 more templates (16 total).Starting Xtigervnc :1 ...Running openclaw onboard ...(may take 30–60s).Patching openclaw.json ...Starting VNC server on :1 ...Starting websockify on 127.0.0.1:6901 -> localhost:5901 ...Registering gateway default in Supabase ... ✓ registeredStarting openclaw gateway ...
- Navigate to Settings → System. You should see one gateway
defaultwith statusonlineandlast_seen_atrecent.
runner container logs should show Starting command runner for gateway=default (Primary gateway).
Common failures:
- Git clone failure on
GIT_REMOTE_URL— expected if unset (templates seed locally instead). openclaw onboardexits non-zero — check Node version inside container (docker compose exec gateway node --version→ must be 24).Registering gateway ...fails with 403 →SUPABASE_SERVICE_ROLE_KEYis wrong or the gateways table doesn’t exist (re-run the migration).
Stage 4 — Codex OAuth
One-time, writes the token into theopenclaw-state volume shared by all agents on this gateway.
Auth profile saved.
Stage 5 — Create an agent end-to-end
Through the UI: navigate to Agents → New Agent → pick the Cofounder template → give it a name/slug → choose a channel (e.g. Telegram) and enter credentials → click Create. Watchdocker compose logs -f runner:
- Command
provisionleased. add-agent.sh cofounder-xyz --channel telegram --telegram-token ...runs.- Git worktree created at
/home/openclaw/.openclaw/workspace-.../. openclaw.jsonpatched with the agent entry and channel binding.- Gateway restarted.
online within a minute.
For Telegram/Discord: send a DM to the bot. Expect a pairing code reply. Paste the code into the agent’s detail page. Command runner runs openclaw pairing approve <channel> <code>. Next message triggers the agent. For Slack: the agent is active immediately — no pairing step.
Stage 6 — noVNC desktop view
Open the UI and navigate to an agent’s detail page → click the Desktop button (or Settings → Gateways → Desktop). Expected: the noVNC modal loads inside the UI. The XFCE desktop appears with (probably) the agent’s Chrome window if an agent is running. The UI proxies noVNC through/api/novnc — no separate port needed.
Tear down
What this does NOT test
- Tailscale join — disabled in Codespaces (NET_ADMIN + tun device behavior differs in the devcontainer env).
- Public HTTPS mode — needs a real domain + DNS record.
- Multi-arch builds — CI/GHCR catches that separately.
- The
curl | bashinstaller flow — best tested on a fresh VPS (see below).
After Codespaces: real-host validation
Once everything above passes, spin up a small VPS ($5/mo on Hetzner) or EC2 t3.small. SSH in and run:.env generation, image pull from GHCR, first-boot on a host you didn’t pre-configure. Tear down the VPS after.
