Files
HealthLog/.github/workflows/test-nightly.yml
T
Marc-André Bombeck 298515d156 ci: fail the mutation floors step, and scale nightly property runs by a factor
The floors step piped scripts/mutation-floors.mjs into tee without
pipefail, so it took tee's exit status and a module under its floor could
never fail the run. It now runs under bash with pipefail, as do the other
steps that pipe into tee, and a guard reads every workflow and fails on a
tee pipe without it. The other piped steps were checked: each either
validates what the pipe produced or pipes a command that cannot fail.

The nightly runs set FC_NUM_RUNS, an absolute count that replaced every
property's own: the database backup round trip, which states twelve cases
for its cost, ran a hundred times against a timeout sized for twelve.
FC_RUNS_FACTOR now multiplies each property's count (five for the unit
suite, four for integration), propertyTimeout grows a test's timeout by
the same factor, and the backup round trip uses it.
2026-10-22 19:04:20 +02:00

179 lines
6.6 KiB
YAML

name: Tests (nightly, shuffled)
# Looks for what a pull request run cannot see by design.
#
# - Order dependence. Both suites run with files and tests in a random order
# (`--sequence.shuffle`), seeded from the clock and printed at the top of
# the log and in the job summary. A failure that does not reproduce on a
# pull request reproduces here with `--sequence.seed=<seed>`.
# - New property-test inputs. Pull requests run every fast-check property
# with a fixed seed; this run picks a fresh one (`FC_SEED`) and five times
# the cases, and fast-check prints the seed and the shrunk counterexample
# on a failure (see tests/property/fast-check.ts).
# - Stale shard timings. The integration reports of this run are merged into
# a regenerated `tests/durations/integration.json`, uploaded as an
# artifact for a maintainer to commit when the shards drift apart.
#
# Not a gate: nothing here blocks a merge. A red run is an issue to open.
on:
schedule:
- cron: "41 2 * * *"
workflow_dispatch:
permissions:
contents: read
jobs:
unit:
runs-on: ubuntu-24.04
timeout-minutes: 45
name: unit (shuffled)
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
- name: Cache Playwright headless shell
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-shell-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
playwright-shell-${{ runner.os }}-
- run: pnpm exec playwright install --only-shell chromium
- name: Pick the seeds
run: |
echo "TEST_SEED=$(date +%s)" >> "$GITHUB_ENV"
echo "FC_SEED=$(( $(date +%s%N) % 2147483647 ))" >> "$GITHUB_ENV"
- name: Unit tests, shuffled
shell: bash
env:
# Five times each property's own case count (tests/property/fast-check.ts).
FC_RUNS_FACTOR: "5"
run: |
set -o pipefail
echo "Order seed ${TEST_SEED}, property seed ${FC_SEED}" | tee -a "$GITHUB_STEP_SUMMARY"
pnpm exec vitest run --sequence.shuffle --sequence.seed="${TEST_SEED}" \
--reporter=default --reporter=json --outputFile.json=unit-report.json
- name: Upload the timing report
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: nightly-unit-report
path: unit-report.json
if-no-files-found: ignore
retention-days: 14
integration:
runs-on: ubuntu-24.04
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
name: integration shard ${{ matrix.shard }}/4 (shuffled)
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
# One seed per run, shared by the shards through the run id, so every
# shard shuffles the same way and a failure names one seed to replay.
- name: Pick the seeds
run: |
echo "TEST_SEED=${{ github.run_id }}" >> "$GITHUB_ENV"
echo "FC_SEED=$(( ${{ github.run_id }} % 2147483647 ))" >> "$GITHUB_ENV"
- name: Integration tests, shuffled
shell: bash
env:
HEALTHLOG_INTEGRATION_WORKERS: "3"
# Four times each property's own case count; a property's timeout
# grows by the same factor (`propertyTimeout`).
FC_RUNS_FACTOR: "4"
# Quoted for the reason given in integration.yml.
API_TOKEN_HMAC_KEY: "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000"
SESSION_SECRET: "integration-tests"
run: |
set -o pipefail
echo "Order seed ${TEST_SEED}, property seed ${FC_SEED}" | tee -a "$GITHUB_STEP_SUMMARY"
pnpm exec vitest run --config vitest.integration.config.mts \
--shard=${{ matrix.shard }}/4 \
--sequence.shuffle --sequence.seed="${TEST_SEED}" \
--reporter=default --reporter=json \
--outputFile.json=integration-report-${{ matrix.shard }}.json
- name: Upload the timing report
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: nightly-integration-report-${{ matrix.shard }}
path: integration-report-${{ matrix.shard }}.json
if-no-files-found: ignore
retention-days: 14
# Merge the timings of this run into fresh timing files. Uploaded, not
# committed: the workflow has read-only access, and a maintainer decides
# whether the drift is worth a commit.
durations:
runs-on: ubuntu-24.04
needs: [unit, integration]
if: always()
timeout-minutes: 10
name: regenerate shard timings
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 22
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: nightly-*-report*
path: reports
- name: Regenerate
shell: bash
run: |
set -o pipefail
shopt -s nullglob
integration=(reports/nightly-integration-report-*/*.json)
unit=(reports/nightly-unit-report/*.json)
if [ ${#integration[@]} -gt 0 ]; then
node scripts/test-durations.mjs integration "${integration[@]}"
fi
if [ ${#unit[@]} -gt 0 ]; then
node scripts/test-durations.mjs unit "${unit[@]}"
fi
git diff --stat -- tests/durations | tee -a "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: test-durations
path: tests/durations/*.json
retention-days: 14