docs/TESTING.md
The beads project uses Go tests plus repository wrapper scripts. Prefer the
wrapper scripts for local validation because they apply the repository's normal
local build flags, skip policy, and timeout policy. The current GitHub Actions
PR contract still runs direct go test commands; the CI cleanup plan will move
that contract behind dedicated scripts/ci/* wrappers.
# Run all tests (auto-skips known broken tests)
make test
# Or directly:
./scripts/test.sh
# Run opt-in ICU regex path tests (maintainer-only, not normal validation)
make test-icu-path
# Run specific package
./scripts/test.sh ./cmd/bd/...
# Run specific test pattern
./scripts/test.sh -run TestCreate ./cmd/bd/...
# Verbose output
./scripts/test.sh -v
# Set custom timeout (default: 3m)
TEST_TIMEOUT=5m ./scripts/test.sh
# Enable verbose output
TEST_VERBOSE=1 ./scripts/test.sh
# Run specific pattern
TEST_RUN=TestCreate ./scripts/test.sh
Dolt integration tests require Docker with the exact Dolt image cached locally. Tests auto-detect the environment and skip gracefully — no manual configuration needed.
State,Condition,Behavior
doltSkipped,BEADS_TEST_SKIP contains "dolt",Silent skip (no warning)
doltNoDocker,Docker daemon not reachable,WARN + skip
doltNoImage,No Dolt image at all,WARN + skip with pull instruction
doltWrongVersion,Image repo cached but wrong tag,WARN + skip with pull instruction
doltReady,Exact image cached and Docker running,Run tests
States are checked once per test binary and cached. Order of evaluation:
BEADS_TEST_SKIP → Docker availability → exact image → any image version.
Set BEADS_TEST_SKIP to opt out without Docker overhead (~1s docker info):
# Skip Dolt tests silently
BEADS_TEST_SKIP=dolt ./scripts/test.sh
# Skip multiple services (comma-separated)
BEADS_TEST_SKIP=dolt,slow ./scripts/test.sh
testing.Short() is reserved for true runtime, stress, and large-fixture skips.
It must not be used as an implicit integration, e2e, API, Docker, or external
dependency boundary.
Use these mechanisms instead:
//go:build integration or //go:build e2e for named suites.BEADS_TEST_SKIP=dolt,
BEADS_TEST_EMBEDDED_DOLT=1, or required API-key checks.make ci-pr-core, the main integration shards, and the
package gate wrappers.Run make check-testing-short to verify that new testing.Short() usage stays
within the approved runtime/stress/large-fixture allowlist. The PR policy wrapper
runs the same check.
# Pull the exact Dolt image to enable integration tests
docker pull dolthub/dolt-sql-server:2.1.0
# Point tests at an existing Dolt server (skips container startup)
BEADS_DOLT_PORT=3308 ./scripts/test.sh
BEADS_DOLT_PORT — when set, tests reuse the server at that port instead of
starting a container. Port 3307 is hardcoded as production and always rejected.
# Skip additional tests beyond .test-skip
./scripts/test.sh -skip SomeSlowTest
# Run with custom timeout
./scripts/test.sh -timeout 5m
# Combine flags
./scripts/test.sh -v -run TestCreate ./internal/beads/...
Tests in .test-skip are automatically skipped by scripts/test.sh.
At the time of this review, .test-skip contains only comments and no active
test-name patterns. Treat any new skip as a temporary exception: file the
upstream issue first, record it in .test-skip, and remove the skip when the
test is fixed.
When running tests during development:
Use the test script: Always use ./scripts/test.sh instead of go test directly
scripts/ci/* wrappers when
reproducing exact CI jobs./scripts/test-icu-path.sh (or deprecated make test-full-cgo)Target specific tests when possible:
# Instead of running everything:
./scripts/test.sh
# Run just what you changed:
./scripts/test.sh -run TestSpecificFeature ./cmd/bd/...
Compilation is the bottleneck:
-run to avoid recompiling unnecessarilyCheck for new failures:
# If you see a new failure, check if it's known:
cat .test-skip
If you discover a broken test:
.test-skip:
# Issue #NNN: Brief description
TestNameToSkip
.test-skip support regex patternsThe top slow tests in cmd/bd:
TestDoctorWithBeadsDir (1.68s) - Only significantly slow testTestFlushManagerDebouncing (0.21s)TestDebouncer_* tests (0.06-0.12s each) - Intentional sleeps for concurrency testingTestMultiWorkspaceDeletionSync (0.12s)Most tests are <0.01s and very fast.
cmd/bd/ - Main CLI tests (82 test files, most of the suite)
internal/beads/ - Core beads library tests
internal/storage/ - Storage backend tests (SQLite, memory)
internal/rpc/ - RPC protocol tests
internal/*/ - Various internal package tests
The current CI workflow does not yet call the scripts/ci/* wrappers for every
job. Until workflow migration is complete, use the command documented in the
failing workflow when reproducing an existing status check exactly.
Use scripts/test.sh for local default validation and targeted development
runs.
Use the CI wrappers for the accepted target PR contracts:
make ci-pr-core
make ci-pr-policy
make ci-pr-lint
Use the package gate wrappers when touching package or docs surfaces:
make ci-package-mcp
make ci-package-npm
make ci-website
PR confidence is based on behavior checks, not raw coverage percentage.
For the current CI/test-surface inventory and cleanup roadmap, see CI_TEST_SURFACE_AUDIT.md. That audit documents where local commands and GitHub Actions currently diverge before the CI cleanup work starts changing workflow behavior.
For accepted CI tier decisions and the implementation order, see CI_CLEANUP_PLAN.md.
./scripts/test.sh -v ./path/to/package/...
./scripts/test.sh -run '^TestExactName$' ./cmd/bd/...
./scripts/test.sh 2>&1 | head -5
Output shows:
Running: go test -timeout 3m -skip TestFoo|TestBar ./...
Skipping: TestFoo|TestBar
When adding new tests:
t.Parallel() for independent testst.Cleanup() or deferWhen tests break:
.test-skip.test-skip with issue number