Skip to content

fix(mock): emit strict mock types in tags-split MSW+faker output - #3575

Merged
melloware merged 5 commits into
orval-labs:masterfrom
Hypenate:fix/strict-mock-tags-split-msw-faker
Jun 9, 2026
Merged

fix(mock): emit strict mock types in tags-split MSW+faker output#3575
melloware merged 5 commits into
orval-labs:masterfrom
Hypenate:fix/strict-mock-tags-split-msw-faker

Conversation

@Hypenate

@Hypenate Hypenate commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Preserve strictMockSchemaTypeNames when building tag targets in tags-split mode so finalizeMockImplementation / dedupeStrictMockTypeDeclarations prepends KeysWithNull, MockWithNullableOverrides, and {Schema}Mock aliases in per-tag .msw.ts and .faker.ts files.
  • Scope array-item factory dedup per mock generator file (msw vs faker) so faker output defines schema factories (e.g. getPetMock) it references instead of relying on the MSW file.
  • Add regression fixtures for fix(mock): prepend strict mock type declarations in tags-split MSW/faker output #3574: OpenAPI spec, angular + fetch mock configs, snapshots, and a focused assertion in api-generation.spec.ts.

Fixes #3574

Test plan

  • bun test in @orval/mock (includes new array-item factory scope test)
  • bun run generate:mock for issue3574StrictMockTagsSplitAngular and issue3574StrictMockTagsSplitFetch
  • Snapshot tests cover new fixtures under tests/__snapshots__/mock/issue-3574-*
  • node tests/scripts/typecheck-generated.mjs passes (mock folder includes new generated output)
  • Existing issue-3525 / issue-3525-multi / issue-3525-widget-mock snapshots unchanged

Summary by CodeRabbit

  • New Features

    • Mock generation now scopes outputs per mock type (e.g., MSW vs Faker) and consistently preserves strict mock type names across generated mock artifacts.
    • Generated client and mock modules for pets/store include faker-backed factories, MSW handlers, nullable-aware override typings, and new index entrypoints for simpler imports.
  • Tests

    • Added tests, snapshots and configs validating output-type-specific mock emission and strict-mock type handling.

Preserve strictMockSchemaTypeNames in tag targets.

Scope array-item factory dedup per mock generator file.

Fixes #3574

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 69feffbd-21b6-44b7-9fbc-601a1cbe758c

📥 Commits

Reviewing files that changed from the base of the PR and between f2eb973 and b490ff4.

📒 Files selected for processing (2)
  • packages/mock/src/mock-types.test.ts
  • packages/mock/src/mock-types.ts

📝 Walkthrough

Walkthrough

Adds output-type-aware mock generation (ContextSpec.activeMockOutputType), preserves strictMockSchemaTypeNames through mock-output merging/wrapping, scopes array-item factory keys by mock output type, and includes updated tests and generated snapshot fixtures exercising tags-split MSW+faker strict-mock output.

Changes

Strict Mock Types in Tags-Split Output

