Skip to content

feat(openapi-mock): add package for generating OpenAPI-shaped fake responses - #5204

Merged
odinr merged 4 commits into
feat/fusion-testingfrom
feat/openapi-mock-utility
Aug 4, 2026
Merged

feat(openapi-mock): add package for generating OpenAPI-shaped fake responses#5204
odinr merged 4 commits into
feat/fusion-testingfrom
feat/openapi-mock-utility

Conversation

@odinr

@odinr odinr commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Why is this change needed?
Extracted from the poc/framework-testability branch cleanup. @equinor/fusion-openapi-mock is a standalone, framework-agnostic utility and deserves its own history/PR rather than being entangled with the HTTP-mock-module and MSAL/service-discovery mock work. It also has its own tracked user story: equinor/fusion-core-tasks#1660.

What is the current behavior?
No package exists for generating fake responses from an OpenAPI document. Testing an API-shaped client currently requires hand-written fixtures for every endpoint.

What is the new behavior?
Adds @equinor/fusion-openapi-mock:

  • createOpenApiMock(document, options?) fakes every operation with an operationId from its declared success response schema ($refs resolved against the document).
  • overrides (at construction) and .register(operationId, handler) (afterwards) replace faked output for specific operations.
  • seed makes faked output repeatable across runs.
  • fetchOpenApiDocument(url, options?) fetches/parses a JSON or YAML spec from a URL.
  • fields (a FieldFakerMap) fakes specific fields with a @faker-js/faker path string or function; loadFakerMap(source) loads one from a sidecar file.
  • No dependency on any HTTP or routing framework — resolve({ method, path, query }) returns a plain { status, mock }.

