Per-workspace test setup
| Workspace | Runner | Notes |
|---|---|---|
apps/server | Vitest, environment: node | tests/**/*.test.ts, setup vitest.setup.ts |
apps/studio | Vitest, environment: jsdom | globals: true, clearMocks: true, @ → src alias |
packages/client | vitest run --typecheck | Type-level assertions run with the tests |
packages/ssr, packages/ai | vitest run | |
packages/cli | node --test | Node's built-in runner |
npm test # turbo run test (builds first)
npm test -w @motherbase/server
npm test -w studio
npm run test:watch -w @motherbase/server
npm test -w @motherbase/server -- <name-filter>turbo.json makes test depend on build, so tests always run against built packages rather than stale dist output.
Server test conventions
apps/server/tests holds ~190 files, split by suffix:
| Suffix | Meaning |
|---|---|
*.unit.test.ts | No external services |
*.integration.test.ts | Needs a real Postgres |
*.test.ts | Service-level tests |
*.sh | Smoke tests run against a deployed VPS |
Some import tests skip themselves unless RUN_IMPORT_VERIFICATION_DB_TESTS=1 is set with a real database available — CI sets it.
Extra suites:
npm run test:contracts -w @motherbase/server # SDK ↔ server contract
npm test -w @motherbase/server -- sdk-contract # regenerates the compatibility matrixStudio mocks
apps/studio/src/mocks (browser.ts, handlers.ts, fixtures.ts) backs both the component tests and npm run studio:mock, so UI work needs no backend.
CI
.github/workflows/ci.yml, on push to master, on every PR, and manually.
The ci job runs on ubuntu-24.04 with a postgres:16.14-alpine3.23 service and a 15-minute timeout:
- Validate deployment Compose files —
docker compose config --quietondeploy/console/docker-compose.yml,deploy/docker-compose.prod.ymlanddeploy/traefik/docker-compose.yml, with placeholder secrets. A malformed compose file fails before anything is built. npm cinpm run prisma:generatenpm run prisma:deploynpm run lint— all seven workspacesnpm run buildnpm testnpm run test:contracts -w @motherbase/server- Regenerate the SDK compatibility matrix and fail on drift in
packages/client/COMPATIBILITY.md.
Step 9 exists because the supabase-js compatibility matrix is generated, never hand-written. CI regenerates it against its own Postgres and fails if the committed copy differs — a matrix that can lie is exactly what this guards against. If it fails, run npm run test -w apps/server -- sdk-contract and commit the result.
Deploy job
deploy-vps runs only after ci, never on pull requests, and only on main/master. It:
- skips itself cleanly when
VPS_HOST/VPS_SSH_KEYare not configured; - refuses to deploy as
root; - SSHes in, fast-forwards the branch, and runs
./setup.sh --yes --skip-dns-check; - runs
tests/vps-smoke.sh, andtests/critical-flows-smoke.shwhen the smoke-test variables are configured.
Concurrency is grouped per branch with cancel-in-progress: false, so two deploys never interleave.