- Contributing
Test
This page lists the checks CI runs on a HappyView pull request and the local commands that correspond to each one. Run them from the root of a checkout set up as in Develop. This page covers testing HappyView itself; to test an app built on HappyView, see Testing.
Checks and their local commands
Section titled “Checks and their local commands”The checks below are the ones CI runs on alpha. The Required to merge column holds for pull requests into alpha and beta. beta runs a subset of the checks: it has no e2e-tests-sqlite, lua-differential or portability job, and its lint job runs only cargo fmt and the first cargo clippy command.
| CI check | Required to merge | Run it locally |
|---|---|---|
lint | Yes | Lint |
unit-tests | Yes | Unit tests |
e2e-tests | Yes | End-to-end tests, on Postgres |
e2e-tests-sqlite | No | End-to-end tests, on SQLite |
lua-differential | No | Lua differential test |
frontend | Yes | Dashboard |
playwright | No | Browser tests |
portability | No | Portability |
test-<package> | Yes, except test-nsid | SDK packages |
audit | No | cargo audit, after cargo install cargo-audit |
Spaces interop workflow | No | Interop tests |
DCO | Yes | Sign off every commit |
Some checks that aren’t required to merge still gate a release. The release gate lists the gate for each branch.
CI runs a job only when the files it covers change. The path filters are in the changes job of .github/workflows/ci.yml, which lists the server’s files and, for each test-<package> check, the files that trigger it.
On alpha, a draft pull request skips every job except audit and the SDK package tests, and e2e-tests fails until the pull request is marked ready. See Open a pull request.
Missing prerequisites fail
Section titled “Missing prerequisites fail”A test that needs a database, a built plugin fixture or an interpreter plugin fails when that prerequisite is missing, and its message names what is missing and how to get it. To run without them, set HAPPYVIEW_ALLOW_SKIPS=1 and those tests skip instead.
A test written for one database backend still skips on the other, since nothing is missing.
cargo fmt -- --checkcargo clippy --workspace --all-targets -- -D warningscargo clippy --workspace --all-targets --features testkit -- -D warningscargo clippy --features lua-reference --all-targets -- -D warningsbash scripts/check-no-native-lua.shbash scripts/check-no-openssl.shbash scripts/stamp-version.test.sh
| Script | Fails when |
|---|---|
check-no-native-lua. | A default build links a Lua interpreter. Scripts run in an interpreter plugin, and the native Lua runtime exists only behind the lua-reference feature |
check-no-openssl. | Anything in the dependency graph pulls in OpenSSL or native-tls. TLS comes from rustls |
stamp-version. | scripts/, which writes a release’s version into the manifest, stops behaving as its test expects |
CI also runs bash scripts/check-migration-drift.sh beta main after fetching those two branches. It fails when a migration that beta or main ships is missing from your branch or differs from it, unless migrations/renumbered.tsv records that the file moved to another version.
Unit tests
Section titled “Unit tests”Unit tests need no database and no fixtures:
HAPPYVIEW_ALLOW_SKIPS=1 cargo test --libHAPPYVIEW_ALLOW_SKIPS=1 cargo test --workspace --exclude happyviewcargo test --features lua-reference --lib --test codemod_cases
The third command runs the tests gated behind lua-reference, the feature that compiles the native Lua runtime. Building it compiles Lua from C, so it needs a C toolchain.
End-to-end tests
Section titled “End-to-end tests”The end-to-end tests need a database, the plugin fixtures and the interpreter plugins.
-
Build the plugin fixtures, which are WebAssembly modules under
tests/fixtures/:bash bash scripts/build-plugin-fixtures.sh -
Fetch the pinned interpreter plugins and point the tests at them:
bash bash scripts/fetch-lua-plugin.shexport HAPPYVIEW_LUA_PLUGIN=tests/fixtures/lua-pluginbash scripts/fetch-quickjs-plugins.shexport HAPPYVIEW_JAVASCRIPT_PLUGIN=tests/fixtures/javascript-pluginexport HAPPYVIEW_TYPESCRIPT_PLUGIN=tests/fixtures/typescript-plugin
Each script downloads a pinned release from
happyproto/pluginsand checks its hash, so a local run loads the same modules CI does. -
Start the test database, which listens on port 5433:
docker docker compose -f docker-compose.test.yml up -d -
Run the suite:
cargo TEST_DATABASE_URL=postgres://happyview:happyview@localhost:5433/happyview_test cargo test --tests -
Stop the database:
docker docker compose -f docker-compose.test.yml down
To check that every prerequisite is in place before a run, use bash scripts/check-plugin-fixtures.sh --require, bash scripts/check-lua-plugin.sh --require and bash scripts/check-quickjs-plugins.sh --require. Each names what is missing.
On SQLite
Section titled “On SQLite”The same suite runs on SQLite when TEST_DATABASE_URL names a SQLite file. Run it when a change touches SQL, migrations or the write path:
dir=$(mktemp -d)TEST_DATABASE_URL="sqlite://$dir/e2e.db?mode=rwc" cargo test --tests -- --test-threads=1
When a run fails without naming the cause
Section titled “When a run fails without naming the cause”A few targets read a plugin fixture directly rather than through the prerequisite check, so an unbuilt fixture can fail many tests without saying why. external_storage is one: without test_library, many of its tests panic. Build the fixtures first:
bash scripts/build-plugin-fixtures.shA fresh worktree has no built fixtures until you do.
In a worktree created inside the main checkout, such as under .worktrees/, the build can fail with “current package believes it’s in a workspace when it’s not”. Cargo walks up from the fixture to the parent checkout’s Cargo.toml, whose workspace.exclude may lack a fixture your branch added. Create the worktree outside the checkout, with git worktree add ../my-branch -b my-branch, or bring the parent checkout’s Cargo.toml up to date with your branch.
Measure storage and ingest cost
Section titled “Measure storage and ingest cost”For a change that affects storage or ingest cost, measure it with scripts/measure-backfill-storage.sh [dids-file] [collection]. It backfills a fixed list of repos, scripts/measure-backfill-dids.txt by default, into a fresh SQLite database. It reports the write-ahead log’s peak size, the database size and, on Linux, the bytes the server wrote; elsewhere write_bytes reads unavailable.
The script reaches the real PLC directory and PDSes, so results vary over time. Run it on your branch and on alpha back to back, and put both summaries in the pull request. The storage settings it exercises are in Configuration.
The provider conformance cases
Section titled “The provider conformance cases”The record-provider conformance cases build only with the testkit feature, so the run above leaves them out:
TEST_DATABASE_URL=postgres://happyview:happyview@localhost:5433/happyview_test cargo test --features testkit --test conformanceShards
Section titled “Shards”CI splits the suite into two shards per database. scripts/e2e-shard.sh computes them from cargo metadata, so a local run can reproduce one:
bash scripts/e2e-shard.sh --list 2cargo test $(bash scripts/e2e-shard.sh 1 2)
Coverage
Section titled “Coverage”The Postgres shards run under cargo-llvm-cov, and the coverage job merges their reports and uploads them to Coveralls. No coverage threshold blocks a merge. To produce the report locally:
cargo install cargo-llvm-covrustup component add llvm-toolsTEST_DATABASE_URL=postgres://happyview:happyview@localhost:5433/happyview_test cargo llvm-cov --workspace --open
Interop tests
Section titled “Interop tests”The atproto spaces interop tests run HappyView against other PDS implementations. The Spaces interop workflow runs them against the PDS images pinned in interop/images.env. It runs on a pull request or a push to main, beta or alpha that changes the spaces code, the interop tests or their Compose files; the full path list is in the workflow’s on block. It also tests each new upstream PDS image on its own, so a failure there can come from the upstream change rather than yours. Each PDS has its own Compose file and ignored test targets:
| PDS | Compose file | Test target |
|---|---|---|
| The Bluesky reference PDS with permissioned data | docker-compose. | spaces_ |
| atproto-pds | docker-compose. | spaces_ |
| pds.js | docker-compose. | spaces_ and spaces_ |
| ZDS | docker-compose. | spaces_ |
docker compose --env-file interop/images.env -f docker-compose.zds.yml up -d --waitcargo test --test spaces_zds_interop -- --ignored --nocapturedocker compose -f docker-compose.zds.yml down -v
spaces_migration_e2e also needs TEST_DATABASE_URL, as in the end-to-end tests.
Lua differential test
Section titled “Lua differential test”This test runs each case through the Lua interpreter plugin and through the native Lua runtime, and compares the results. It is ignored by default because it is slow.
bash scripts/build-plugin-fixtures.sh differential_librarybash scripts/fetch-lua-plugin.shexport HAPPYVIEW_LUA_PLUGIN=tests/fixtures/lua-pluginTEST_DATABASE_URL="sqlite://$(mktemp -d)/differential.db?mode=rwc" cargo test --features lua-reference --test lua_differential -- --ignored --nocapture
Dashboard
Section titled “Dashboard”Run these in web/:
npm cinpm run format:checknpm testNEXT_PUBLIC_BASE_PATH=/__HAPPYVIEW_BP__ npm run build
The frontend job also checks that the release binary carries the dashboard. It builds the library tests with the embedded-dashboard feature, moves web/out aside, and runs the dashboard:: tests, which pass only if the dashboard is compiled in. From the repository root, after the build above:
bin=$(cargo test --release --features embedded-dashboard --lib --no-run --message-format=json \ | jq -r 'select(.profile.test == true and .target.kind[0] == "lib") | .executable' | tail -1)mv web/out web/out.aside"$bin" dashboard::mv web/out.aside web/out
Browser tests
Section titled “Browser tests”Playwright drives the whole stack in Docker: HappyView behind Caddy, its own Postgres, a PLC directory and a PDS.
bash scripts/fetch-lua-plugin.shdocker compose -f docker-compose.e2e.yml down -vdocker compose -f docker-compose.e2e.yml up -d --build --waitcd web && npx playwright install chromium && npx playwright test
Portability
Section titled “Portability”The portability job builds the static musl binary, which no other job compiles, and starts it from an empty directory until /health answers. That shows the binary carries its migrations and needs no system OpenSSL. To build it locally on Debian or Ubuntu:
sudo apt-get install -y musl-toolsrustup target add x86_64-unknown-linux-muslcargo build --locked --bin happyview --target x86_64-unknown-linux-musl
SDK packages
Section titled “SDK packages”Each of the five SDK packages has its own check: test-oauth-client, test-oauth-client-browser, test-oauth-client-node, test-lex-agent and test-nsid. Run one from the repository root, with the package’s name in place of @happyview/oauth-client:
bun installbun run --filter '@happyview/oauth-client' buildbun run --filter '@happyview/oauth-client' typecheckbun run --filter '@happyview/oauth-client' test
@happyview/oauth-client-browser, @happyview/oauth-client-node and @happyview/lex-agent depend on @happyview/oauth-client, so build it first.
© HappyProto

