# ───────────────────────────────────────────────────────────── # Test gate: run the API test suite against a throwaway Postgres, in a container # built from the same image as production. Integration tests need a live database, # so they cannot run during `docker build` (no DB there) — they run here instead. # # Build the images, then run the suite (non-zero exit on any failing test). `run` # starts api-test's dependencies, runs the suite, and exits with its code: # docker compose -f docker-compose.test.yml build # docker compose -f docker-compose.test.yml run --rm api-test # # The test database is created automatically by the postgres-test service # (POSTGRES_DB=itsaplan_test); its data is tmpfs, so nothing persists between runs. # Use as a Coolify pre-deployment step now, or drop the same command into a # GitHub Actions job later. # ───────────────────────────────────────────────────────────── # Its own project: without a name compose derives one from the directory and the test # containers join the self-hosting stack, which `docker compose down` then takes with it. name: itsaplan-test services: postgres-test: image: postgres:17-alpine # Frequent TRUNCATEs retain relation files until a checkpoint; keep the # disposable database below the small tmpfs limit of CI runners. command: [ 'postgres', '-c', 'checkpoint_timeout=30s', '-c', 'max_wal_size=128MB', '-c', 'min_wal_size=32MB', ] environment: POSTGRES_USER: itsaplan POSTGRES_PASSWORD: itsaplan # Name contains "test" — the resetDb() guard requires it. POSTGRES_DB: itsaplan_test # Ephemeral: the data directory lives in memory, wiped when the run ends. tmpfs: - /var/lib/postgresql/data healthcheck: test: ['CMD-SHELL', 'pg_isready -U itsaplan -d itsaplan_test'] interval: 3s timeout: 3s retries: 20 # Throwaway RustFS for the attachments feature test (object bytes). Ephemeral: # /data is tmpfs, wiped when the run ends. rustfs-test: image: rustfs/rustfs:1.0.0 environment: RUSTFS_ACCESS_KEY: minioadmin RUSTFS_SECRET_KEY: minioadmin tmpfs: - /data:uid=10001,gid=10001 healthcheck: test: ['CMD', 'curl', '-fsS', '-o', '/dev/null', 'http://127.0.0.1:9000/health'] interval: 3s timeout: 3s retries: 20 # One-shot: creates the attachments bucket, then exits. rustfs-test-init: image: rustfs/rustfs:1.0.0 depends_on: rustfs-test: condition: service_healthy entrypoint: > sh -c " curl -fsS -X PUT --aws-sigv4 aws:amz:us-east-1:s3 --user minioadmin:minioadmin http://rustfs-test:9000/planner-attachments && echo 'bucket ready' " api-test: build: context: . dockerfile: apps/api/Dockerfile # Empty install flags so the image keeps devDeps the test suite needs # (@elysiajs/eden); production builds default to --production. The bot and # worker suites run out of this image too, so their workspaces are installed # as well — the production image keeps only api's closure. args: INSTALL_FILTER: '--filter api --filter bot --filter worker' INSTALL_FLAGS: '' depends_on: postgres-test: condition: service_healthy rustfs-test-init: condition: service_completed_successfully environment: NODE_ENV: test DATABASE_URL: postgres://itsaplan:itsaplan@postgres-test:5432/itsaplan_test # Any fixed secret works for a throwaway test DB. BETTER_AUTH_SECRET: test-secret-not-for-production-use-only # Fixed 32-byte base64 key for the provider-credential encryption tests. APP_ENCRYPTION_KEY: dGVzdC1lbmNyeXB0aW9uLWtleS0zMmJ5dGVzLWxlbiE= API_URL: http://localhost:3000 APP_URL: http://localhost:3001 # Object store for the attachments test (throwaway RustFS above). S3_ENDPOINT: http://rustfs-test:9000 S3_REGION: us-east-1 S3_BUCKET: planner-attachments S3_ACCESS_KEY_ID: minioadmin S3_SECRET_ACCESS_KEY: minioadmin # Migrate the throwaway DB, then run the api, bot and worker suites. The api's # test script runs it in parallel workers, as locally; env above satisfies the # resetDb() guard. command: sh -c "bun run packages/db/src/migrate.ts && cd apps/api && bun run test && cd ../bot && bun test && cd ../worker && bun test" # Webhook delivery worker, built from the same image as production, so the test # gate also verifies the worker image builds and boots. It idles here (no events # are emitted during the api suite) and is torn down when api-test exits. A tick # before api-test migrates just logs and retries — it does not fail the gate. worker-test: build: context: . dockerfile: apps/worker/Dockerfile depends_on: postgres-test: condition: service_healthy environment: NODE_ENV: test DATABASE_URL: postgres://itsaplan:itsaplan@postgres-test:5432/itsaplan_test