# Test and debug

An agent can verify a capsule end to end from the terminal. The loop is: `fuast check` until it is clean, `fuast dev` in one terminal, then `fuast run`, `fuast db dump`, and `fuast logs` in another to prove each feature works. Use a browser for the UI, not to find out whether a mutation wrote a row.

## Check before you deploy

```sh
fuast check
fuast check --json
```

`check` builds the capsule, runs the TypeScript checker on every file, and checks the capsule contract: import rules, `capsule()` keys, schema, index declarations, cron expressions, tools, and the favicon. It exits with code 1 on any error. `fuast deploy` runs the same checks before it uploads, and the platform runs them again, so a capsule that fails `check` never deploys.

Each error names the file, line, and column with the source line underneath. `--json` prints `{ ok: false, error, code, diagnostics }` where every diagnostic has `file`, `line`, `column`, `code`, `category`, `message`, and a `frame`. Fix the first error first: later ones are often caused by it.

## Type errors in the browser

While `fuast dev` runs, it type checks the capsule after every save. Build errors and type errors appear in the terminal and as an overlay on the open app page, with the file, line, and source frame. Fix the code and save, and the overlay updates on its own.

A type error does not stop `fuast dev`: the app keeps running so you can keep testing. It does stop `fuast deploy`, so clear the overlay before you deploy.

## Call handlers headlessly

`fuast run` calls one query, mutation, or action the way a client would and prints the result as JSON:

```sh
fuast run todos                                     # query with no arguments
fuast run addNote '"Buy milk"' 2                    # each argument is one JSON value
fuast run addTodo '{"text":"milk"}'                 # a handler with tool args takes one object
fuast run setTodoDone '{"todoId":"01j9…","done":true}'
fuast run todos --json                              # result plus logs and read metrics
```

- Without a slug it calls `fuast dev` on `--port` (default 3000) as your local identity: `dev`, or the name from `fuast auth as <name>` (no restart needed). Switch to `alice`, run the same call, and confirm alice cannot see dev's rows. `--as <name>`, `--guest`, and `--anon` change the identity for one call.
- With a slug (`fuast run todos my-app`) it calls the deployed app as your logged-in account, with the same sharing rules and daily quotas as the browser.
- The handler kind comes from the app's manifest, so you never name it. A last argument that is not JSON is the slug or app URL, so quote string arguments: `'"milk"'`. A slug and `--port` together are an error, and so is a bare word that cannot be a slug (`Hello`): the CLI tells you to quote it instead of calling the wrong app.
- A handler error prints the message and its `code` and exits with code 1. That makes `fuast run` usable in scripts and CI.
- `--json` prints `{ ok, kind, name, result, ms, logs, metrics }`: the result, the log entries the call wrote, and its database read metrics (the operations of that one run).

A mutation run this way is a real write. Subscribed browser tabs update, and the row is there for `fuast db dump`.

The same call over HTTP, for scripts that do not use the CLI, is `POST <app url>/api/call`. See [reference](https://fuast.com/docs/reference.md#http-call-api).

## Measure one query

```sh
fuast inspect --query todos --port 3000
fuast inspect --query todos my-app --json
```

`inspect --query <name>` prints the database read metrics recorded for that query, per run: the totals of the last run (rows read, rows returned, bytes read, index scans, summed over every database call the run made), then the last few runs, each with its database calls (table and index, rows returned, rows read, bytes read, and whether it was a full scan). With `--json`, `queryExecution.runs` holds the same data and `observed` counts runs. Run the query first (open the page, or `fuast run <name>`) so there is something to show. Compare `read` with `returned`: if a query reads far more rows than it returns, add an index that matches its filter. See [limits](https://fuast.com/docs/limits.md#checking-usage).

## Read logs and stacks

```sh
fuast logs --port 3000
fuast logs my-app --follow
fuast logs --json
```

Every `ctx.log` call and every handler error lands in the app's log. A handler error entry has `level: "error"`, the message `<kind> <name> failed: <error>`, the handler name, and `data.stack`. Stacks are mapped back to your capsule source in `fuast dev` and on hosted apps alike (every deploy keeps its server source map), so frames read `server/index.ts:42:11`, not a line in a bundle. Go to the first frame inside `server/` or `shared/`.

`fuast dev` also prints each log line and error in its own terminal as it happens.

Errors from a query reach the client too: `useQuery` throws to the nearest `ErrorBoundary`, and mutation promises reject with the message and `code`. See [errors](https://fuast.com/docs/capsule-api.md#errors).

## Check the data

```sh
fuast db list --port 3000          # tables and row counts
fuast db dump --port 3000          # rows, bounded by the 1000-row return limit
fuast db dump --table todos --json
```

After every mutation you test, confirm the rows changed the way you expected. Do not assume a write happened because the UI looks right.

## Test several people

Locally you are the signed-in account `dev`. Add `?fuast_user=alice` to the URL for a second person in another tab, `?fuast_guest=1` for a guest on a public app. For headless checks, `fuast auth as alice`, then `fuast run`, then `fuast auth reset`. See [local identities](https://fuast.com/docs/auth.md#local-identities).

## Many dev servers at once

```sh
fuast run-many --count 5 --base-port 4000
```

`run-many [dir]` starts `--count` independent dev servers of the same capsule (default 20) on consecutive ports from `--base-port` (default 4000) and prints their URLs. Each has its own database, so parallel test runs or several agents can work against the same code without sharing state. Stop them all with Ctrl-C.

## Before you report done

1. `fuast check` passes.
2. Every handler you wrote was exercised with `fuast run` or in the browser, and `fuast db dump` shows the expected rows.
3. `fuast logs` has no handler errors you did not expect.
4. Private data was checked with two identities.
5. After deploy, the same checks pass against the slug: `fuast run <name> <slug>`, `fuast logs <slug>`.
