fix: address Copilot review round 14 — allOf under additionalProperties, own-property const match
- rootUnion: the added-names guard is now a shared `addsForbiddenNames` and applies to the `allOf` fold as well as to union branches. A member adding a name a restrictive root `additionalProperties` rejects would otherwise be merged beside the keyword, where the name reads as allowed. - selectBranchIndex tests `Object.hasOwn` before reading a pinned value: a field legally named `constructor` resolved to the inherited one and ruled out the very branch that pins it. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
df1e39e623
commit
5b6d68a9b6
1 changed files with 91 additions and 19 deletions
|
|
@ -147,6 +147,24 @@ describe("resolveRootUnion", () => {
|
|||
expect(base.properties).toBeUndefined();
|
||||
});
|
||||
|
||||
it("leaves an allOf that adds names a restrictive additionalProperties forbids", () => {
|
||||
// The root rejects `x` as an additional property; folding the member in
|
||||
// would move it beside the keyword, where it reads as allowed.
|
||||
const { base } = resolveRootUnion({
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
allOf: [
|
||||
{
|
||||
type: "object",
|
||||
properties: { x: { type: "string" } },
|
||||
required: ["x"],
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(base.allOf).toHaveLength(1);
|
||||
expect(base.properties).toBeUndefined();
|
||||
});
|
||||
|
||||
it("merges allOf branches unconditionally", () => {
|
||||
const { base, branches } = resolveRootUnion({
|
||||
type: "object",
|
||||
|
|
@ -754,6 +772,31 @@ describe("resolveRootUnion", () => {
|
|||
expect(selectBranchIndex(objectPinned, { tag: { a: 2 } })).toBe(1);
|
||||
});
|
||||
|
||||
it("does not read an inherited property as a supplied constant", () => {
|
||||
// `constructor` is a legal field name; reading the inherited one would
|
||||
// rule out the very branch that pins it.
|
||||
const pinnedOnInherited = resolveRootUnion({
|
||||
type: "object",
|
||||
anyOf: [
|
||||
{
|
||||
type: "object",
|
||||
properties: Object.fromEntries([
|
||||
["constructor", { const: "a" }],
|
||||
["x", { type: "string" }],
|
||||
]),
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: Object.fromEntries([
|
||||
["constructor", { const: "b" }],
|
||||
["y", { type: "string" }],
|
||||
]),
|
||||
},
|
||||
] as unknown[],
|
||||
}).branches;
|
||||
expect(selectBranchIndex(pinnedOnInherited, { x: "supplied" })).toBe(0);
|
||||
});
|
||||
|
||||
it("reports none when the values identify nothing", () => {
|
||||
expect(selectBranchIndex(branches, {})).toBeNull();
|
||||
expect(selectBranchIndex(branches, { kind: "other" })).toBeNull();
|
||||
|
|
|
|||
Loading…
Reference in a new issue