Six phases claimed the product runs on SQLite and on Postgres. Nothing had ever run it on Postgres. TEST_DATABASE_URL now points the whole suite at a real server and CI runs both arms. The first run found a bug that would have shipped. better-auth's banned flag is integer 0/1 on SQLite and a real boolean on Postgres, and the code read it as `banned === 1`, so on Postgres an account someone asked us to freeze went on receiving email. Both of those flags are now typed for either dialect and read through isFlagSet. It also found the migration advisory lock being taken on a pool. An advisory lock belongs to the session that took it, so a lock on one pooled connection and an unlock on another leaves it held. Two replicas migrating at once is the ordinary case in k8s and is precisely what it was there to protect. New for the scaled mode: k8s manifests with migrations as an initContainer, one poller in its own worker Deployment rather than one per API replica, and an Ingress that exposes the web app only. The storage drivers finally have tests, S3 included, since scaled mode requires it and it had never been exercised. Two acceptance tests, both checked against a deliberately broken build first: two workers claiming a hundred jobs report 188 claims with SKIP LOCKED removed, and the in-flight request is cut off with the drain wait removed. 340 tests on SQLite, 341 on Postgres, 70 Playwright, rules coverage 100%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
132 lines
4.3 KiB
YAML
132 lines
4.3 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main, master]
|
|
pull_request:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# Everything that does not need a database, once.
|
|
static:
|
|
name: types, lint and rules coverage
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm typecheck
|
|
- run: pnpm lint
|
|
# RULES.md section 10: the tax logic is the product, so its coverage is a gate.
|
|
- run: pnpm test:coverage
|
|
|
|
# The same suite against both dialects. SPEC.md section 13: the product claims to run on
|
|
# SQLite and on Postgres, so both are run rather than one being assumed from the other.
|
|
test:
|
|
name: tests (${{ matrix.dialect }})
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
dialect: [sqlite, postgres]
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
env:
|
|
POSTGRES_USER: impuestos
|
|
POSTGRES_PASSWORD: impuestos
|
|
POSTGRES_DB: impuestos
|
|
ports: ['5432:5432']
|
|
options: >-
|
|
--health-cmd "pg_isready -U impuestos"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 10
|
|
# Scaled mode requires S3, so the driver is exercised against a real object store
|
|
# rather than assumed from the local one.
|
|
minio:
|
|
image: bitnami/minio:latest
|
|
env:
|
|
MINIO_ROOT_USER: impuestos
|
|
MINIO_ROOT_PASSWORD: impuestos-secret
|
|
MINIO_DEFAULT_BUCKETS: comprobantes
|
|
ports: ['9000:9000']
|
|
options: >-
|
|
--health-cmd "curl -f http://localhost:9000/minio/health/live"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 20
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
|
|
# TEST_DATABASE_URL unset means SQLite in memory, which is what the harness falls
|
|
# back to. Set, every harness builds itself a database of its own inside this server,
|
|
# and the two-worker claim test in scale.test.ts stops skipping.
|
|
- name: Run the suite
|
|
env:
|
|
TEST_DATABASE_URL: ${{ matrix.dialect == 'postgres' && 'postgres://impuestos:impuestos@localhost:5432/impuestos' || '' }}
|
|
TEST_S3_ENDPOINT: http://localhost:9000
|
|
TEST_S3_BUCKET: comprobantes
|
|
TEST_S3_REGION: us-east-1
|
|
TEST_S3_ACCESS_KEY_ID: impuestos
|
|
TEST_S3_SECRET_ACCESS_KEY: impuestos-secret
|
|
run: pnpm test
|
|
|
|
# The golden paths, against a built stack rather than the dev server. SQLite and local
|
|
# files: that is the combined mode compose ships, and it is the one the helpers in
|
|
# e2e/db.ts can read, since they open the database file to check what a flow wrote.
|
|
e2e:
|
|
name: playwright
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
DATABASE_URL: sqlite:./apps/api/data/app.db
|
|
STORAGE_DRIVER: local
|
|
STORAGE_LOCAL_PATH: ./apps/api/data/files
|
|
BETTER_AUTH_SECRET: ci-secret-that-is-long-enough-32-chars
|
|
BETTER_AUTH_URL: http://localhost:3005
|
|
APP_PUBLIC_URL: http://localhost:3005
|
|
PORT: '4000'
|
|
API_INTERNAL_URL: http://localhost:4000
|
|
E2E_BASE_URL: http://localhost:3005
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm exec playwright install --with-deps chromium
|
|
|
|
- run: pnpm db:migrate
|
|
- run: pnpm db:seed
|
|
- run: pnpm build
|
|
|
|
- name: Start the stack
|
|
run: |
|
|
node --import tsx apps/api/src/index.ts &
|
|
pnpm --filter @impuestos/web start &
|
|
npx --yes wait-on http://localhost:4000/readyz http://localhost:3005/healthz -t 120000
|
|
|
|
- run: pnpm test:e2e
|
|
|
|
- uses: actions/upload-artifact@v4
|
|
if: failure()
|
|
with:
|
|
name: playwright-report
|
|
path: playwright-report/
|
|
retention-days: 7
|