From 1d45c17c4b512720dc28757ab01bdf6503f479bd Mon Sep 17 00:00:00 2001 From: alexander-akait Date: Mon, 7 Sep 2026 21:22:23 +0000 Subject: [PATCH] test: cover collecting a subtree of errors and grouping a nested `anyOf` `codecov` reported the instance path index was only exercised by errors that share a path or have none in common, so the branch that walks into the children of a node and the comparator that puts the collected errors back in the order they were reported were never reached. An error arriving after the errors nested inside it covers both. The second test covers `groupChildrenByFirstChild` putting the errors of an inner `anyOf` under it, which is the path that `findAllChildren` was changed for. `src/validate.js` goes from 97.84% to 100% of lines, what is left are the guards for environments without `process` or `globalThis`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_013wqLmVHkAGBsWQgXknEQCK --- test/filter-errors.test.js | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/filter-errors.test.js b/test/filter-errors.test.js index e816099..f01c30c 100644 --- a/test/filter-errors.test.js +++ b/test/filter-errors.test.js @@ -143,6 +143,68 @@ describe("filter errors", () => { ); }); + // `groupChildrenByFirstChild` puts the errors of an inner `anyOf` under it + it("should group the children of a nested `anyOf`", () => { + const schema = { + type: "object", + properties: { + a: { + anyOf: [ + { anyOf: [{ type: "string" }, { type: "number" }] }, + { type: "boolean" }, + ], + }, + }, + }; + + const message = getMessage(schema, { a: {} }); + + expect(message).toContain("configuration.a should be one of these:"); + expect(message).toContain("string | number | boolean"); + // the inner `anyOf` and its own branches, not a flat list of every branch + expect(message).toContain( + "configuration.a should be one of these:\n string | number", + ); + }); + + // Enough errors to use the instance path index, with an error that arrives after the errors + // nested inside it and so collects a whole subtree at once + it("should nest a subtree of errors under a later error covering it", () => { + const length = 30; + const properties = {}; + + for (let i = 0; i < length; i++) { + properties[`p${i}`] = { type: "string" }; + } + + const schema = { + type: "object", + properties: { + a: { anyOf: [{ type: "object", properties }, { type: "string" }] }, + }, + }; + + const options = { a: {} }; + + for (let i = 0; i < length; i++) { + options.a[`p${i}`] = 1; + } + + const errors = getErrors(schema, options); + + expect(errors).toHaveLength(1); + expect(errors[0].keyword).toBe("anyOf"); + expect(errors[0].instancePath).toBe("/a"); + + const children = errors[0].children.map((error) => error.instancePath); + + // every nested error, in the order it was reported, plus the error for the second branch + expect(children).toStrictEqual([ + ...Array.from({ length }, (_, i) => `/a/p${i}`), + "/a", + ]); + }); + // `filterErrors` used to be quadratic in the amount of reported errors, so a large invalid // configuration was enough to lock up the process for minutes it("should filter a large amount of sibling errors in a reasonable time", () => {