Skip to content

Contract tests & CI

tests/contract/ verifies cross-backend parity of the shared protocol core — not any one backend in isolation. Each test runs the exact same RequestSpec through executeHttpProxy (or executeHttpProxyStreaming) twice, once per Fetcher adapter, against one real local upstream, and asserts the two ExecuteResults match.

Two fetchers stand in for the real backends:

  • workerFetcher — uses globalThis.fetch, mirroring worker/handlers/proxy.ts.
  • electronFetcher — uses undici, mirroring electron/main/handlers/http-handler.ts. It adapts undici’s response shape to the shared FetcherResponse contract (headers collapsed to Record<string, string | string[]>, statusText hard-coded to '' since undici doesn’t expose it).

The upstream is a real node:http server (tests/contract/upstream.ts) with fixture routes — headers echo, JSON roundtrip, redirect chains, chunked transfer-encoding, arbitrary status codes — not a mock of the shared code and not the Cloudflare runtime.

http-proxy.contract.test.ts runs buffered fixtures (headers, query params, JSON POST, 404, 500, chunked body, redirect chains) through both fetchers and asserts pairwise equality. http-proxy-streaming.contract.test.ts does the same for the streaming path (executeHttpProxyStreaming) that SSE/NDJSON/chunked downloads use — asserting identical bytes on both rails, identical rejection of a disallowed method, and identical handling of a non-2xx status. The buffered test never exercises the streaming code path, so the streaming file exists specifically to catch stream-framing/decoding parity breaks that would otherwise only surface in end-to-end tests.

Run it with:

Terminal window
npm run test:contract

Static architecture and generated-artifact gates

Section titled “Static architecture and generated-artifact gates”

These gates catch generated drift and repository-level architecture violations:

  • npm run verify:opencollection-types — regenerates shared/opencollection/spec-types.ts from the OpenCollection JSON Schema, then fails if that regeneration produces a diff. If this fails, the schema changed but the generated types weren’t regenerated and committed.
  • npm run architecture:check — enforces runtime dependency directions, rejects runtime import cycles, and prevents new or grandfathered production modules from exceeding their size cap.
  • npm run capabilities:check — regenerates docs/CAPABILITY_MATRIX.md from src/lib/shared/capabilities.ts and fails on any diff, for the same reason.

The two generated-artifact checks are fixed by running their corresponding generator and committing the result, not by hand-editing generated files. Architecture violations must be fixed at the dependency, cycle, or module-ownership boundary.

npm run validate is the canonical local core-product gate and is used by CI’s required validate job. It runs three explicit stages:

  1. validate:static — every product/tooling TypeScript config, Biome lint/format, generated OpenCollection types, the capability matrix, and architecture:check.
  2. validate:test — root Vitest with coverage plus CLI and VS Code workspace unit tests.
  3. validate:build — web/Worker and self-host Node/Docker builds, CLI and extension bundles, the Electron file:// renderer build, Electron main/preload compilation with runtime-import verification, and renderer bundle-size budgets.

Run the whole thing locally before opening a PR:

Terminal window
npm run validate

Playwright end-to-end jobs (web, Electron, and extensions), packaging smoke, the docs site, and PR preview deployment remain separate environment-specific jobs. See docs/CI_CD.md for the full workflow and branch-protection breakdown.