Files
hackerspace.sh/docs/LOCAL_DEV.md
T
Moheeb Zara 6403220c92 test(integration): DB-backed harness validating shipped SQL (045/042/044/043)
Root-cause fix for 'correctness depended on post-deploy manual audits'.
Separate psql-driven suite (pnpm test:integration, own vitest config,
integration/ outside __tests__ so the hermetic pnpm test gate is
untouched; self-skips without a DB). 18 tests run against real Postgres:
- 045 class_signup_tx/class_cancel_tx: RETURNS TABLE->array shape the
  actions destructure, capacity/waitlist/dedupe, and the per-session
  advisory lock under TWO concurrent connections (D11 proven, never
  over-enrolls).
- 042 equipment exclusion constraint (overlap 23P01; cancelled/adjacent
  ok). 044 self-change trigger (role + payment cols blocked; service
  bypass). 043 members_update WITH CHECK (cross-space move blocked).
Disproves the pass-52 flagged risk (signup rpc-shape). Non-runtime;
default pnpm test still 532/26 hermetic; build clean.
2026-05-19 13:01:16 -07:00

229 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Local development quickstart
Fresh-clone to running app in under five minutes. Uses the Supabase CLI to spin up a complete local stack (Postgres + Auth + PostgREST + Realtime + Storage + Studio) in Docker. No Supabase account needed.
## Prerequisites
- macOS, Linux, or WSL2 on Windows.
- **Docker** (running). `docker info` should print a server section.
- **Homebrew** on macOS, or follow the [Supabase CLI install docs](https://supabase.com/docs/guides/cli/getting-started) for your OS.
- **Node 20+** and **pnpm 9+**. nvm works fine.
## 1. Install the Supabase CLI
```bash
brew install supabase/tap/supabase
supabase --version # 1.x or 2.x
```
## 2. Clone and install
```bash
git clone <repo-url>
cd hackerspace-management
pnpm install
```
## 3. Initialize and start the local Supabase stack
```bash
supabase init # writes supabase/config.toml; safe if it already exists
supabase start
```
First run pulls a few GB of images and takes 1–3 minutes. Subsequent starts are seconds.
When it finishes, the CLI prints something like:
```
API URL: http://127.0.0.1:54321
GraphQL URL: http://127.0.0.1:54321/graphql/v1
S3 Storage URL: http://127.0.0.1:54321/storage/v1/s3
DB URL: postgresql://postgres:[email protected]:54322/postgres
Studio URL: http://127.0.0.1:54323
Inbucket URL: http://127.0.0.1:54324
JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
anon key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
service_role key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
Save the **anon key** and **service_role key**. You can re-print them anytime with `supabase status`.
The DB is at `postgresql://postgres:[email protected]:54322/postgres`. Studio (Supabase dashboard, locally) is at `http://127.0.0.1:54323`. The Inbucket email catcher is at `http://127.0.0.1:54324` — every email the app would send shows up there.
## 4. Apply the schema and the seed
```bash
# Schema (canonical, covers every migration through 019)
psql "postgresql://postgres:[email protected]:54322/postgres" -f scripts/schema.sql
# Optional but recommended for local: load a demo space with members, tasks,
# proposals, an incident, a policy, and activity-log entries.
psql "postgresql://postgres:[email protected]:54322/postgres" -f scripts/seed.sql
```
You can verify with Studio: open `http://127.0.0.1:54323`, switch to the `public` schema, and see 18 tables including `proposals`, `incidents`, `policies`.
## 5. Generate TypeScript types from the live local schema
```bash
supabase gen types typescript --local > types/database.ts
```
This clears the masked TS errors that `next.config.mjs: typescript.ignoreBuildErrors: true` was hiding. After this you can drop that ignoreBuildErrors flag and any future schema drift surfaces in CI.
## 6. Write `.env.local`
```bash
cp .env.example .env.local
```
Edit `.env.local`:
```bash
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=<paste anon key from step 3>
SUPABASE_SERVICE_ROLE_KEY=<paste service_role key from step 3>
NEXT_PUBLIC_APP_URL=http://localhost:3000
NODE_ENV=development
```
## 7. Run the dev server
```bash
pnpm dev
```
Open `http://localhost:3000`. Health check: `curl http://localhost:3000/api/health` returns `{"status":"ok"}`.
## 8. Sign up as a real user and join the demo space
1. Visit `/signup`.
2. Pick **Join a Space** and enter invite code: `DEMO-2026-TEST`.
3. Fill in your email / password.
4. Local Supabase is configured to skip email confirmation by default, so signup takes you straight to `/dashboard`.
5. The dashboard now shows the seeded activity. Visit `/proposals`, `/incidents`, `/policies`, `/financials` to see the full feature surface.
If you want admin permissions for the demo: connect to the DB (or via Studio) and run:
```sql
UPDATE public.space_members
SET role = 'admin'
WHERE email = '[email protected]';
```
## Common dev tasks
### Wipe and re-seed
```bash
supabase db reset # drops & re-runs the database
psql "postgresql://postgres:[email protected]:54322/postgres" -f scripts/schema.sql
psql "postgresql://postgres:[email protected]:54322/postgres" -f scripts/seed.sql
```
### Apply a new incremental migration
```bash
psql "postgresql://postgres:[email protected]:54322/postgres" -f scripts/0NN_my_change.sql
```
PostgREST auto-refreshes on schema changes locally; no restart needed.
### Run tests
```bash
pnpm vitest run # 259 tests across 8 files; no DB needed
pnpm exec playwright test # E2E (Playwright will spin up the dev server)
```
### Type check
```bash
pnpm exec tsc --noEmit
```
After step 5, this should be clean. If it isn't, regenerate types.
### Stop the local stack
```bash
supabase stop
```
Containers are stopped but volumes persist. To nuke them entirely: `supabase stop --no-backup` then `supabase start` for a fresh slate.
### Tail logs
```bash
supabase logs # all services
supabase logs --type api
supabase logs --type db
```
### Run the proposal-expiry job manually
```sql
SELECT public.expire_proposals();
-- returns the number of proposals flipped from 'open' to 'decided' or 'expired'
```
In production you'd schedule this via pg_cron (the schema's migration 019 sets this up automatically when pg_cron is available) or via a Next.js cron endpoint guarded by a `CRON_SECRET`.
## Integration tests (DB-backed)
`pnpm test` is hermetic (pure logic, jsdom, no DB) and is the gate. The
separate **`pnpm test:integration`** suite (`integration/`, its own
`vitest.integration.config.ts`) exercises the shipped SQL — the
`class_signup_tx`/`class_cancel_tx` RPCs (045) including the advisory lock
under real concurrency, the `equipment_reservations_no_overlap` exclusion
constraint (042), and the self-change trigger / `members_update` WITH CHECK
(044/043) — against a real Postgres through `psql` (no extra npm dep). It
drives both access paths the app uses: the service path (no JWT) and a
logged-in member (`role authenticated` + `request.jwt.claims.sub`).
Prereqs: the Supabase CLI stack running (`supabase start`) with the schema
applied (`psql "$DB_URL" -f scripts/schema.sql`). It connects to
`INTEGRATION_DATABASE_URL` (default the local stack
`postgresql://postgres:[email protected]:54322/postgres`) and **self-skips
cleanly** when no DB is reachable, so it never blocks contributors/CI
without one. It is intentionally NOT part of `pnpm test` or the deploy gate.
```bash
supabase start
psql "postgresql://postgres:[email protected]:54322/postgres" -f scripts/schema.sql
pnpm test:integration
```
## Troubleshooting
### `supabase start` hangs or fails
Make sure Docker Desktop is open and `docker info` works. Some corporate VPNs interfere with the local images; turn the VPN off, run `supabase start`, then turn it back on.
### "relation does not exist" when the app runs
You skipped step 4. Apply `scripts/schema.sql`.
### "Invalid API key" toasts in the app
The anon key in `.env.local` doesn't match `supabase status`. `supabase stop --no-backup && supabase start` will regenerate the keys cleanly; paste the new ones into `.env.local`.
### Emails aren't arriving
Local Supabase routes outbound mail to Inbucket. Open `http://127.0.0.1:54324`.
### Signup says "Database error saving new user"
The `handle_space_signup` trigger fired but couldn't insert the space_members row. Almost always because `space_id` metadata was missing. The app's signup flow handles this correctly via the admin client; if you're testing direct `auth.users` inserts, that path doesn't write a space_members row by design.
### Realtime chat doesn't refresh
Confirm `comms_messages` and `comms_channels` are in `supabase_realtime`:
```sql
SELECT tablename FROM pg_publication_tables WHERE pubname = 'supabase_realtime';
```
If they aren't, re-run `scripts/schema.sql` (Section 8 handles it idempotently).