A jam is a shared-deadline creative challenge: one prompt, a roster of participants, a single due date. Submissions point at records that li
0

Configure Feed

Select the types of activity you want to include in your feed.

atjam.at / tests
2 folders 7 files
README.md

@atjam/tests#

Integration test harness for atjam. Drives real PDS writes against four test accounts (test-1test-4), then asserts via the same Constellation backlinks the web app uses for reads. Catches lexicon-shape regressions and (once invitation validation lands) bilateral correctness.

This is not unit tests — it talks to the PDS and to Constellation. Network-dependent, slower than unit tests by design. The point is to verify the system as it actually works, not as it's modeled.

Setup#

You need four ATProto accounts the harness can sign in to. By default the harness points at https://bsky.social; works against any ATProto-compliant PDS that exposes the standard server APIs.

Option 1 — Automated (bsky.social or self-hosted PDS)#

cd tests
GMAIL_ADDRESS=you@gmail.com pnpm create-accounts > .env

What happens:

  1. Calls com.atproto.server.createAccount four times, creating test-1.bsky.social through test-4.bsky.social (or whatever HANDLE_DOMAIN you point it at).
  2. Plus-addresses your gmail per signup (you+test-1@gmail.com, …) so verification emails all land in one inbox.
  3. Mints an app password per account via com.atproto.server.createAppPassword.
  4. Writes a complete .env block to stdout containing handles, app passwords, and the right PDS_URL.

You still need to click the verification email for each account in your inbox. Bsky.social may also push you to SMS verify on first use — handle that in the bsky.app UI if it happens.

Custom count: pnpm create-accounts 6 for six accounts (but only the first four are read by the harness today). Custom PDS: PDS_URL=https://your-pds pnpm create-accounts > .env.

Option 2 — Manual#

Create four accounts however you like (bsky.app web flow, goat, Tranquil UI), generate app passwords in each account's settings, then:

cd tests
cp .env.example .env
# edit .env, fill in TEST1_HANDLE / TEST1_APP_PASSWORD, etc.

.env is gitignored. Don't commit it.

Running#

pnpm --filter @atjam/tests test          # run scenarios once, exit code reflects pass/fail
pnpm --filter @atjam/tests test:watch    # vitest watch mode while developing

pnpm --filter @atjam/tests probe:lag     # measure firehose → Constellation latency

Each scenario:

  1. Logs in to all four test accounts
  2. Resets each account's at.atjam.* records (deletes whatever's there)
  3. Runs the scenario's writes
  4. Asserts the resulting state via Constellation backlinks
  5. Cleans up again at the end

Reset means scenarios are hermetic — rerunning gives the same result, no accumulation.

Account naming#

The four accounts are referred to as test1, test2, test3, test4 internally (TS identifiers can't contain hyphens). The actual bsky handles use hyphens: test-1.bsky.social. Each scenario file aliases them locally to semantic roles for readability:

const organizer    = agents.test1;
const participant1 = agents.test2;
const participant2 = agents.test3;
// test4 is reserved for chain/forgery scenarios

What's covered today#

Scenario Status What it asserts
open-round.test.ts runs Organizer creates jam + open round; two participants sign up; one submits. Constellation returns the expected backlinks.
hosted-validation.test.ts skipped Invitation validation positive/negative cases. Waiting for the validation logic to exist in a reader.
network-chain.test.ts skipped Chain validation (organizer → A → B), forged-chain rejection, networkGate=contributed enforcement. Same — waiting for validator.

When the invitation validator lands (likely in @atjam/lexicons or as a module in web/app/lib/), un-skip the stub files and replace it.todo(...) with real assertions. The setup pattern from open-round.test.ts is the template.

Probes#

probe:lag measures how long it takes a record written to the PDS to become visible via Constellation. Useful for:

  • Sanity-checking the PDS → relay → indexer pipeline is alive
  • Setting realistic waitForBacklinks timeouts in scenarios
  • Spotting if Constellation has gotten slower over time

Probes are plain TS scripts (no vitest), invoked via pnpm probe:*. Easy place to add one-off experiments without ceremony.

Adding a scenario#

  1. Drop a new file under src/scenarios/*.test.ts.
  2. beforeAllloginAll() then resetAll(agents).
  3. Write whatever sequence of records the scenario needs using helpers from writes.ts.
  4. Assert via waitForBacklinks (handles firehose lag) or by directly fetching with listMyRecords.
  5. afterAllresetAll(agents) again so the next run starts clean.

Use types from @atjam/lexicons for record bodies — the compiler catches lexicon-shape mistakes before they reach the PDS.

Why this isn't unit tests#

  1. The behavior under test is bilateral — records on multiple PDSes that converge via the firehose. Unit tests can fake half of it; integration can't.
  2. Constellation is a real dependency — testing the read path with a fake indexer doesn't tell you whether production reads work.
  3. The lexicon is the contract. Real PDS writes validate it end-to-end; in-process mocks of createRecord don't.

The cost is speed and brittleness (network flakes, Constellation lag) — accepted in exchange for the realism.

Caveats with bsky.social#

The default PDS_URL is bsky.social, which works but has constraints:

  • Records go to the global firehose. Anything you write during testing is public. Test data has throwaway content; just don't put anything sensitive in scenario inputs.
  • Use disposable accounts. reset.ts deletes all at.atjam.* records on each account at the start of every run. Point the harness at burner handles, not your personal one.
  • Rate limits apply. Per-account write limits are tighter than a self-hosted PDS. ~15 writes per run is fine; running back-to-back-to-back dozens of times might trip them.
  • Phone verification may gate things. Bsky started pushing SMS verify in 2024. The harness can sign in to a verified account fine; an unverified one may have limited capabilities.

For automated CI eventually, point at a self-hosted PDS via PDS_URL — no global firehose pollution, no rate limits, no SMS gates.