Layer / File(s) Summary
Output-type-aware context and dispatch
packages/core/src/types.ts, packages/mock/src/faker/getters/array-item-factory.ts, packages/mock/src/index.ts
ContextSpec gains activeMockOutputType; generateMock sets/restores it around per-generator dispatch; getArrayItemMockFileScope appends the active mock output type to scope keys (e.g., single:faker, tag:alpha:faker).
Array-item factory test coverage
packages/mock/src/faker/getters/array-item-factory.test.ts
Update scope-key assertions to expect output-type suffixes and add a test that toggles context.activeMockOutputType to verify $ref array-item factories are emitted separately per mock output type.
Strict type-name collection and propagation
packages/mock/src/mock-types.ts, packages/mock/src/mock-types.test.ts, packages/mock/src/msw/index.ts
Add helpers to collect strict-mock schema names from generated implementations, merge them into MSW mock metadata, and add tests validating extraction and name handling.
Mock output metadata preservation
packages/core/src/writers/target-tags.ts
Thread strictMockSchemaTypeNames into initial per-operation outputs, merges, and header/footer wrapping so strict type declarations can be emitted per generated file.
Test configuration and OpenAPI specs
tests/configs/mock.config.ts, tests/specifications/issue-3574-strict-mock-tags-split.yaml, tests/specifications/issue-3574-strict-mock-tags-split-multi.yaml
Add tags-split mock configs (Angular and fetch) and OpenAPI specs used to generate snapshot fixtures for single-tag Pets and multi-schema Store.
Generated fetch fixture
tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/**
Snapshot MSW and faker outputs for fetch client: index re-exports, typed fetch client helpers, faker factories with MockWithNullableOverrides helpers, MSW handlers, and schema snapshots exposing strict mock types.
Generated Angular fixture
tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/**
Snapshot MSW and faker outputs for Angular service: index re-exports, typed PetsService, faker factories with nullable-override helpers, MSW handlers with 150ms delay, and schema snapshots exposing strict mock types.
Generated multi-schema fixture
tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-multi-fetch/**
Snapshot MSW and faker outputs for multi-schema store fixture: schema faker modules for Pet/Owner, MSW handlers, client helpers, and index re-exports.
Snapshot test assertions
tests/api-generation.spec.ts
Vitest tests asserting generated MSW and faker files include strict mock types (MockWithNullableOverrides, PetMock, KeysWithNull) and getPetMock, and that multi-schema outputs include per-tag strict types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

mock, msw

Suggested reviewers

  • melloware

"🐰 I hopped through tags and mock-filled nights,
Faker and MSW now keep their sights.
Each file knows its own little type,
No missing exports — tests take flight!
Hooray — the rabbits dance in byte-sized light."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically identifies the main change: fixing strict mock type emission in tags-split mode with MSW and faker generators.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding objectives from issue #3574: preserving strictMockSchemaTypeNames in tags-split targets, scoping array-item factories per mock generator file, and adding regression test fixtures.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing #3574: core type definitions, writer logic for preserving strict mock metadata, test fixtures for regression coverage, and helper utilities for extracting schema type names from implementations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/writers/target-tags.ts (1)

50-69: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve accumulated strictMockSchemaTypeNames when cloning merge state.

At Line 50, the accumulator clone drops previously collected strictMockSchemaTypeNames, so only later operations’ names survive. That can omit required strict aliases in finalized per-tag mock files.

Proposed fix
 function mergeOperationMockOutputs(
   accMockOutputs: GeneratorMockOutputFull[],
   opMockOutputs: GeneratorMockOutputFull[],
 ): GeneratorMockOutputFull[] {
   const result: GeneratorMockOutputFull[] = accMockOutputs.map((m) => ({
     type: m.type,
     implementation: { ...m.implementation },
     imports: [...m.imports],
+    strictMockSchemaTypeNames: m.strictMockSchemaTypeNames
+      ? [...m.strictMockSchemaTypeNames]
+      : undefined,
   }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/writers/target-tags.ts` around lines 50 - 69, The clone of
accumulated mock outputs (result built from accMockOutputs) is dropping existing
strictMockSchemaTypeNames so earlier collected names are lost; update the
mapping of accMockOutputs to include cloning of strictMockSchemaTypeNames (e.g.,
copy m.strictMockSchemaTypeNames into the new object) so GeneratorMockOutputFull
entries preserve that array when creating result, and keep the existing merge
logic in the opMockOutputs loop (references: result, accMockOutputs,
opMockOutputs, emptyMockOutputFull, strictMockSchemaTypeNames,
GeneratorMockOutputFull).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/api-generation.spec.ts`:
- Around line 35-45: The test is using the non-fetch fixture name
'issue-3574-strict-mock-tags-split' which should point to the fetch fixture;
update the fixture identifier passed to the generated(...) calls (specifically
the petsMsw and petsFaker variable initializations) from
'issue-3574-strict-mock-tags-split' to 'issue-3574-strict-mock-tags-split-fetch'
so the regression test targets the fetch fixture directory.

---

Outside diff comments:
In `@packages/core/src/writers/target-tags.ts`:
- Around line 50-69: The clone of accumulated mock outputs (result built from
accMockOutputs) is dropping existing strictMockSchemaTypeNames so earlier
collected names are lost; update the mapping of accMockOutputs to include
cloning of strictMockSchemaTypeNames (e.g., copy m.strictMockSchemaTypeNames
into the new object) so GeneratorMockOutputFull entries preserve that array when
creating result, and keep the existing merge logic in the opMockOutputs loop
(references: result, accMockOutputs, opMockOutputs, emptyMockOutputFull,
strictMockSchemaTypeNames, GeneratorMockOutputFull).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a86c2c88-0f6b-4165-9b98-a714b3addbe5

📥 Commits

Reviewing files that changed from the base of the PR and between 08170d7 and c083b57.

📒 Files selected for processing (23)
  • packages/core/src/types.ts
  • packages/core/src/writers/target-tags.ts
  • packages/mock/src/faker/getters/array-item-factory.test.ts
  • packages/mock/src/faker/getters/array-item-factory.ts
  • packages/mock/src/index.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/pets/index.faker.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/pets/index.msw.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/pets/pets/pets.faker.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/pets/pets/pets.msw.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/pets/pets/pets.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/schemas/index.faker.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/schemas/index.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/schemas/pet.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/pets/index.faker.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/pets/index.msw.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/pets/pets/pets.faker.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/pets/pets/pets.msw.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/pets/pets/pets.service.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/schemas/index.ts
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/schemas/pet.ts
  • tests/api-generation.spec.ts
  • tests/configs/mock.config.ts
  • tests/specifications/issue-3574-strict-mock-tags-split.yaml

Comment thread tests/api-generation.spec.ts
Use issue-3574-strict-mock-tags-split-fetch for the focused assertion.

Allow getPetMock via import when schemas: true delegates to index.faker.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Hypenate

Hypenate commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@wadakatu

Can you verify the fix?
We're having some troubles with the latest version:

✘ [ERROR] TS2304: Cannot find name 'MockWithNullableOverrides'. [plugin angular-compiler]

    libs/content-engine/shared/api/src/lib/generated/admin/admin.msw.ts:99:8:
      99 │   }) as MockWithNullableOverrides<BatchDto, O, BatchDtoMock>;
         ╵         ~~~~~~~~~~~~~~~~~~~~~~~~~


✘ [ERROR] TS2304: Cannot find name 'BatchDtoMock'. [plugin angular-compiler]

    libs/content-engine/shared/api/src/lib/generated/admin/admin.msw.ts:99:47:
      99 │   }) as MockWithNullableOverrides<BatchDto, O, BatchDtoMock>;

This PR should fix this.

@wadakatu

wadakatu commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for the fix @Hypenate. I verified the per-generator file scoping (B) and the first-op / wrapped-output preservation (A) — both work. But there's a remaining drop point in (A), and I think it still leaves the reporter's real-world case broken.

Residual bug: mergeOperationMockOutputs drops strictMockSchemaTypeNames

In packages/core/src/writers/target-tags.ts, mergeOperationMockOutputs is called for every operation after the first one in a tag. Its initial .map() copies imports but not strictMockSchemaTypeNames:

// target-tags.ts (~L50)
const result: GeneratorMockOutputFull[] = accMockOutputs.map((m) => ({
  type: m.type,
  implementation: { ...m.implementation },
  imports: [...m.imports],
  // strictMockSchemaTypeNames is NOT carried over here
}));

So the accumulator's strict names are reset to undefined on each merge, and the loop below only re-adds the current op's names. Net effect: only the schema referenced by the last-merged operation in a tag keeps its strict type. (The non-tags target.ts mutates the same acc object in place, so it accumulates correctly — only the tags/tags-split path has this asymmetry. Note this code also serves mode: 'tags'.)

Repro (built this PR, then generated)

One tag (Store) with two operations returning different schemas — getPets → Pet[], getOwner → Owner — under tags-split + strict + generators: [msw, faker]:

// generated store.msw.ts
export type OwnerMock = { ... };        // last op only — PetMock is never declared
export const getPetMock = <O extends Partial<Pet> = {}>(overrideResponse?: O):
  MockWithNullableOverrides<Pet, O, PetMock> => ...   // references undeclared PetMock
store.msw.ts(41):  error TS2304: Cannot find name 'PetMock'.
store.faker.ts(33): error TS2304: Cannot find name 'PetMock'.   (x3)

This is the same error class as #3574. A real tag with many operations/DTOs (e.g. the reporter's admin module with BatchDto) would still fail after this PR — every DTO except the last operation's would be missing its Mock type. The current regression fixture doesn't catch it because both ops reference the same Pet (so the last merge re-adds it) — I'll note the coverage side separately.

Suggested fix (mirror initialMockOutputsForOperation)

const result: GeneratorMockOutputFull[] = accMockOutputs.map((m) => ({
  type: m.type,
  implementation: { ...m.implementation },
  imports: [...m.imports],
  strictMockSchemaTypeNames: m.strictMockSchemaTypeNames
    ? [...m.strictMockSchemaTypeNames]
    : undefined,
}));

With this, both PetMock and OwnerMock are emitted in the .msw.ts and .faker.ts files and tsc reports 0 TS2304. I also re-generated the existing issue-3574-strict-mock-tags-split-fetch fixtures with this change and they are byte-identical to the committed snapshots, so it doesn't regress the single-schema case.

@wadakatu

wadakatu commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Test coverage gap (separate from the residual bug above)

The regression fixtures (issue-3574-strict-mock-tags-split / -fetch) put a single schema (Pet) behind both operations of the one tag. Because both ops reference the same schema, the mergeOperationMockOutputs path re-adds Pet on the final merge, so the suite stays green even when per-operation strict names are being dropped. In other words, the current fixture can't distinguish "correctly accumulated" from "only the last op survived".

To actually guard the merge path, I'd suggest a fixture where one tag has ≥2 operations returning distinct schemas, e.g. a Store tag with getPets → Pet[] and getOwner → Owner. Then assert that each generated .msw.ts / .faker.ts declares export type PetMock and export type OwnerMock (or run tsc over the generated output). That fixture fails on master + this PR today and passes with the one-line fix.

Copy strictMockSchemaTypeNames in mergeOperationMockOutputs.

Add multi-schema Store tag fixture and regression test for #3574.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Hypenate

Hypenate commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@wadakatu Applied your comments

Hypenate and others added 2 commits June 9, 2026 11:09
Scan generated implementation for MockWithNullableOverrides and SchemaMock refs.

Merge with response schema names so nested helpers get type declarations.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop prefix captures when a longer nameMock schema was also collected.

Fixes Widget extracted from WidgetMock schema references.

Co-authored-by: Cursor <cursoragent@cursor.com>
@wadakatu

wadakatu commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

LGTM — thanks for the quick turnaround, and nice catch going past my two notes to the actual nested-factory case. I rebuilt the PR at b490ff4 and verified:

  • Residual merge bug (mergeOperationMockOutputs): fixed exactly as suggested, and the new multi-schema Store fixture/test guards it. 👍
  • Nested split factories (f2eb97327): I reproduced the reporter-style case — an Admin tag with getBatches → BatchDto[] (arrayItems), getTenant → TenantInfoDto, and getReport → ReportDto where ReportDto.lines is a nested $ref array of LineItemDto. All of BatchDtoMock / TenantInfoDtoMock / ReportDtoMock / LineItemDtoMock are now declared in both admin.msw.ts and admin.faker.ts. LineItemDto only ever appears via the nested item factory, so it's a good proof that the implementation scan does its job.
  • tsc over the generated output: 0 TS2304.
  • @orval/mock unit tests: 232 passing.
  • No churn in existing snapshots (only the new issue-3574-...-multi-fetch fixture), CI green.

🚢 from my side.

@wadakatu

wadakatu commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Two non-blocking nits for a possible follow-up — feel free to ignore for this PR:

  1. Recovering schema names by regex-scanning generated text (collectStrictMockSchemaTypeNamesFromImplementation) is pragmatic and well tested, but it's reverse-parsing the output, so it's coupled to the exact factory syntax. The structurally robust place to collect a nested item's schema name is where the $ref split factory is generated (the array-item / faker getters, where the ref name is known), then thread it into strictMockSchemaTypeNames. Worth at most a code comment noting the coupling.

  2. The WidgetMock false-positive guard (filter(name => !names.has(${name}Mock))) has one residual edge: if a spec has both a Widget and a WidgetMock schema used as strict mocks in the same file, Widget gets dropped. Extremely contrived, just flagging it.

@Hypenate

Hypenate commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I'll do a follow-up PR with your remarks when the PR from @kfrederix has been merged.
And if that is ok with @melloware

@melloware melloware added mock Related to mock generation msw MSW related issues labels Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mock Related to mock generation msw MSW related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(mock): prepend strict mock type declarations in tags-split MSW/faker output

3 participants