Skip to content

test(downgrader): cover restriction-loss and conversion gaps found by mutation testing - #96

Open
dinwwwh wants to merge 2 commits into
mainfrom
claude/project-thread-kczaq1
Open

dinwwwh wants to merge 2 commits into
mainfrom
claude/project-thread-kczaq1

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Requested by dinwwwh · project thread

Test-only. Closes gaps that Stryker found in the downgrader. No source changes.

3.1 to 3.0, "a schema that lost a restriction" (3 new cases):

  • A lost restriction carries over to the schema around it, through allOf, anyOf, properties, not and oneOf.
  • propertyNames with a pattern counts as lost (as zod emits for z.record with a key pattern); propertyNames: true doesn't.
  • unevaluatedProperties beside allOf counts as lost; beside additionalProperties it doesn't.

Extra expects in existing tests:

  • { enum: [null, 'a'] } keeps 'a'.
  • contentEncoding: 'binary' gets format: 'binary'.
  • An anyOf branch with contentMediaType keeps the anyOf.
  • { type: 'number', const: 5 } (Zod z.literal(5)) keeps its written type: { type: 'number', enum: [5] }.
  • { examples: [] } becomes exactly {}.
  • A oneOf branch with an undefined-valued keyword (if: undefined) keeps the oneOf.
  • 3.2 to 3.1: a response $ref stays as written next to the summary handling.

expectValidDowngrade now also checks the output holds only what JSON keeps (toStrictEqual(JSON.parse(JSON.stringify(output)))), so a key holding undefined (e.g. a tag, info or example written as undefined) fails.

All 265 tests and pnpm run check pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EjMyAKtd1J94kr1PfQeQza

… mutation testing

Covers lost restrictions carried through allOf, anyOf, properties, not and
oneOf; propertyNames and unevaluatedProperties edge cases; contentMediaType
in an anyOf branch; contentEncoding "binary"; enum [null, "a"]; and a 3.2 to
3.1 response $ref staying as written.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EjMyAKtd1J94kr1PfQeQza
@dinwwwh dinwwwh self-assigned this Oct 24, 2026
@codecov

codecov Bot commented Oct 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 21 untouched benchmarks


Comparing claude/project-thread-kczaq1 (57c9f18) with main (6a906e9)

Open in CodSpeed

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Test-only PR covering gaps Stryker found in the downgrader; no source changes.

  • v3.1-to-v3.0.test.ts — lost-restriction coverage: { enum: [null, 'a'] } with no type stays as written; contentEncoding: 'binary' maps to format: 'binary'; an anyOf with a non-media-type-only contentMediaType branch keeps its anyOf; and new cases assert propertyNames with a pattern counts as lost while propertyNames: true does not, unevaluatedProperties beside an in-place applicator (allOf) counts as lost while beside additionalProperties it does not, and looseness propagates through allOf/anyOf/properties/not/oneOf.
  • v3.2-to-v3.1.test.ts — response $ref: a response reference stays as written alongside the summary→description handling.

Verified both files pass plus uncheck (oxlint/oxfmt/tsc). To confirm the new cases aren't theater, I mutated losesRestriction's propertyNames branch to return false — three tests failed as expected, then restored the source (tree clean). Assertions match the converter's documented contract.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

…st and examples gaps

expectValidDowngrade now checks the output holds only what JSON keeps, so a
key holding undefined fails. Adds a typed const, an empty examples list, and
a oneOf branch with an undefined-valued keyword.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EjMyAKtd1J94kr1PfQeQza

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Re-review of the delta since the previous pullfrog review (d9d6459); all new content is test-only, no source changes.

  • Added a typed-const case: { type: 'number', const: 5 } keeps its written type, yielding { type: 'number', enum: [5] } rather than falling back to the integer type valueType would infer.
  • Added { examples: [] } → exactly {}, asserted with toStrictEqual so a surviving examples: undefined key would fail.
  • Added an undefined-valued keyword case: oneOf: [{ type: 'object', if: undefined }, { type: 'string' }] keeps its oneOf, pinning the schema[key] !== undefined guard that stops if: undefined from marking the branch loose.
  • expectValidDowngrade now asserts output equals JSON.parse(JSON.stringify(output)), so the helper rejects any key holding undefined and any non-JSON value.

Verified each new assertion against shared.ts and v3.1-to-v3.0.ts; the full suite passes (265 tests, 5 files). The prior review left no open threads.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants