Testing
This page covers the AppView’s automated suite, run with Vitest. For manually testing the native shell on a given platform, including emulator, Simulator and real-device workflows, see Testing native builds instead. For the client’s own tests, see Client development.
What is tested where
Section titled “What is tested where”Unit tests live in a *.test.ts file next to the code they cover, in every
package and in the AppView app itself:
Directorypackages
- lexicons/src/conformance.test.ts every lexicon document, checked structurally
- db/src/schema/parity.test.ts the libSQL and Postgres schemas agree
- space, space-sync, identity, community, projections, notifications, blobs, embeds, voice each has its own
src/**/*.test.ts
Directoryapps
- appview/src routes, views, the WebSocket topics, and Jetstream handling
- appview/test/integration the one suite that needs a real PDS
Running tests locally
Section titled “Running tests locally”pnpm test # unit tests, every packagepnpm test:watch # the same, in watch modepnpm test:integration # against a real spaces-alpha PDS, see belowpnpm typecheck # sources and testsBoth test and test:integration are Vitest projects defined in the root vitest.config.ts: unit includes every {packages,apps}/*/src/**/*.test.ts, integration includes {packages,apps}/*/test/integration/**/*.test.ts. Running pnpm test never touches the integration project, so a database or a PDS is never required to get a green run.
The lexicon conformance test
Section titled “The lexicon conformance test”packages/lexicons/src/conformance.test.ts reads every lexicon document in
the package and checks it structurally, without a server or a network call.
Among other things it asserts that:
- Every method (
query,procedure, orsubscription) declares anerrorsarray, with one exception,social.colibri.beta.server.describeServer, listed in the test by name. - Every error name is PascalCase and has a
description. - Every
refresolves to a definition that exists, and a same-documentrefuses the short#nameform rather than repeating the full NSID. - No lexicon description uses an em dash or a semicolon, the same house style this documentation follows.
There’s no separate error-code generator or route-parity check. A lexicon’s
errors array and its schema are checked directly against the JSON rather than
against generated code. See AppView development for where a new endpoint’s lexicon lives.
Testing against a file, not :memory:
Section titled “Testing against a file, not :memory:”Anything that needs a database uses openTestDatabase() from
@colibri-social/appview-db, not openDatabase({ url: ":memory:" }). It
hands back a migrated database on a temporary file, plus a destroy() that
closes it and removes the directory.
@libsql/client closes and reopens its local connection around a transaction,
and an in-memory database doesn’t survive that: the reopened connection is a
different, empty database, so every db.transaction() throws. A file-backed
database keeps the atomicity the sync store depends on, where a repository’s
records and its sync cursor move together or not at all.
Integration tests
Section titled “Integration tests”pnpm test:integration runs apps/appview/test/integration/spaces.test.ts
against a real spaces-alpha PDS and its own PLC directory in Docker:
docker compose -f docker-compose.integration.yml up -d --wait --buildpnpm test:integrationdocker compose -f docker-compose.integration.yml down -vThe suite provisions a community, joins a member, writes a message straight to that member’s own repo, and checks the AppView syncs it, recovers from a drifted sync cursor, and removes a banned member. It’s the only place that exercises the real credential and sync path end to end rather than through injected fakes.
It’s not part of pull-request CI. It runs nightly on a schedule and on manual dispatch, so drift against the spaces alpha arrives as its own failure rather than as noise on an unrelated pull request.
Continuous integration
Section titled “Continuous integration”Three jobs run on every pull request:
| Job | What it does |
|---|---|
| Lint | pnpm biome ci . |
| Test | pnpm build, pnpm typecheck, then pnpm test |
| Lexicons | re-runs pnpm lexicons:codegen and fails on a dirty tree, then checks the vendored com.atproto.space and com.atproto.simplespace lexicons are byte-identical to the pinned upstream commit |
The vendored lexicons are pinned, so an upstream change arrives as a re-vendor
rather than as a codegen result that shifts underneath the AppView. packages/lexicons/lexicons/com/atproto/PINNED_COMMIT holds the commit, and packages/lexicons/README.md covers how to move it.