inspector/clients/cli/__tests__/README.md
Bob Dickinson 33e88eac9f
CLI: catalog listing, auth flags, and OAuth browser UX (#1782)
* feat(cli): catalog listing, auth flags, and OAuth browser UX

Add servers/list and servers/show, --relogin / --stored-auth-only, browser
OAuth navigation with OSC 8 links, and extract method handlers under
clients/cli/src/handlers. Tracks #1781.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): address #1782 review — relogin clear, stored-auth-only, docs

Clear both raw and normalized OAuth keys on --relogin; check the shared
store before bailing under --stored-auth-only mid-session; restore the
tests README link target; document servers/show redaction limits; keep
mcpi groundwork with TODO(#1432) markers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): gate OAuth browser open and harden #1782 review follow-ups

Arm auto-open only for CLI-owned interactive OAuth, honor MCP_AUTO_OPEN_ENABLED,
move --relogin clear after validation, skip keychain on servers/list, and cover
secret redaction after rehydrate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): silence disarmed OAuth URLs and round-3 review nits

Suppress authorize URL print when navigation is disarmed or
--stored-auth-only; document MCP_AUTO_OPEN_ENABLED; redact Cookie/auth
headers; keep servers/show unit tests off the OS keychain.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(cli): cover withArmedAutoOpen for the ≥90 branch gate

CI failed on cliOAuth.ts branch coverage after the auto-open arming
helper landed; exercise the armed path through runCliInteractiveOAuth.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): mid-RPC unauthorized recovery and clear round-4 nits

Mirror connect's UnauthorizedError path in withCliAuthRecoveryRetry, let
MCP_AUTO_OPEN_ENABLED=true force open off-TTY, mock open-url in the
in-process runner, reject --relogin on catalog methods, restore the
__tests__ README inventory, and narrow OAuth helpers to structural client types.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): reconnect after mid-RPC OAuth and finish round-5 nits

Reconnect before retrying after unauthorized recovery, guard non-OAuth
configs, decouple forceAutoOpen, move open-url mock to vitest setupFiles,
and reject --relogin with stored-auth short-circuits.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): fail fast on non-TTY interactive OAuth and round-6 nits

Skip interactive OAuth without a TTY unless MCP_AUTO_OPEN_ENABLED=true;
document --stored-auth-only for CI; redact settings.metadata; reject
--relogin for stdio; clarify AuthRecoveryRequired reconnect comments.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): admit interactive OAuth when stdin is a TTY (round-7)

Gate on stdin||stderr so `2>&1 | tee` still works; fold confirm/auto-open/
TTY into CliOAuthConnectOptions; rename authRequiredFailure; document
MCP_AUTO_OPEN_ENABLED=true as non-TTY admit.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): fail fast on step-up without stdin (round-8)

Decline [y/N] on readline EOF/close, require a stdin TTY for the default
step-up confirmer (force-open admit is not enough), document stderr-only
browser auto-open vs stdin||stderr flow admit, and drop the alias wrapper.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): allow piped step-up answers; bound silent pipes (round-9)

Drop the hard stdin-TTY gate that broke `echo y | …`. Keep EOF decline and
add a short non-TTY timeout so an open pipe that never writes cannot hang.
Clarify isTTY as admit-only; extract the readline mock factory.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): keep partial piped step-up answers (round-10)

Capture readline `line` before close so `printf y` without a newline is
not treated as decline; timeout is auth_required with a distinct message;
document the 5s bound and post-confirm callback wait; pin the TTY no-timeout arm.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): tighten step-up timeout messaging and TTY test (round-11)

Make the TTY no-timeout assertion use a settled flag (the Promise.race
check was vacuous), say "timed out" instead of "declined" on the pipe
bound, and document that piped answers need a newline or stdin close.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 16:55:25 -04:00

65 lines
4 KiB
Markdown

# CLI Tests
Tests live under `__tests__/` and run via Vitest.
- Most tests import `runCli()` **in-process** (see `helpers/cli-runner.ts`) so
`clients/cli/src` is measured under the coverage gate. Suite-wide
`helpers/mock-open-url.ts` (vitest `setupFiles`) mocks `open-url` so an armed
interactive OAuth path cannot launch a real browser.
- `e2e.test.ts` (and root `scripts/smoke-cli.mjs`) spawn the built binary for
shebang / `process.exit` paths — `pretest` builds `test-servers` + the CLI
bundle first.
## Scripts (from `clients/cli/`)
```bash
npm test # pretest build + all tests
npm run test:cli # subset: cli.test.ts
npm run test:cli-tools # subset: tools.test.ts
npm run test:cli-headers # subset: headers.test.ts
npm run test:cli-metadata # subset: metadata.test.ts
npm run test:coverage # build + ≥90 per-file coverage gate
npm run validate # format:check && lint && typecheck && test
```
## Test files
| File | Focus |
| --------------------------------------- | ------------------------------------------- |
| `cli.test.ts` | Core connect / method / config matrix |
| `tools.test.ts` | `tools/call` argument coercion |
| `headers.test.ts` | `--header` merging |
| `metadata.test.ts` | `--metadata` / `--tool-metadata` |
| `methods.test.ts` | Method allow-list / rejection |
| `app-info.test.ts` | `--app-info` probe paths |
| `format-json.test.ts` | `--format json` envelopes |
| `format-output.test.ts` | Text/json writers |
| `emit-result.test.ts` | Result emission helpers |
| `method-types.test.ts` | `ONE_SHOT_METHODS` / guards |
| `run-method.test.ts` | Handler dispatch against a real test server |
| `run-method-mocks.test.ts` | Handler edge cases with mocks |
| `servers-list.test.ts` | `servers/list` / `servers/show` + redaction |
| `cliOAuth.test.ts` | Connect / mid-RPC OAuth recovery |
| `cli-oauth-navigation.test.ts` | OSC 8, arm/disarm, `MCP_AUTO_OPEN_ENABLED` |
| `open-url.test.ts` | `open` package wrapper |
| `oauth-runner.test.ts` | Runner client-config / CIMD flags |
| `oauth-interactive.test.ts` | Loopback callback OAuth (in-process) |
| `stored-auth.test.ts` | `--use-stored-auth` / handoff / wait |
| `clear-stored-auth-for-relogin.test.ts` | Dual-key `--relogin` clear |
| `programmatic-ergonomics.test.ts` | Flag conflicts, timeouts, ergonomics |
| `error-handler.test.ts` | Exit codes / error shaping |
| `style.test.ts` | ANSI / OSC 8 helpers |
| `e2e.test.ts` | Out-of-process binary smoke |
| `helpers/assertions.test.ts` | Assertion helpers |
## Helpers
| Helper | Role |
| -------------------------- | --------------------------------------------------------- |
| `helpers/cli-runner.ts` | In-process `runCli` with stdout/stderr capture |
| `helpers/mock-open-url.ts` | Suite-wide `open-url` mock (vitest `setupFiles`) |
| `helpers/assertions.ts` | `expectCliSuccess` / `expectCliFailure` / output matchers |
| `helpers/fixtures.ts` | Temp config / client.json factories |
OAuth interactive tests stub or drive the loopback callback in-process rather
than requiring a real browser.