mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-03 16:56:56 +00:00
jobs_test.go covers the job framework thoroughly, but nothing covers the
wiring: whether the kickoff handler renders the progress fragment into the
right hx-target, and whether the loop actually outlives the request it was
started from. Both are what ab4a4eb changed, and both are invisible to Go
tests — a typo in an hx-target or a fragment that renders blank passes every
assertion we have.
The load-bearing check drives crew import rather than the tier remap. A
one-member remap completes in under a second, so closing the tab "mid-run"
proves nothing; import does a PDS write plus a network PLC lookup per entry,
which leaves a real window to close the browser and watch the job keep going.
It is caught mid-flight at a progress tick with no admin page open.
Seeded members are created on the local-only dev hold and removed in a
finally block. README records the environment traps found while building
this: 127.0.0.1 vs localhost, in-memory sessions dying on every hold rebuild,
UA/IP pinning that makes curl log you out, and the forward-only appview
migrations that require a per-batch DB reset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
86 lines
4.1 KiB
Markdown
86 lines
4.1 KiB
Markdown
# Browser-driven batch validation
|
|
|
|
Checks for the `val/*` validation stack — the branch-per-batch series used to
|
|
sign off the range between the deployed commit and `main`.
|
|
|
|
These complement, and do not replace, `go test` and the in-process integration
|
|
harness (`internal/testharness`, `test/integration`, `make integration-test`).
|
|
Pick by what needs proving:
|
|
|
|
| Prove | Use |
|
|
|---|---|
|
|
| A function's logic, a query's shape, a guard's behaviour | Go unit test |
|
|
| A push/pull/delete round trip across appview + hold + S3 | `make integration-test` |
|
|
| A fragment renders into the right target, a job outlives its request, a 500 dressed as an empty state | these scripts |
|
|
|
|
## Running
|
|
|
|
```bash
|
|
npm i -D @playwright/test && npx playwright install chromium
|
|
|
|
node test/e2e/login.mjs # interactive, once per hold rebuild
|
|
node test/e2e/batch00-admin-jobs.mjs # then the batch checks
|
|
```
|
|
|
|
Env overrides: `ATCR_HOLD_URL`, `ATCR_APPVIEW_URL`, `ATCR_E2E_PROFILE`,
|
|
`ATCR_E2E_SEED`.
|
|
|
|
## Things that will cost you an afternoon
|
|
|
|
**Use `127.0.0.1`, never `localhost`.** The appview canonicalises to
|
|
`http://127.0.0.1:5000` and answers `localhost` with a 307. Any snippet written
|
|
against `localhost:5000` measures the redirect, not the endpoint.
|
|
|
|
**Admin sessions are in-memory and die on every hold rebuild.** They live in
|
|
`ui.sessions` (`pkg/hold/admin/admin.go`), not the `admin_sessions` table, which
|
|
is vestigial for this path. Air rebuilds the hold whenever tracked source
|
|
changes — including a batch checkout — so budget one interactive login per
|
|
switch. There is no test-mode bypass; `server.test_mode` only affects OAuth
|
|
redirect URLs.
|
|
|
|
**Never drive the admin panel with curl.** Sessions are pinned to User-Agent and
|
|
client IP prefix, and a mismatch does not merely reject the request — it calls
|
|
`deleteSession` and logs you out. Drive everything through `ctx.request`, which
|
|
inherits the browser's cookie jar and UA.
|
|
|
|
**Closing every Playwright page disposes `ctx.request`.** It fails with "Request
|
|
context disposed". Keep one `about:blank` page open when the test needs the
|
|
browser out of the way.
|
|
|
|
**Crew delete is a `<button hx-post>`, not a `<form>`.** Scraping for forms
|
|
finds nothing, deletes nothing, and cheerfully reports a clean tab while every
|
|
seeded member is still live. Assert against page text after a reload, not
|
|
against the scrape that just ran.
|
|
|
|
**Crew rows hydrate per-row via `hx-trigger="load"`.** The tab needs a real
|
|
settle window (~6s here) before anything is scrapeable.
|
|
|
|
**A seeded fixture makes the second run lie.** Crew import skips DIDs that
|
|
already exist, so a re-run finishes instantly and the detachment check silently
|
|
passes without ever exercising a running job. Purge before re-running.
|
|
|
|
**Preconditions are easy to miss.** The tier reconciliation card only renders
|
|
for crew on a tier absent from quota config (`handleCrewList`), so it is invisible
|
|
on a healthy hold. Crew add/update do not validate the tier against config,
|
|
which is how these tests manufacture the condition without restarting the hold.
|
|
|
|
## Per-batch stack switching
|
|
|
|
Use `val-switch.sh`. The appview DB migrates **forward only**, so older batch
|
|
code hits a schema from the future: batch 00 selects `tags.id` (dropped by
|
|
0032) and cannot write `manifests.manifest_key` (added NOT NULL by 0033/0034,
|
|
which kills every backfill insert). The script destroys and re-migrates the
|
|
appview volume so the DB matches the branch.
|
|
|
|
It deliberately does **not** touch `atcrio_atcr-hold`, which holds the hold's
|
|
did:web signing key and the CAR store — captain, crew, layer, stats and scan
|
|
records. Losing it means a new hold identity and every pushed layer gone.
|
|
|
|
`docker-compose.yml` is pinned to `main` throughout. `a7c7db6` (batch 01) is
|
|
what makes the appview share the hold's netns so `did:web:localhost%3A8080`
|
|
resolves, and every compose-based batch needs it — including batch 00, which
|
|
lands before it. The file is dev-only, so pinning it is a fixture decision
|
|
rather than a change to what is under validation. Never commit it from a batch
|
|
branch; the script unstages it for you, because `git checkout main -- <path>`
|
|
stages what it restores.
|