feat(user-manager-client): stateful Counterfact mock with seed/reset + E2E (LLMO-5616)#1685
feat(user-manager-client): stateful Counterfact mock with seed/reset + E2E (LLMO-5616)#1685byteclimber wants to merge 7 commits into
Conversation
…ion pipeline Foundation slice for @adobe/spacecat-shared-user-manager-client, mirroring the Project Engine foundation (LLMO-5461) applied to the larger User Manager API (LLMO-5558). Private, types-only package: vendors the Swagger 2.0 spec (/enterprise/users/api, 234 paths / 284 operations, 187 Auth-Data-Jwt routes) and wires the swagger2openapi --patch -> openapi-typescript / datamodel-codegen pipeline behind `npm run generate`. The openapi-fetch client wrapper with dual auth (IMS bearer + API-Key) and the counterfact mock store land in follow-up PRs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generated by `npm run generate` from the vendored spec. Committed in its own commit and marked linguist-generated (see .gitattributes) so it collapses in review and is excluded from diff counts. - src/generated/types.ts: openapi-typescript 7.13.0 paths/components (all ops) - python/serenity_user_manager/: datamodel-codegen pydantic_v2 package Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds @adobe/spacecat-shared-user-manager-client and its generation-pipeline devDeps (openapi-typescript, swagger2openapi, counterfact) to the root lockfile so `npm ci` stays in sync in CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ests Addresses mysticat-bot review: `/child` and `/status` were too generic and would pass against any large OpenAPI types file. Replace with the full, unambiguous path templates that only exist in this spec. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auth-Data-Jwt appears on ~187 operations in the vendored spec but is a vendor spec artifact — the live Adobe gateway authenticates on Authorization: Bearer only (same finding as rainer-friederich CR2 on Project Engine PR #1661, 2026-06-15). Sending Auth-Data-Jwt returns 401. Update README and test label to make clear this is a spec artifact, not the live auth contract. A generation-time overlay to strip it from generated types follows in a later PR. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…+ E2E (LLMO-5616) Adds a stateful mock of the Semrush User Manager API for E2E tests and local dev. - Stateful core chain on Counterfact's Context singleton (`.counterfact/routes/_.context.ts`): workspaces create-child/get/update/delete/list, members, profile, resources (+ deprecated `/limits` alias), service-units balance. POST is reflected in a later GET. - Seed-on-start + `POST /__reset` to return to a known fixture state between tests. - `npm run mock` serves on :4010 under `/enterprise/users/api` with `--no-validate-request` (permissive auth; the spec's `Auth-Data-Jwt` is a vendor artifact). - E2E suite (`test/mock.test.js`) boots the mock in a child process: statefulness, reset, negative path, committed-handler + env-var assertions. 17 tests, 100% coverage on `src/`. Architecture note: Counterfact only compiles its own `routes/` tree, so mock state lives on the Context class, not `src/` (verified: an imported external store 500s). The `.counterfact/` tree is committed except `.cache/`; regeneration preserves hand-edited routes. `.gitignore`, `clean`, and the root eslint ignore are updated accordingly. Defers api-service wiring to LLMO-5615 (api-service calls no User Manager endpoints today); the future client's base-URL env var `SEMRUSH_USERS_BASE_URL` is recorded in `src/config.js`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ount, dedup nf, more coverage
- deleteMembers now returns the count of members actually removed (null only
when the workspace is missing), mirroring the spec's "number of user units
freed" semantics where 0 is a valid success. Previously returned {deleted:true}
even for absent members; the DELETE handler now returns 200 + count.
- Extract the duplicated `nf()` 404-or-500 responder into _.helpers.ts (an
underscore-prefixed, compiled-not-routed sibling of _.context.ts).
- Add E2E coverage for the { members: [...] } and bare-array body shapes,
the missing-workspace add path, delete-member count (1 and 0), and the
update-missing-member error path.
- Clarifying comments: clone() JSON-safety constraint, nextId/reset coupling,
and why the test port (4099) differs from the documented default (4010).
22 passing, 100% coverage on src/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This PR will trigger a minor release when merged. |
|
Heads-up now that this mock stacks on the User Manager foundation branch ( What this PR already gets right (the Project Engine mock had to be fixed for both): the The one coupling left — CR2 (workspace-status shape). After rebasing onto the updated base to pick up the overlay ( export const GET: workspacesStatusGet = async ($) => {
return $.response[200].random();
};
Two ways to fix — pick one:
Context — the overlay + corrections that introduce CR2: |
Stacked on #1680 (foundation). Implements LLMO-5616 — a stateful mock of the Semrush User Manager API for E2E tests + local dev.
What works (17 tests, 100% coverage on
src/)/limitsalias), service-units balance.POST /__resetreturns to it; E2E suite resets inbeforeEach.npm run mock→:4010under/enterprise/users/api, permissive auth (--no-validate-request).Reviewer focus — the ~13 hand-authored files (rest of
.counterfact/is generated, markedlinguist-generated).counterfact/routes/_.context.ts— the store (state + seed/reset).counterfact/routes/__reset.ts.counterfact/routes/v1/profile.ts,v1/workspaces.ts,v1/workspaces/{id}.ts,v1/workspaces/{id}/child.ts,v1/workspaces/{id}/members.ts,v1/workspaces/{id}/resources.ts,v1/workspaces/resources.ts,v1/workspaces/{id}/limits.ts,v1/workspaces/limits.ts,v1/workspaces/{id}/service-units/balance.ts,v2/workspaces/{id}.tssrc/config.js,test/mock.test.js, README "Mock" section;package.json/.gitignore/clean/root eslint ignore.Key architecture decision (validated, differs from the original plan)
Counterfact only compiles its own
routes/tree, so mock state lives on the Context class ($.context), notsrc/— an imported external store 500s. The logic is therefore validated behaviorally by the E2E suite (the 100% gate coverssrc/only). The.counterfact/tree is committed except.cache/.Deferred (LLMO-5615)
spacecat-api-service calls no User Manager endpoints today, so wiring it to the mock is deferred to when the client lands. The base-URL env var
SEMRUSH_USERS_BASE_URLis recorded insrc/config.js; re-open trigger documented in the README (incl. the http-localhost-vs-https caveat).Known caveat
Counterfact warns of ambiguous sibling wildcards (
{id}vs{workspace_id}) on some non-core paths; the core chain uses{id}and routes correctly.🤖 Generated with Claude Code