Heavy tests run 26542638471 (commit 60145eee, after the --no-embed
fix) failed at the same script but at a downstream step:
> Source "default" has no local_path. Run: gbrain sources add default --path <path>
Three independent bugs in the script that all surfaced at once after
v0.41's source-registry landed:
1. `gbrain config set sync.repo_path` is the legacy way; sync now
reads `sources.local_path` first. Replaced with an upsert into the
sources table via psql:
INSERT INTO sources (id, name, local_path)
VALUES ('default', 'default', $BRAIN_DIR)
ON CONFLICT (id) DO UPDATE SET local_path = EXCLUDED.local_path
Kept the legacy `config set sync.repo_path` line too as
belt-and-suspenders for any downstream caller that still reads it.
2. `gbrain sync --dir <path>` is silently ignored; sync's CLI parser
recognizes `--repo`, not `--dir`. Switched to `--repo`.
3. `bun run src/cli.ts doctor --json` at the top (used to apply
migrations as a side effect) exits non-zero whenever ANY check
warns — including the new "no embedding provider configured"
warning on a fresh CI runner. The script's `set -e` aborted at
line 53 before reaching any of the sync invocations. Added `|| true`
since the migration runs regardless of doctor's exit verdict.
Verified locally — `DATABASE_URL=... bash tests/heavy/sync_lock_regression.sh`
output:
[sync 1] rc= (lock-busy: 'Another sync is in progress')
[sync 2] rc=0 (winner)
[sync 3] rc= (lock-busy: 'Another sync is in progress')
[sync 4] rc= (lock-busy: 'Another sync is in progress')
outcomes: winners=1 losers=3 unknown=0
post-run gbrain_cycle_locks(gbrain-sync) row count: 0
OK — 1 winner, 3 lock-busy losers, no leaked lock rows.
Production code untouched. All three fixes are in the bash script.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tests/heavy/
Heavy ops-shape tests. Shell scripts that exercise gbrain end-to-end against
real infrastructure (Postgres, large fixtures, concurrent processes). Cost
minutes per run; NOT in default bun test.
When to add a script here
Put a test here if it:
- Costs more than ~30s wallclock per run
- Needs real Postgres (not PGLite in-memory)
- Spins up multiple processes or measures concurrency
- Measures system metrics (RSS, latency under load, lock contention)
- Tests an upgrade / migration matrix against committed historical states
When to use *.slow.test.ts instead
Put a slow test in test/ with the .slow.test.ts suffix if it:
- Runs under
bun test(TypeScript, uses bun:test imports) - Is correctness-shaped, not ops-shaped (asserts behavior of one function)
- Can stub external dependencies
The two patterns coexist intentionally. *.slow.test.ts is per-file
correctness for cold paths; tests/heavy/ is ops-shape scripts that don't
fit bun's test runner.
How to run
# Run every script in this directory, sequentially:
bun run test:heavy
# Run a single script:
tests/heavy/<script>.sh
The runner is scripts/run-heavy.sh. It discovers every tests/heavy/*.sh
file at this directory's top level (NOT recursive), runs them in lexical
order, fails on the first non-zero exit.
Naming convention
tests/heavy/<name>.sh— top-level test script, picked up by the runner.tests/heavy/_<name>.sh— library/helper invoked by a sibling test. The leading underscore tells the runner to SKIP this file. Use this pattern for fixture builders, shared setup, anything that needs a required argument or isn't standalone-runnable.tests/heavy/fixtures/<name>— committed input data (SQL, JSON, etc).
CI scheduling
Heavy tests run nightly at 08:17 UTC via .github/workflows/heavy-tests.yml,
and on PRs labeled heavy-tests. They are NOT part of the default PR CI
matrix — that gate stays fast.
Failure output convention
Each script writes a per-run log to ~/.gbrain/audit/heavy-<script>-<ts>.log
containing subprocess stdout/stderr, environment state, and any captured
metrics. The CI workflow uploads these as artifacts on failure for triage
without re-running locally.
Style
#!/usr/bin/env bashset -euo pipefail- Explicit array argv for execs (no
eval, no unquoted globs) - Print a one-line
[<script>] <action>log per major step - Exit non-zero on any failure path; print enough context to diagnose
- Honor
$GBRAIN_HOME/$TMP_ROOTenv overrides where relevant
See scripts/check-jsonb-pattern.sh and scripts/run-slow-tests.sh for the
in-tree style reference.