Skip to content

Add comprehensive E2E test suite for error handling - #4

Merged
kunchen-cookiy merged 5 commits into
mainfrom
claude/add-unit-tests-error-handling
Apr 27, 2026
Merged

Add comprehensive E2E test suite for error handling#4
kunchen-cookiy merged 5 commits into
mainfrom
claude/add-unit-tests-error-handling

Conversation

@kunchen-cookiy

Copy link
Copy Markdown
Contributor

Summary

This PR adds a comprehensive end-to-end test suite that validates error handling across the CLI, covering token validation, HTTP error responses, network failures, the save-token command, and CLI argument parsing.

Key Changes

  • New test file tests/error-handling.e2e.test.ts: 16 test cases organized into 7 test suites covering:

    • Token file issues (missing, empty, whitespace-only files)
    • HTTP 401 responses from the real API with invalid tokens
    • HTTP 403 responses (forbidden/host-not-in-allowlist)
    • HTML error responses (e.g., 502 Bad Gateway)
    • Network-layer errors (connection refused, DNS resolution failure, timeouts)
    • save-token command validation and file writing
    • CLI argument parsing (unknown commands, missing required options, invalid option values)
  • New test helpers tests/helpers.ts: Utility functions for:

    • Running the built CLI as a subprocess with captured stdout/stderr
    • Creating and cleaning up temporary directories
    • Starting mock HTTP servers for controlled error scenarios
    • Ensuring the CLI is built before tests run
  • Test configuration vitest.config.ts: Vitest setup with Node environment and 20-second timeout

  • Updated package.json: Added vitest as a dev dependency and test/test:watch npm scripts

Notable Implementation Details

  • Tests use async subprocess spawning (not spawnSync) to avoid blocking the parent event loop, which is necessary since mock HTTP servers run in the same Node process
  • Mock servers are used for scenarios that are difficult to reproduce with the real API (403 errors, HTML responses, timeouts, connection refused)
  • Real API tests (T4–T5) hit production with fake tokens to exercise the 401 error path
  • Tests validate both exit codes and error message formatting, including auth banners and error prefixes

https://claude.ai/code/session_013SjRYb8WkckP4ZVXyYSJYr

claude added 4 commits April 25, 2026 13:04
- tests/auth.test.ts (T1-T3, T9-T13): loadCredentials, runSaveToken
- tests/v1Client.test.ts (T4-T8): mocked fetch covers HTTP 401 and network errors
- tests/cli.test.ts (T14-T16): subprocess test against built dist/index.js
- tests/util.test.ts (T16): parseIntOption direct unit test
- tests/helpers.ts: mocks process.exit + console; snapshots stderr at first
  exit so the v1Client catch-handler doesn't pollute assertions

Adds vitest devDep, npm test / test:watch scripts, vitest.config.ts.

https://claude.ai/code/session_013SjRYb8WkckP4ZVXyYSJYr
Replaces unit-test approach (which mocked process.exit, fs, and global.fetch
inside the test process) with end-to-end tests that exercise the built CLI
binary as a real subprocess.

- tests/e2e.test.ts: single file, 16 cases organized by category (T1-T16)
  matching the original error-handling test plan.
- tests/helpers.ts: runCli (async spawn + capture stdout/stderr/exit code),
  startMockServer (in-process HTTP server), tmp dir helpers.

Note: runCli uses async spawn, not spawnSync. spawnSync would block the
parent's event loop, freezing the in-process mock server's accept() loop and
causing the CLI subprocess to hang on connect.

T4-T5 (HTTP 401) and T8 (timeout) use a local mock HTTP server bound to
127.0.0.1:0 instead of hitting s-api.cookiy.ai. T6 picks an ephemeral port,
closes the listener, then connects to get a deterministic ECONNREFUSED
(avoids ":1" being rejected as a "bad port" by undici).

https://claude.ai/code/session_013SjRYb8WkckP4ZVXyYSJYr
…bidden

- T4/T5: hit the real production API (no COOKIY_SERVER_URL override). The
  real server returns 401 for fake tokens, exercising dieNoAccess() against
  actual server behavior rather than a mock.
- T_403 (new): mock server returns 403 with "host not in allowlist" body.
  The real API doesn't easily reproduce this, so a local mock is justified.
  Verifies the CLI prints [HTTP 403] + body details and does NOT trigger
  the auth-redirect banner (that path is reserved for missing/empty token
  files and 401 responses).
- Annotated each describe block: [no network], [real network], [mock — ...],
  [no mock] so it's obvious at a glance which tests reach external systems.

Mock usage summary:
  • T_403 — mock 403 server (real API can't be made to return 403 easily)
  • T8    — mock hanging server (real API responds in ms, not seconds)
  • T6    — opens then immediately closes a local listener to get a closed
            port; not strictly a mock, just a way to find a deterministically
            refused address

All other tests use either pure local I/O (T1–T3, T9–T16) or the real
s-api.cookiy.ai (T4, T5) or real DNS lookups (T7).

https://claude.ai/code/session_013SjRYb8WkckP4ZVXyYSJYr
…st.ts

- T_HTML (new): mock server returns 502 with an HTML body (typical reverse-
  proxy error page). Verifies that formatBody/summarizeHtml emits a one-line
  "(HTML body) <title>" summary instead of dumping raw HTML to stderr, and
  that the [HTTP 502] header is present. Real API returns JSON, so a mock
  is required to exercise this path.
- Renamed tests/e2e.test.ts → tests/error-handling.e2e.test.ts to make the
  scope explicit (the file covers error-handling scenarios end-to-end).

https://claude.ai/code/session_013SjRYb8WkckP4ZVXyYSJYr
Triggers:
  - pull_request (any branch → any base)
  - push to main
  - workflow_dispatch (manual)

Job: test
  - ubuntu-latest, Node 22.x
  - npm ci → typecheck → build → npm test (vitest E2E)
  - 10-minute job timeout
  - npm cache via setup-node
  - concurrency group cancels superseded runs on the same ref

Note: T4/T5 hit s-api.cookiy.ai over the public internet — accepted
tradeoff for genuine integration coverage of the 401 response shape.

https://claude.ai/code/session_013SjRYb8WkckP4ZVXyYSJYr
@kunchen-cookiy
kunchen-cookiy merged commit f6fba18 into main Apr 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants