name: Integration tests on: pull_request: branches: [main] push: branches: [main] permissions: contents: read jobs: # Four shards of about equal duration, each running its files in parallel # against one database per worker. The shards used to be four slices of # equal file count, run serially: 954 s to 1 449 s, because a slice holding # the sharing matrix and the backup files took half again as long as one # holding small files. `--shard` now goes through the duration sequencer # (`tests/durations/sequencer.ts`, timings in # `tests/durations/integration.json`), and every file in a shard runs on # one of three workers, each against its own clone of the migrated template # (`tests/integration/worker-database.ts`). Three workers leave a core of # the four-core runner to the Postgres container. The job guard stays well # above an ordinary run; a wedged test still surfaces through vitest's own # 60-second budget, so the guard only needs to catch a container that never # boots. Each shard boots its own Postgres testcontainer and uploads its # JSON report, which `scripts/test-durations.mjs` turns into the timing file. shard: runs-on: ubuntu-24.04 timeout-minutes: 30 strategy: fail-fast: false matrix: shard: [1, 2, 3, 4] name: integration shard ${{ matrix.shard }}/4 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 22 cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm db:generate # vitest is invoked directly: pnpm passes the `--` separator through to # the script literally, so `pnpm test:integration -- --shard=…` hands # vitest a stray positional argument and the shard flag never applies — # both "shards" then run the full suite. Proven by a paired local run # before this workflow shipped. - run: >- pnpm exec vitest run --config vitest.integration.config.mts --shard=${{ matrix.shard }}/4 --reporter=default --reporter=json --outputFile.json=integration-report-${{ matrix.shard }}.json env: HEALTHLOG_INTEGRATION_WORKERS: "3" # The integration suite boots a Postgres testcontainer and points # Prisma at it via DATABASE_URL set inside `tests/integration/setup.ts`. # These env vars satisfy code paths that read them at module load. # # ENCRYPTION_KEY MUST be quoted: YAML 1.2 parses an unquoted run of # zeros as the integer 0, which then arrives in `process.env` as the # string "0" — failing crypto's hex/base64 length check and breaking # the backups + integration-status suites for 50+ runs (regression # since v1.4.13). Quoting forces a literal scalar. API_TOKEN_HMAC_KEY: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000" SESSION_SECRET: "integration-tests" - name: Upload the timing report if: always() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: integration-report-${{ matrix.shard }} path: integration-report-${{ matrix.shard }}.json if-no-files-found: ignore retention-days: 14 # Branch protection requires a check named exactly "integration". This job # carries that name and answers for every shard: it fails when any shard # failed or was cancelled, so a half-green matrix cannot satisfy the gate. integration: runs-on: ubuntu-24.04 needs: shard if: always() timeout-minutes: 5 steps: - name: Every shard finished green run: | if [ "${{ needs.shard.result }}" != "success" ]; then echo "shard result: ${{ needs.shard.result }}" exit 1 fi