Ctrl K
ComunidadGuías…

scenarios/README.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# scenarios — e2e regression suite

Self-contained end-to-end tests. Each scenario builds nothing but reuses one
shared Docker image, starts its **own** container on a random `127.0.0.1` port
with a fresh `DATA_DIR`, drives the built `ac` CLI (plus occasional `curl`), and
prints `PASS`/`FAIL`. Requires `docker`, `jq`, and a built CLI (`pnpm build`).

## Run

    scenarios/run-all.sh                  # build image once, run all, summary table
    scenarios/010-bootstrap-org/run.sh    # run one (builds image if missing)

`run-all.sh` exits non-zero if any scenario fails. Override the image with
`SPARROW_SCENARIO_IMAGE=<tag>`.

The suite mirrors the SPEC "Scenarios" list. 010–120 cover bootstrap/invite/
enroll, messaging (send/pop/read/broadcast/attachments/conversation), the invite
onboarding doc, working status & presence, the human CLI, rooms & members,
suggested replies, agent sharing, org boundaries, direct conversations, the
sidebar sources (#borg) regression, voice, and delivery receipts. 125 and 155
cover v4's unified attention layer: the medium-spanning work queue
(`POST /me/inbox/pop` → `{ item: WorkItem | null }`) and the activity timeline —
both now interleave chat with email.

130–150 are the email medium. 130/135/140/145/148 run the `EMAIL_PROVIDER=fake`
stack, driving inbound through `POST /admin/email/inject` (the injector picks the
`verification` verdicts, so spoof / spam / virus are testable offline) and reading
outbound off `GET /admin/email/outbox`. **150-email-smtp** is the only scenario
that is not one container: see below.

## Compose scenarios (`scenario_compose_start`)

A scenario that needs sidecars ships a `compose.yml` next to its `run.sh` and
calls `scenario_compose_start "$(dirname "$0")"` instead of `scenario_start`. It
does everything `scenario_prepare` does and then brings the stack up under a
**per-run project name** (`docker compose -p sparrow-scn-$$ up -d --build`), so
concurrent or repeated runs never collide:

- host ports come from the same `_ac_free_port` helper — `SPARROW_PORT` for core,
  plus every variable named in `SCENARIO_COMPOSE_PORTS`, exported so compose
  interpolates them;
- the compose file pins `image: ${SPARROW_SCENARIO_IMAGE}` for core, so
  `run-all.sh`'s single build is reused, and pins upstream tags for sidecars;
- readiness is core's `/healthz` plus each `PORT_VAR:/path` in
  `SCENARIO_COMPOSE_READY`;
- `$SERVER` / `$ADMIN_TOKEN` / `$SPARROW_SERVER` are exported **identically** to
  `scenario_start`, so `ac_tok`, `api`, `signup`, `sse_me_watch` … all work
  unchanged;
- the EXIT trap gains `docker compose … down -v` ahead of the tmp-root wipe.

Host-tool requirements are **declared**, never assumed: `SCENARIO_REQUIRES=(compose
swaks)` plus a `scenario_requires` call makes a missing tool print `SKIP` and exit
0. `run-all.sh` reports SKIP in its summary and stays green — a machine without
the toolchain reports the gap instead of failing the suite.

Two shapes to keep straight when writing pop assertions: the **unified**
`sparrow pop` (no `--room`) returns the v4 `{ item }` envelope, while the
**room-scoped** `sparrow pop --room <id>` keeps its v3 `{ message, room }` shape
— rooms have no email, so that route never became a work-item route.

## Add scenario NNN-name

1. `mkdir scenarios/NNN-name && touch scenarios/NNN-name/run.sh && chmod +x` it.
2. Start it with:

       #!/usr/bin/env bash
       set -euo pipefail
       SCENARIO_NAME="NNN-name"
       . "$(cd "$(dirname "$0")/.." && pwd)/lib.sh"
       scenario_start   # sets $SERVER, $ADMIN_TOKEN, $SPARROW_SERVER; traps cleanup

3. Drive the CLI and API with the v3 helpers (see `lib.sh` for the full set):
   - `signup <email> <pw> <name>` → a `ses_` token (the FIRST signup bootstraps
     an org and owns it).
   - `ac_tok <token> <args…>` — run the CLI as a bearer credential (`ses_`/`agk_`)
     via `SPARROW_TOKEN`; `ac_as <profile> <args…>` for `login`/`enroll` persistence.
   - `create_agent`, `add_human_to_org`, `enroll_existing_human` for fixtures.
   - `api` / `api_raw` / `http_status` / `admin_api` for raw calls; `first_org_id`,
     `invite_token_of` for discovery; `sse_room_watch` / `sse_me_watch` +
     `wait_for_line` for SSE assertions (grep-based — never awk on vm8's mawk).
   - `assert_eq` / `assert_contains` / `assert_not_contains` / `assert_json` /
     `pass` / `fail` / `skip`.
   `run-all.sh` picks up `[0-9]*-*/run.sh` automatically, in sorted order.
4. Needs sidecars? Add a `compose.yml` beside `run.sh`, set
   `SCENARIO_REQUIRES` / `SCENARIO_COMPOSE_PORTS` / `SCENARIO_COMPOSE_READY`, and
   call `scenario_compose_start "$(cd "$(dirname "$0")" && pwd)"`. See
   `150-email-smtp` for the worked example (core + `apps/mail-gateway` + mailpit
   + a CoreDNS sidecar publishing the DKIM key the edge has to look up).