Repository navigation
test(downgrader): close test gaps found by mutation testing - #79
Conversation
Add table-style cases for the 3.0 keyword allowlist, the applicators that keep `unevaluatedProperties` from becoming `additionalProperties`, XML `nodeType` under every 3.2 subschema keyword, exclusive bounds at and past their plain bounds, ArkType's open-ended tuple getting `maxItems`, and the shared helpers: own `__proto__` keys, keys holding `undefined`, cycles through arrays, `~0`/`~1` in `$ref` pointers, and extensions in paths, responses, and callbacks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Y5xQyjQEqVH9Piyh7RN5U
…promises Bound tuples by maxItems in a table, drop `$dynamicRef` and `$vocabulary`, leave non-pointer and inherited-key `$ref`s as written, tolerate a non-string `$ref`, compare a dropped content `$ref`, a Link's `server`, and XML `nodeType: none` exactly, and read a null-prototype schema. Check that the oRPC document converts to 3.1 the same when built from null-prototype objects or with a key holding undefined in every object, and to 3.0 the same when built from null-prototype objects. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Y5xQyjQEqVH9Piyh7RN5U
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This PR only expands downgrader test coverage for mutation survivors and unusual object/input shapes; it does not modify production converter code or shipped behavior. The changed files run only in the test harness. Notes:
You can add or adjust custom eligibility rules. Learn more. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run reviewed the full test-only diff at 0165644 (3 files). I ran the three touched test files (133 passed) and traced each new expectation against src/v3.1-to-v3.0.ts and src/v3.2-to-v3.1.ts.
- 3.1→3.0 schema tables — "keeps every keyword 3.0 shares as it is"; exclusive bounds as an
it.each(adds the equal-bound rows); tuple bounds as a table (items: false→maxItems, ArkType{ prefixItems, minItems, items: false }→maxItems: 2);$dynamicRef/$vocabularydropped; pointer percent/~1/~0decode order; non-pointer and inherited-key$refs left as written;unevaluatedPropertiesbeside eachIN_PLACE_APPLICATORSentry stays out ofadditionalProperties; extensions copied as-is; a non-string$refbecomes{}. - 3.2→3.1 schema/keyword coverage —
xml.nodeTypeconverted inside every subschema/map/list keyword,nodeType: 'none'sets nowrapped, exact Linkserverand dropped content$ref, own__proto__keys kept ordinary,toStrictEqualforundefined-holding keys, cycles through arrays preserved. - oRPC input promises — the document converts identically when rebuilt from null-prototype objects and from objects carrying
missing: undefined(3.2→3.1), and from null-prototype objects for the 3.1→3.0 step.
Every expectation is consistent with the implementation. The keyword it.each tables mirror the source lists (SUBSCHEMA_KEYWORDS, IN_PLACE_APPLICATORS, SUBSCHEMA_MAP/LIST_KEYWORDS, the $ref-rewrite set), so coverage tracks the code rather than hard-coding arbitrary cases. The author's note that the undefined twin is not asserted for 3.1→3.0 is a pre-existing isMediaTypeOnly/propertyNames bug left for a separate fix PR — out of scope here.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
…propertyNames checks (#80) <!-- claude-projects-attribution: {"github_login":"dinwwwh"} --> _Requested by **dinwwwh** · [project thread](https://claude.ai/code/project/chan_01UcKMz91tMarPV1kWLVgLYE?thread=cmsg_01UcKMz91tMarPV1kWLVgLYENrRmND8vTmDypUMYJpJSLE)_ The README promises that a key holding `undefined` counts as missing, but two checks in the 3.1 to 3.0 converter counted such keys: - `isMediaTypeOnly` used `Object.keys(value).length === 1`, so a Zod-style `anyOf` branch like `{ contentMediaType: 'image/png', description: undefined }` was not recognized and became a binary string branch instead of being dropped. - `losesRestriction` for `propertyNames` treated `{ type: 'string', x: undefined }` as a lost restriction, so an enclosing `oneOf` (directly or through a `$ref`) became `anyOf`. Both checks now skip entries holding `undefined`. A unit test covers both cases. I also checked that the oRPC document rebuilt with an extra `missing: undefined` key in every object (the `withUndefined` twin from #79) converts to 3.0 exactly like the plain one: it fails on main and passes with this change. Once #79 lands, that twin can be extended to 3.0. --- _Generated by [Claude Code](https://claude.ai/code/session_0129me54jxy7ZKsYhwWvAmPP)_ Co-authored-by: Claude <noreply@anthropic.com>
<!-- claude-projects-attribution: {"github_login":"dinwwwh"} -->
_Requested by **dinwwwh** · [project
thread](https://claude.ai/code/project/chan_01UcKMz91tMarPV1kWLVgLYE?thread=cmsg_01UcKMz91tMarPV1kWLVgLYENrRmND8vTmDypUMYJpJSLE)_
Test-only follow-up to #79 and #80. #79 checked the `withUndefined` twin
of the oRPC document only for the 3.2 to 3.1 step, because 3.1 to 3.0
counted keys holding `undefined`. #80 fixed that, so the 3.0 twin test
now runs over both `nullPrototype` and `withUndefined`.
The new case fails on the commit before #80 and passes on main.
---
_Generated by [Claude
Code](https://claude.ai/code/session_0129me54jxy7ZKsYhwWvAmPP)_
Co-authored-by: Claude <noreply@anthropic.com>

Requested by dinwwwh · project thread
Test-only. Stryker on
packages/downgrader/srcleft 155 of 1,211 mutants alive onmain. With these tests, 81 are left, and no mutant survives that was killed before.What the new cases cover
3.1 to 3.0
readOnly,writeOnly,deprecated,discriminator,externalDocs,xml,multipleOf,min/maxProperties, and the rest) comes out unchanged, not renamed tox-.it.eachover every in-place applicator besideunevaluatedProperties: false: none of them turns it intoadditionalProperties: false.{ prefixItems: [a, b], minItems: 1, items: false }, which getsmaxItems: 2.$dynamicRefand$vocabularyare dropped.$refpointers decode percent-encoding,~1, and~0in that order, and non-pointer or inherited-key refs stay as written.3.2 to 3.1
it.eachover every subschema keyword, map keyword, and list keyword:xml.nodeTypeconverts inside each one.nodeType: noneon an array doesn't setwrapped.serveris compared exactly.Shared helpers
__proto__keys stay ordinary, writable, enumerable keys.undefinedare compared withtoStrictEqual, so a missing key and one holdingundefinedcount as different.Object.create(null)schema is read like any other object.README input promises (
orpc.test.ts)undefinedin every object. It also converts to 3.0 the same when built from null-prototype objects.undefinedtwin is not yet checked for the 3.1-to-3.0 step, because that step fails today.isMediaTypeOnlyand thepropertyNamescheck count keys that holdundefined. That bug is left for its own fix PR, which can add the check.Remaining survivors
Most of the 81 that are left are equivalent mutants (the
$reftarget cache,clone's pre-check,typeofguards that only matter for malformed input). A few are.map(() => undefined)mutants that throw at import, which Stryker records as surviving. The rest are in the looseness and type logic, which this PR doesn't touch.Generated by Claude Code