E2E Testing
Guide for running and debugging fullsend admin e2e tests locally and in CI.
Related ADRs: 0040 (org pool), 0060 (cross-org mint), 0009 (pull_request_target security model for shims; e2e uses a separate gate pattern documented below).
Historical ADRs 0010 (browser session) and 0039 (2FA) are superseded for CI by cross-org mint auth (#2155); local runs no longer use Playwright or stored sessions.
Prerequisites
Before running e2e locally or in CI:
- Pool orgs (
halfsend-01…halfsend-06) provisioned per Pool org provisioning below - Mint deployed with
e2erole enrolled andALLOWED_ORGSincludingfullsend-ai - CI only: pool orgs with
FULLSEND_FOREIGN_E2E_REPOSauthorizingfullsend-ai/fullsend - Local only:
gh auth login(orGH_TOKEN/GITHUB_TOKEN) with admin access on pool orgs
Local runs
- Authenticate as an admin on the pool orgs (
gh auth login --web, or exportGH_TOKEN). - Run tests (uses
gh auth token,GH_TOKEN, orGITHUB_TOKEN):
make e2e-testOptional environment variables:
| Variable | Purpose |
|---|---|
GH_TOKEN / GITHUB_TOKEN | Override token source for local runs |
FULLSEND_MINT_URL | Override mint endpoint (default: hosted public mint, same as fullsend admin --mint-url) |
E2E_LOCK_TIMEOUT | Max wait for a free pool org (default 10m) |
E2E_GCP_PROJECT_ID | GCP project for inference-related setup (if needed) |
Tests acquire an exclusive lock on one org from the pool (halfsend-01 … halfsend-06) — see ADR 0040.
CI runs
In GitHub Actions, tests mint a cross-org installation token via the mint service:
- Workflow requests a GHA OIDC token (
id-token: write) mintclient.MintTokenPOSTs to{FULLSEND_MINT_URL or hosted default}/v1/tokenwith{role: "e2e", target_org: "<pool org>"}(repos omitted for installation-wide access)- Mint verifies the caller against
FULLSEND_FOREIGN_E2E_REPOSon the target org (ADR 0060)
Required repository secrets:
| Secret | Purpose |
|---|---|
E2E_GCP_WIF_PROVIDER | GCP WIF provider (inference / auxiliary GCP access) |
E2E_GCP_SERVICE_ACCOUNT | GCP service account for WIF |
E2E_GCP_PROJECT_ID | GCP project ID |
Mint URL uses the hosted public endpoint by default (same as fullsend admin --mint-url). Override with org/repo variable FULLSEND_MINT_URL if needed; no separate e2e secret.
Pool org provisioning
Each pool org must be provisioned before e2e can use it:
- Org exists with
botsendas owner test-repoande2e-lockrepos (lock created at runtime)- All role apps installed, including
fullsend-ai-e2ewith Repository → Variables: Read and write (actions_variables) and Organization → Variables: Read and write (organization_actions_variables) FULLSEND_FOREIGN_E2E_REPOSincludesfullsend-ai/fullsendwith org-wide visibility (visibility: all)- Mint enrolled: org in
ALLOWED_ORGS,${ORG}/e2einROLE_APP_IDS, e2e app PEM enrolled
Use the idempotent setup script:
MINT_PROJECT=... MINT_FUNCTION=... hack/setup-new-e2e-org.sh 07Verify foreign authorization:
fullsend admin foreign list --org halfsend-01
# expect e2e → fullsend-ai/fullsendExisting pool orgs (halfsend-01 … halfsend-06) need a one-time operator pass: install the e2e app (if missing) and run:
fullsend admin foreign allow --org halfsend-NN --role e2e --caller fullsend-ai/fullsendCI authorization
Pull requests trigger e2e via pull_request_target in .github/workflows/e2e.yml so fork PRs can use repository secrets. Because that exposes credentials to untrusted code, a gate job runs first (see workflow comments for why it is a separate job).
Who runs automatically
E2E tests run without maintainer action when the PR author is an org/repo member or collaborator (author_association of OWNER, MEMBER, or COLLABORATOR on the base repo). The gate uses the frozen github.event.pull_request.author_association from the workflow event — not a live REST lookup — because GITHUB_TOKEN lacks read:org and cannot see org membership for members with private visibility. (Note: agent dispatch paths use the collaborator permission API instead, which does not have this limitation — see ADR 0054.)
Who needs ok-to-test
External contributors and fork PR authors must have a maintainer apply the ok-to-test label after the latest push. The label must be created once in GitHub repo settings (Settings → Labels).
Stale labels
If new commits are pushed after ok-to-test was applied, the label is removed automatically and e2e is skipped until a maintainer re-applies it after reviewing the latest changes. Freshness compares the label timestamp against the frozen PR updated_at from the workflow event (PR_UPDATED_AT); the live API fallback may over-reject when non-push activity bumped updated_at. Applying the label triggers immediate authorization on labeled events.
Blocked runs
When e2e does not run, a sticky PR comment (marker <!-- e2e-gate -->) explains why and what to do. Re-run the workflow or add/re-apply ok-to-test as appropriate.
CI architecture
- Gate — authorize the PR author or a fresh
ok-to-testlabel (base checkout only; never checks out PR head) - E2E — checkout PR head SHA, authenticate to GCP via WIF, mint cross-org tokens per pool org,
make e2e-test
Pushes to main, merge queue, and workflow_dispatch skip the gate and run e2e directly.