What is the intended behavior or invariant?
resolve() returns undefined for any request that matches no operation in the document, so callers (e.g. @equinor/fusion-framework-module-http's mock router via fromOpenApiMock) can compose it with other registrations covering endpoints outside the spec. The package has zero runtime dependency on any HTTP module — consumers duck-type against OpenApiMockLike.

Does this PR introduce a breaking change?
No — this is a new package.

Impact assessment:

  • Breaking changes: No
  • Version bump: Minor (initial release, see changeset)
  • Consumer impact: New opt-in package; no existing packages depend on it yet
  • Downstream impact: None yet — @equinor/fusion-framework-module-http's fromOpenApiMock adapter is duck-typed against this package's shape but has no hard dependency

Review guidance:
Focus on the public API surface (createOpenApiMock, fetchOpenApiDocument, loadFakerMap) and TSDoc. Tests cover schema dereferencing, field-faker application, and mock generation from schema.

Additional context
Extracted cleanly from poc/framework-testability — verified pnpm --filter @equinor/fusion-openapi-mock test run, build, biome check, and fusion-lint lint packages/utils/openapi-mock all pass with no issues.

Related issues
closes: equinor/fusion-core-tasks#1660
ref: equinor/fusion-core-tasks#1654

Checklist

  • Confirm completion of the self-review checklist
  • Confirm TSDoc captures intent for functions, hooks, components, classes, and named arrow functions
  • Confirm iterator blocks, decision gates, RxJS chains, and complex decisions explain why they exist
  • Confirm React logic and derived values are resolved before markup when applicable (N/A — no React in this package)
  • Confirm README/docs are updated for user-facing changes
  • Confirm changes to target branch validation
    • Included files validated
    • No new linting warnings
    • Not a duplicate PR
  • Confirm adherence to code of conduct

@changeset-bot

changeset-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1f7b278

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@equinor/fusion-openapi-mock Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added 📚 documentation Improvements or additions to documentation 🚀 feature New feature or request 🚧 chore maintaines work, (update deps, workflos ...) 🛠️ utils packages related to utils labels Aug 3, 2026
odinr and others added 2 commits August 3, 2026 23:05
…sponses

Adds @equinor/fusion-openapi-mock, a framework-agnostic utility that fakes
OpenAPI 3 responses straight from a parsed spec document. Every operation
with an operationId is faked from its declared success response schema,
with overrides/register for edge cases, seed for repeatable output, and
a FieldFakerMap for realistic per-field values via @faker-js/faker.

No dependency on any HTTP or routing framework: resolve({ method, path,
query }) returns a plain { status, mock }, so it composes with
@equinor/fusion-framework-module-http's mock router or any other server.

Related: equinor/fusion-core-tasks#1660

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@odinr
odinr force-pushed the feat/openapi-mock-utility branch from e7abda6 to ce7ea98 Compare August 3, 2026 21:05
@odinr
odinr marked this pull request as ready for review August 3, 2026 21:09
@odinr
odinr requested a review from a team as a code owner August 3, 2026 21:09
@odinr
odinr requested a review from Copilot August 3, 2026 21:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new published utility package, @equinor/fusion-openapi-mock, for generating OpenAPI-shaped fake responses (with overrides, seeding, and field-level faker mapping), plus accompanying tests, docs, and a changeset for the initial release.

Changes:

  • Introduce @equinor/fusion-openapi-mock public API (createOpenApiMock, fetchOpenApiDocument, loadFakerMap) and supporting internals for $ref dereferencing and schema-based mock generation.
  • Add Vitest coverage for dereferencing, document fetching/parsing, sidecar faker maps, and resolve/override behavior.
  • Add package metadata/docs + changeset, and update CODEMAP/lockfile to include the new package and dependencies.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds the new package importer and locks new deps (json-schema-faker, yaml, etc.).
packages/utils/openapi-mock/vitest.config.ts Vitest project config for the new package.
packages/utils/openapi-mock/tsconfig.json TS build configuration for the new package (project references, output dirs).
packages/utils/openapi-mock/package.json New published package manifest, scripts, and dependencies.
packages/utils/openapi-mock/README.md Consumer-facing docs and usage examples for the new mock generator.
packages/utils/openapi-mock/src/index.ts Public export surface for the package.
packages/utils/openapi-mock/src/types.ts Public types for documents, mocks, overrides, and field faker mappings.
packages/utils/openapi-mock/src/load-faker-map.ts Sidecar loader for FieldFakerMap (JSON/YAML/script via importConfig).
packages/utils/openapi-mock/src/fetch-open-api-document.ts Fetch + parse helper for JSON/YAML OpenAPI docs.
packages/utils/openapi-mock/src/dereference-schema.ts $ref inlining helper with cycle protection.
packages/utils/openapi-mock/src/generate-mock-from-schema.ts Schema-to-value generation using json-schema-faker + @faker-js/faker.
packages/utils/openapi-mock/src/create-open-api-mock.ts Core routing + operation indexing + override/resolve implementation.
packages/utils/openapi-mock/src/apply-field-fakers.ts Field-level faker-map application by annotating schemas before generation.
packages/utils/openapi-mock/tests/load-faker-map.test.ts Tests for sidecar parsing/loading behavior.
packages/utils/openapi-mock/tests/fetch-open-api-document.test.ts Tests for JSON/YAML parsing and non-OK fetch handling.
packages/utils/openapi-mock/tests/dereference-schema.test.ts Tests for $ref resolution, arrays, and cycle breaking.
packages/utils/openapi-mock/tests/create-open-api-mock.test.ts Tests for routing, schema-based mocking, overrides, and seeding.
packages/utils/openapi-mock/tests/fixtures/fields-yaml.faker.yaml YAML fixture for loadFakerMap.
packages/utils/openapi-mock/tests/fixtures/fields-ts.faker.ts TS sidecar fixture (default export with real function).
packages/utils/openapi-mock/tests/fixtures/fields-json.faker.json JSON fixture for loadFakerMap.
CODEMAP.md Updates published package count and adds the new package to the map.
.changeset/openapi-mock_initial-release.md Changeset for initial minor release of the new package.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/utils/openapi-mock/src/create-open-api-mock.ts
Comment thread packages/utils/openapi-mock/src/create-open-api-mock.ts Outdated
Comment thread packages/utils/openapi-mock/src/load-faker-map.ts
Comment thread packages/utils/openapi-mock/src/fetch-open-api-document.ts Outdated
Comment thread packages/utils/openapi-mock/src/generate-mock-from-schema.ts Outdated
odinr added 2 commits August 4, 2026 08:48
…e fallback, and validation

- sort operations by specificity so literal routes win over templated ones
- fall back to lowest declared numeric status when no 2xx/default response exists
- validate parsed YAML faker maps and fetched documents are plain objects
- use an isolated faker instance per generation instead of mutating the global singleton
- refactor createOpenApiMock and apply-field-fakers into smaller, focused functions
- replace && shorthand and let-based branches with explicit if/early-return
- extract resolveResponseSchema, parseDocumentText helpers
- rewrite resolvePointer's pointer walk with Array#reduce instead of a mutable loop variable
@github-actions github-actions Bot added the 🐞 bug Something isn't working label Aug 4, 2026
@odinr
odinr requested a lite review from Copilot August 4, 2026 06:59
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 67.44% 3327 / 4933
🔵 Statements 66.66% 3977 / 5966
🔵 Functions 52.6% 1151 / 2188
🔵 Branches 57.21% 1855 / 3242
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/utils/openapi-mock/src/apply-field-fakers.ts 91.83% 84.84% 100% 100% 42, 46, 114, 141
packages/utils/openapi-mock/src/create-open-api-mock.ts 88.75% 76.66% 96.42% 90.14% 72, 83-93
packages/utils/openapi-mock/src/dereference-schema.ts 95.83% 94.73% 100% 100% 11
packages/utils/openapi-mock/src/fetch-open-api-document.ts 91.66% 80% 100% 91.66% 65
packages/utils/openapi-mock/src/generate-mock-from-schema.ts 100% 100% 100% 100%
packages/utils/openapi-mock/src/load-faker-map.ts 87.5% 77.77% 100% 86.66% 22, 28-30
Generated in workflow #15082 for commit 1f7b278 by the Vitest Coverage Report Action

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 22 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Suppressed comments (4)

packages/utils/openapi-mock/src/create-open-api-mock.ts:256

  • options.overrides entries are never validated against the operations in the document, so a misspelled operationId in overrides is silently ignored (contradicting the @throws doc and the intent of failing fast on typos). Validate the keys eagerly and throw the same error message used elsewhere.
  const operations = buildOperations(document);
  const overrides = new Map<string, OpenApiMockOverride>(Object.entries(options.overrides ?? {}));

packages/utils/openapi-mock/src/index.ts:14

  • The package-level TSDoc says resolve() returns { status, mock }, but the actual return type/value includes operationId and params as well. This mismatch can mislead consumers reading the generated docs.
 * This package has no opinion on HTTP or routing frameworks: {@link OpenApiMock.resolve}
 * takes a plain `{ method, path, query }` and returns a plain
 * `{ status, mock }`, so it drops into `@equinor/fusion-framework-module-http`'s

.changeset/openapi-mock_initial-release.md:24

  • The changeset claims resolve({ method, path, query }) returns { status, mock }, but the public API actually returns { status, mock, operationId, params }. Aligning this text avoids publishing misleading release notes for consumers.
- No dependency on any HTTP or routing framework: `resolve({ method, path, query })` returns a plain `{ status, mock }`, so it drops into `@equinor/fusion-framework-module-http`'s mock router, `openapi-backend`, Express, or a hand-rolled server equally easily.

packages/utils/openapi-mock/src/load-faker-map.ts:92

  • loadFakerMap validates YAML sidecars, but .json (and .ts/.js) sidecars loaded via importConfig are returned without runtime shape validation. A malformed .json map (non-string values) will fail later in less obvious ways; validate the resolved config immediately and throw an error that names the resolved file.
  // .json, .ts, .js, .mjs (or no extension) resolve through `importConfig`'s own
  // extension probing, so a sidecar can be authored in whichever of those formats
  // (real functions require .ts/.js/.mjs, since JSON can't hold one)
  const basename = source.replace(/\.(json|ts|mjs|js)$/i, '');
  const { config } = await importConfig<FieldFakerMap>(basename, { baseDir: options.baseDir });
  return config;

@odinr
odinr requested a review from Noggling August 4, 2026 07:05
@odinr odinr self-assigned this Aug 4, 2026
@odinr
odinr merged commit b5b2f1d into feat/fusion-testing Aug 4, 2026
14 checks passed
@odinr
odinr deleted the feat/openapi-mock-utility branch August 4, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 bug Something isn't working 🚧 chore maintaines work, (update deps, workflos ...) 📚 documentation Improvements or additions to documentation 🚀 feature New feature or request 🛠️ utils packages related to utils

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants