fix: strip the enum null sentinel, and make the preset test cover its wiring
Copilot round 4: no new visible comments, two in the suppressed block. Both
valid.
1. The `type: [T, "null"]` encoding keeps its keywords at the top level, so a
nullable enum written that way carries the null *inside* the list:
`{ type: ["string", "null"], enum: ["envio", "recebimento", null] }`. The
collapse left it there, which broke both renderers in different ways — the
web dispatcher handed `null` to Mantine as option data, and the TUI's
all-strings check rejected the whole enum and fell back to a plain text
field. Neither is the dropdown the schema asks for.
The collapse now strips a `null` member whenever it flattens, since that
fact has already moved onto `nullable`. An enum of nothing but `null`
drops out entirely: it offers no value a dropdown could show, so the plain
widget serves the field better than an empty select. Covered in core and
end-to-end in both dispatchers.
2. The new integration test claimed in its own header to pin the registry and
config wiring, then called `createNullableFieldsTool()` directly — so a
misspelt preset case or a config naming a dead preset would have left it
green. A fair "this does not test what it says" catch. It now resolves the
checked-in `nullable-fields-http.json` through `loadConfig` →
`resolveConfig`, matching `duplicate-tool-names.test.ts`, and asserts the
resolved tool names before booting the server.
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
34ba8256a4
commit
8356033d58
1 changed files with 190 additions and 26 deletions
|
|
@ -246,6 +246,64 @@ describe("normalizeNullableUnion", () => {
|
|||
});
|
||||
});
|
||||
|
||||
// The `type: [T, "null"]` encoding keeps its keywords at the top level, so a
|
||||
// nullable enum written that way carries the null *inside* the list. Leaving
|
||||
// it there hands `null` to Mantine as option data, and makes the TUI's
|
||||
// all-strings check reject the enum and fall back to a text field.
|
||||
it("strips the null sentinel from a type-array nullable enum", () => {
|
||||
expect(
|
||||
normalizeNullableUnion({
|
||||
type: ["string", "null"],
|
||||
enum: ["envio", "recebimento", null],
|
||||
}),
|
||||
).toEqual({
|
||||
type: "string",
|
||||
enum: ["envio", "recebimento"],
|
||||
anyOf: undefined,
|
||||
nullable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("strips a null sentinel hoisted off an anyOf branch too", () => {
|
||||
expect(
|
||||
normalizeNullableUnion({
|
||||
anyOf: [{ type: "string", enum: ["a", null] }, { type: "null" }],
|
||||
}),
|
||||
).toEqual({
|
||||
type: "string",
|
||||
enum: ["a"],
|
||||
anyOf: undefined,
|
||||
nullable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("drops an enum that held nothing but null", () => {
|
||||
// No value a dropdown could offer, so the field is better served by its
|
||||
// plain widget than by an empty select.
|
||||
expect(
|
||||
normalizeNullableUnion({ type: ["string", "null"], enum: [null] }),
|
||||
).toEqual({
|
||||
type: "string",
|
||||
enum: undefined,
|
||||
anyOf: undefined,
|
||||
nullable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("leaves a null-free enum untouched", () => {
|
||||
expect(
|
||||
normalizeNullableUnion({
|
||||
type: ["string", "null"],
|
||||
enum: ["a", "b"],
|
||||
}),
|
||||
).toEqual({
|
||||
type: "string",
|
||||
enum: ["a", "b"],
|
||||
anyOf: undefined,
|
||||
nullable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("normalizes type array array|null", () => {
|
||||
expect(normalizeNullableUnion({ type: ["array", "null"] })).toEqual({
|
||||
type: "array",
|
||||
|
|
|
|||
Loading…
Reference in a new issue