Karina/1077 cloudflareworkermock - #1088
Draft
karinamzalez wants to merge 5 commits into
Draft
Conversation
…Worker First ticket of the timeboxed #1077 experiment measuring the surface area of a standalone Cloudflare Worker mock (ADR Option 3A). Adds `mock-api` as a literal pnpm workspace member with the repo's standard TS package layout (catalog pins, eslint flat config, prettier, vitest, `checks`/`test`/`ci` scripts) plus the Worker toolchain: a `wrangler.jsonc` with a real `main` entrypoint — unlike the assets-only docs Worker in `website/` — targeting the `cg-mock-api` service name so it cannot collide with the `common-grants` preview Worker. `src/index.ts` is a pure `fetch(Request) => Response` handler serving a health route at `GET /` that reports the service name and the four supported spec versions (0.1.0-0.4.0); the accompanying vitest suite invokes the exported handler directly, establishing the test pattern the opportunity router reuses in #1077-T3. Also wires the package into the root `ci` aggregate. Refs #1077 Files changed: - pnpm-workspace.yaml - package.json - pnpm-lock.yaml - mock-api/package.json - mock-api/wrangler.jsonc - mock-api/tsconfig.json - mock-api/eslint.config.js - mock-api/vitest.config.ts - mock-api/.prettierrc - mock-api/.gitignore - mock-api/src/index.ts - mock-api/__tests__/index.spec.ts
Brings the deterministic opportunity dataset from the MSW mock-playground spike (#1049, branch `karina/playground-spike`) into the standalone Worker package as `src/data/fixtures.ts`: the 11 frozen records, `SUPPORTED_VERSIONS`, `isSupportedVersion`, `shapeOpportunityForVersion`, `getById`, and `allForVersion`. The file carried zero MSW imports on the spike branch, so the records, the type surface, and the v0.1/list-variant stripping logic all move over unchanged — the canonical id Swagger UI pre-fills (30a12e5e-5940-4c08-921c-17a8960fcf4b) and the reserved-missing id are both preserved. The one behavior change is extending `SUPPORTED_VERSIONS` with "0.4.0", which shipped on main via #976; v0.4.0 added awards/organizations routes but left `OpportunityBase`/`OpportunityDetails` byte-identical to v0.3.0, so it shapes identically and needs no new branch in `shapeOpportunityForVersion` — #1077-T4 will verify that against the generated per-version schemas. The ported spec is verbatim apart from that criterion: 0.4.0 is now asserted supported, "0.5.0" becomes the rejected case, and a new case pins 0.4.0 shaping equal to 0.3.0 across both list and detail variants. `src/index.ts` now sources the health route's version list from the fixture instead of the local const #1077-T1 left as a placeholder. Refs #1077 Files changed: - mock-api/src/data/fixtures.ts - mock-api/__tests__/data/fixtures.spec.ts - mock-api/src/index.ts
Ports the #1049 spike's opportunity handlers into the standalone Worker, replacing MSW's handler registration with a path-prefix router and adding cross-origin access. `GET /v{version}/common-grants/opportunities`, `GET .../opportunities/{oppId}`, and `POST .../opportunities/search` now serve the fixture set for all four spec versions (0.1.0-0.4.0), returning the same envelopes as the spike for the same inputs; an unsupported, missing, or malformed version prefix answers a protocol-shaped 404. MSW touched the handlers in exactly two places, both removed: `http.get/post` registration became exported `(Request, Version) => Response` functions, and `HttpResponse.json` became `Response.json`. Every filter, sort, pagination, and validation rule moved across unchanged. CORS is applied once, wrapping the router, so success responses, validation errors, and route misses alike carry `Access-Control-Allow-Origin: *` and allow `Content-Type`, `X-API-Key`, and `Authorization` -- the headers the docs site's "Try it out" and the TS SDK need. `OPTIONS` preflights are answered for any path, matched or not, since a 404 to a preflight surfaces in the browser as an opaque CORS failure rather than the protocol error the real request would return. One deliberate behavior change from the port: a search body that is valid JSON but not an object (`null`, `[]`, `42`, a bare string) now answers 400 instead of either throwing -- which escaped the handler before CORS was attached -- or quietly returning 200 with the whole unfiltered set. A `withErrorBoundary` backstop keeps any future throw on the CORS path as a 500 envelope. Verified by 107 new tests (the ported suite re-pointed at the Worker's fetch handler, plus router, CORS, and SDK-envelope cases) and by curl against `wrangler dev`. Refs #1077 Files changed: - mock-api/src/index.ts - mock-api/src/handlers/opportunities.ts - mock-api/src/http/cors.ts - mock-api/src/http/envelope.ts - mock-api/__tests__/handlers/opportunities.spec.ts - mock-api/__tests__/handlers/sdk-envelope.spec.ts - mock-api/__tests__/http/cors.spec.ts
Validates every fixture record, shaped per (version x variant), against the per-version JSON Schemas the docs pipeline generates, so hand-coded shaping in `src/data/fixtures.ts` can't drift from the protocol unnoticed. A new `schemas` script regenerates `website/public/schemas/yaml/versions/` (gitignored build output) and `ci` runs it before the suite; the suite raises rather than skips when those schemas are absent, since a skipped conformance run reads as a green CI run that validated nothing. `__tests__/utils/schema-validator.ts` overlays each version's generated directory onto the unversioned schema set and loads the result into one Ajv2020 instance, following `website/src/lib/validation.ts:createAjvWithSchemas` and `lib/ts-sdk/__tests__/utils/ajv-validator.ts` rather than pre-dereferencing as `website/src/lib/schema/ref-resolver.ts` does — the versioned directories omit `$ref` targets like `uuid.yaml`, so dereferencing them fails outright. Formats are validated for real via ajv-formats, with `time` overridden to the protocol's own "HH:mm:ss without timezone" definition because `isoTime.yaml` emits JSON Schema's RFC 3339 `time`, which rejects the spec's published `17:00:00` example. Two limitations are documented in code rather than smoothed over. Models emitted only into `versions/v0.4.0/` (ApplicantType, OppStatus, CompetitionStatus, and others the changelog records as added in 0.2.0) fall back to current shapes, so this catches drift in the opportunity models' own fields but not in the enums they reference. And the fixture's trimmed `Competition` shape omits CompetitionBase's required `forms`/`createdAt`/`lastModifiedAt`, which fails OpportunityDetails validation for 3 of the 11 records; that deliberate #1049 deviation is carved out of the detail assertions and pinned by a canary designed to fail if the fixture is ever completed. Splits typechecking so `src/` stays Worker-only while the Node-based tests get Node's types, keeping a stray `fs`/`process` in the Worker entrypoint an error. Refs #1077 Files changed: - mock-api/__tests__/conformance/fixtures-vs-schemas.spec.ts - mock-api/__tests__/utils/schema-validator.ts - mock-api/tsconfig.test.json - mock-api/tsconfig.json - mock-api/package.json - pnpm-lock.yaml
Adds `.github/workflows/ci-mock-api.yml`, path-filtered to `mock-api/**` plus the workflow file, following the per-package convention of the sibling `ci-lib-*.yml` workflows. The `validate` job runs build, checks, versioned-schema generation, tests, and audit — the schema step kept separate so the conformance suite's cross-package dependency on the website pipeline stays visible in the CI log. The `deploy` job is `workflow_dispatch`-only and gated on `validate`, running a plain `wrangler deploy` with the existing CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID secrets; it is deliberately not modelled on `ci-website-preview.yml`, which versions the shared `common-grants` Worker, since this is a separate service with its own name. After deploying it resolves the `*.workers.dev` URL and smoke-tests the two routes the ticket names, so a deploy that succeeds while serving a broken Worker fails the run. The concurrency group is intentionally not keyed on `github.ref` — every ref targets the one `cg-mock-api` slot, so a ref-scoped group would permit exactly the race it is meant to prevent. `mock-api/DEPLOYMENT.md` records the deployment-overhead rubric input: jobs, secrets (no new ones), locally measured step timings, a manual first-deploy runbook, and the gaps worth carrying into the findings — personal-account hosting, a single deployment slot, and a path filter that cannot see the `website/`- and `lib/core/`-generated schemas the conformance suite depends on. Acceptance criterion 2 is NOT met: no Worker has been deployed, so the `*.workers.dev` URL is unverified. `wrangler deploy` publishes a public service and needs a human decision, and `workflow_dispatch` only exposes a "Run workflow" control once the workflow file has reached the default branch, so CI cannot do it from this branch yet. The ticket stays open; DEPLOYMENT.md carries the runbook and the unfilled URL. Refs #1077 Files changed: - .github/workflows/ci-mock-api.yml - mock-api/DEPLOYMENT.md - mock-api/wrangler.jsonc
Contributor
|
🚀 Website Preview Deployed! Preview your changes at: https://pr-1088-common-grants.brian-derfer.workers.dev Built from commit 5bbc3c4 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is a timeboxed experiment, not a feature. It may close unmerged. The point is
to find out what ADR Option 3A would actually cost.
Changes proposed
New
mock-api/workspace package: a small Cloudflare Worker that serves fake grantopportunity data at one real URL.
mock-api/a Worker serving three opportunity endpoints (list, detail, search)for spec versions 0.1.0–0.4.0, with CORS. 11 fixed records, same answer every time.
to drop MSW.
generates, so the mock can't silently drift from the protocol.
.github/workflows/ci-mock-api.ymlruns checks and tests on PRs; deploys byhand (
workflow_dispatch) to keep the experiment cheap.mock-api/DEPLOYMENT.mdwhat deploying costs, and what's still missing.Nothing outside
mock-api/changes behaviour. The only shared edits are adding thepackage to the workspace and a
ci:mock-apiscript.Context for reviewers
Not done yet: nothing is deployed yet (WIP). The
runbook to deploy is in
mock-api/DEPLOYMENT.md.Three bugs the conformance test found. Two are not the mock's fault and are worth
their own issues:
ApplicantType,OppStatus, andthree others are only written into
v0.4.0/, though the changelog says they wereadded in 0.2.0. Cause is in
website/src/lib/schema/version-generator.ts, it skipsa check when a version has no changelog entry. This also means the docs site's own
versioned schema pages are wrong for those models.
isoTimedoesn't match its own format. It saysformat: timebut describesitself as
HH:mm:sswith no timezone, and publishes17:00:00as its example. JSONSchema's
timerequires a timezone, so any strict validator rejects the protocol'sown example.
CompetitionBaserequires. Inherited from [POC] #1034 Test A: client-side MSW mock playground #1049 on purpose. Pinned by a test thatwill fail if someone fixes the fixture, so it can't be forgotten.
How to try it:
pnpm install && pnpm --filter @common-grants/mock-api run devHow it was verified: 232 tests pass. Endpoints, CORS preflight, and error shapes
checked by hand against
wrangler dev. The deploy job's smoke test was run against alocal Worker to confirm it works. Reviewed by subagent at each step; the review caught
a masked deploy failure (
teeswallowing wrangler's exit code), a concurrency groupthat allowed the race it was meant to stop, and an audit step that could be skipped on
a deploying run.
Additional information
Endpoints, CORS preflight, and the unsupported-version error, against

wrangler dev:Full package CI:
