fix: address Copilot review round 5 — union member faithfulness, partial discriminators, const-null siblings
- rootUnion: union members get the same flattenability test as allOf members,
so a branch whose nested `allOf: [false]` makes it unsatisfiable is no
longer offered as a callable alternative with that constraint erased.
- selectBranchIndex: weigh only the pinned constants the caller SUPPLIED. One
it did not supply is a value this identification exists to seed, so
requiring it meant a deep link naming `kind` alone matched no branch
whenever the branches also pinned, say, a `version`. A supplied constant
that disagrees is still evidence against.
- admitsNull: `const: null` is conjunctive with its siblings, not an
override — `{ type: "string", const: null }` and
`{ const: null, anyOf: [...] }` reject every value, so the claim is made
only where the type and a sibling union leave null on the table.
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
7e4af5ed34
commit
f556f8831b
2 changed files with 103 additions and 14 deletions
|
|
@ -731,4 +731,23 @@ describe("admitsNull", () => {
|
|||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
describe("a const-null schema (#2123)", () => {
|
||||
it("admits null when nothing else rules it out", () => {
|
||||
expect(admitsNull({ const: null })).toBe(true);
|
||||
expect(admitsNull({ type: "null", const: null })).toBe(true);
|
||||
expect(admitsNull({ type: ["string", "null"], const: null })).toBe(true);
|
||||
});
|
||||
|
||||
it("does not override a sibling that rejects null", () => {
|
||||
// `const` is conjunctive with its siblings, not an override: both of
|
||||
// these reject every value, so claiming nullability would let the
|
||||
// required-field gate accept a `null` the schema forbids.
|
||||
expect(admitsNull({ type: "string", const: null })).toBe(false);
|
||||
expect(admitsNull({ const: null, anyOf: [{ type: "string" }] })).toBe(
|
||||
false,
|
||||
);
|
||||
expect(admitsNull({ const: null, not: { type: "null" } })).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -397,6 +397,24 @@ describe("resolveRootUnion", () => {
|
|||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("declines a member carrying a constraint the merge cannot apply", () => {
|
||||
// Same faithfulness test the `allOf` fold applies — otherwise a branch
|
||||
// whose nested `allOf: [false]` makes it unsatisfiable is offered as a
|
||||
// callable alternative.
|
||||
const { branches } = resolveRootUnion({
|
||||
type: "object",
|
||||
anyOf: [
|
||||
EMAIL,
|
||||
{
|
||||
type: "object",
|
||||
properties: { x: { type: "string" } },
|
||||
allOf: [false as unknown],
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(branches).toEqual([]);
|
||||
});
|
||||
|
||||
it("declines an empty union", () => {
|
||||
expect(resolveRootUnion({ type: "object", anyOf: [] }).branches).toEqual(
|
||||
[],
|
||||
|
|
@ -450,6 +468,33 @@ describe("resolveRootUnion", () => {
|
|||
expect(selectBranchIndex(branches, { kind: "sms" })).toBe(1);
|
||||
});
|
||||
|
||||
it("identifies a branch from the constants that were supplied", () => {
|
||||
// Both branches pin `version` as well; a deep link naming only `kind`
|
||||
// must still find its branch — the unsupplied constant is one this
|
||||
// identification exists to seed.
|
||||
const versioned = resolveRootUnion({
|
||||
type: "object",
|
||||
anyOf: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
version: { const: "1" },
|
||||
kind: { const: "email" },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: { version: { const: "1" }, kind: { const: "sms" } },
|
||||
},
|
||||
],
|
||||
}).branches;
|
||||
expect(selectBranchIndex(versioned, { kind: "sms" })).toBe(1);
|
||||
// A supplied constant that disagrees is still evidence against.
|
||||
expect(selectBranchIndex(versioned, { version: "2", kind: "sms" })).toBe(
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
it("reports none when the values identify nothing", () => {
|
||||
expect(selectBranchIndex(branches, {})).toBeNull();
|
||||
expect(selectBranchIndex(branches, { kind: "other" })).toBeNull();
|
||||
|
|
|
|||
Loading…
Reference in a new issue