UNPKG

json-schema-library

Version:

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation

654 lines (538 loc) 26.8 kB
import { compileSchema } from "./compileSchema"; import { strict as assert } from "assert"; import { isSchemaNode, isJsonError } from "./types"; describe("compileSchema : getNodeChild", () => { describe("behaviour", () => { it("should return node of property", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string", minLength: 1 } } }).getNodeChild("title", { title: "abc" }); assert(isSchemaNode(node), "should have returned a valid schema node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1 }); }); it("should return node of property even it type differs", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string", minLength: 1 } } }).getNodeChild("title", { title: 123 }); assert(isSchemaNode(node), "should have returned a valid schema node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1 }); }); it("should return undefined if property is not defined, but allowed", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string", minLength: 1 } } }).getNodeChild("body", { body: "abc" }); assert.deepEqual(node, undefined); }); it("should return an error when the property is not allowed", () => { const { error } = compileSchema({ type: "object", properties: { title: { type: "string", minLength: 1 } }, additionalProperties: false }).getNodeChild("body", { title: "abc" }); assert(isJsonError(error), "should have return an error"); }); it("should return the original schema of the property", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string", allOf: [{ minLength: 1 }] } } }).getNodeChild("title", { body: "abc" }); assert.deepEqual(node?.schema, { type: "string", allOf: [{ minLength: 1 }] }); }); it("should reduce parent schema for returned property", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } }, allOf: [ { properties: { title: { minLength: 1 } } } ] }).getNodeChild("title", { body: "abc" }); assert.deepEqual(node?.schema, { type: "string", minLength: 1 }); }); }); describe("array - multiple contains", () => { it("should pick correct schema from allOf contains", () => { const { node } = compileSchema({ allOf: [{ contains: { multipleOf: 2 } }, { contains: { multipleOf: 3 } }] }).getNodeChild(0, [2, 5]); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { anyOf: [{ multipleOf: 2 }, { multipleOf: 3 }] }); }); }); describe("object - reduce parent schema when returning child-property", () => { it("should reduce parent if-then schema when returning property node", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } }, if: { properties: { title: { minLength: 2 } } }, then: { properties: { title: { maxLength: 3 } } }, else: { properties: { title: { minLength: 4 } } } }).getNodeChild("title", { title: "abcd" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", maxLength: 3 }); }); it("should reduce parent if-else schema when returning node", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } }, if: { properties: { title: { minLength: 2 } } }, then: { properties: { title: { maxLength: 3 } } }, else: { properties: { title: { minLength: 4 } } } }).getNodeChild("title", { title: "a" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 4 }); }); it("should reduce parent anyOf schema when returning property node", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } }, anyOf: [{ properties: { title: { minLength: 1 } } }, { properties: { title: { maxLength: 1 } } }] }).getNodeChild("title", { title: "abcd" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1 }); }); it("should reduce all matching parent anyOf schema when returning property node", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } }, anyOf: [{ properties: { title: { minLength: 1 } } }, { properties: { title: { maxLength: 1 } } }] }).getNodeChild("title", { title: "a" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1, maxLength: 1 }); }); it("should reduce all allOf schema when returning property node", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } }, allOf: [{ properties: { title: { minLength: 1 } } }, { properties: { title: { maxLength: 1 } } }] }).getNodeChild("title"); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1, maxLength: 1 }); }); it("should reduce matching oneOf schema when returning property node", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } }, oneOf: [{ properties: { title: { minLength: 1 } } }, { properties: { title: { maxLength: 1 } } }] }).getNodeChild("title", { title: "" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", maxLength: 1 }); }); it("should return matching oneOf schema with `additionalProperties=false`", () => { const { node } = compileSchema({ oneOf: [ { properties: { title: { type: "string" } }, additionalProperties: false }, { properties: { title: { type: "number" } }, additionalProperties: false } ] }).getNodeChild("title", { title: "" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string" }); }); it("should return error when multiple oneOf-items match", () => { const { error } = compileSchema({ type: "object", properties: { title: { type: "string" } }, oneOf: [{ properties: { title: { minLength: 1 } } }, { properties: { title: { maxLength: 1 } } }] }).getNodeChild("title", { title: "a" }); assert(isJsonError(error), "should have returned a valid schema property node"); }); it("should return error when multiple oneOf-items match", () => { const { error } = compileSchema({ type: "object", properties: { title: { type: "string" } }, oneOf: [{ properties: { title: { minLength: 1 } } }, { properties: { title: { maxLength: 1 } } }] }).getNodeChild("title", { title: "a" }); assert(isJsonError(error), "should have returned a valid schema property node"); }); }); describe("object - get child property from resolved dynamic schema", () => { it("should get property from dependentSchemas", () => { const { node } = compileSchema({ type: "object", dependentSchemas: { trigger: { properties: { title: { type: "string", minLength: 1 } } } } }).getNodeChild("title", { trigger: "trigger" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1 }); }); it("should get merged property from patternProperties", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string", minLength: 1 } }, patternProperties: { le: { maxLength: 3 } } }).getNodeChild("title"); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1, maxLength: 3 }); }); it("should get property from merged allOf schema", () => { const { node } = compileSchema({ type: "object", allOf: [{ properties: { title: { type: "string" } } }, { properties: { title: { minLength: 1 } } }] }).getNodeChild("title"); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 1 }); }); it("should return combined allOf schema", () => { const allOf = [ { properties: { secondary: { id: "secondary", type: "string" } } }, { properties: { tertiary: { id: "tertiary", type: "number" } } } ]; const { node: res } = compileSchema({ type: "object", properties: { dynamicSchema: { type: "object", properties: { trigger: { type: "boolean" } }, allOf } } }).getNodeChild("dynamicSchema"); const { node } = res.reduceNode({ trigger: true }); assert.deepEqual(node?.schema, { type: "object", properties: { trigger: { type: "boolean" }, secondary: { type: "string", id: "secondary" }, tertiary: { type: "number", id: "tertiary" } } }); }); it("should get property from matching anyOf schema", () => { const { node } = compileSchema({ type: "object", anyOf: [ { properties: { title: { type: "string", maxLength: 0 }, label: { type: "number", maximum: 0 } } }, { properties: { title: { type: "string", minLength: 1 }, label: { type: "number", minimum: 1 } } } ] }).getNodeChild("label", { title: "matches minLength" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "number", minimum: 1 }); }); it("should get property from matching oneOf schema", () => { const { node } = compileSchema({ type: "object", oneOf: [ { properties: { title: { type: "string" }, label: { type: "string", minLength: 2 } } }, { properties: { title: { type: "number" } } } ] }).getNodeChild("label", { title: "matches string" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", minLength: 2 }); }); it("should return `undefined` for valid oneOf property missing the property", () => { const { node } = compileSchema({ type: "object", oneOf: [ { properties: { title: { type: "string" }, label: { type: "string", minLength: 2 } } }, { properties: { title: { type: "number" } } } ] }).getNodeChild("label", { title: 123 }); assert(node === undefined); }); it("should get property from then schema", () => { const { node } = compileSchema({ type: "object", if: { properties: { title: { type: "string", minLength: 1 } } }, then: { properties: { label: { type: "string", maxLength: 2 } } }, else: { properties: { label: { type: "number", minimum: 1 } } } }).getNodeChild("label", { title: "matches minLength" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "string", maxLength: 2 }); }); it("should get property from else schema", () => { const { node } = compileSchema({ type: "object", if: { properties: { title: { type: "string", minLength: 1 } } }, then: { properties: { label: { type: "string", maxLength: 2 } } }, else: { properties: { label: { type: "number", minimum: 1 } } } }).getNodeChild("label", { title: "" }); assert(isSchemaNode(node), "should have returned a valid schema property node"); assert.deepEqual(node?.schema, { type: "number", minimum: 1 }); }); }); describe("ref", () => { it("should resolve references in allOf schema", () => { const node = compileSchema({ definitions: { object: { type: "object" }, additionalNumber: { additionalProperties: { type: "number", minLength: 2 } } }, allOf: [{ $ref: "#/definitions/object" }, { $ref: "#/definitions/additionalNumber" }] }); const schema = node.getNodeChild("title", { title: 4, test: 2 })?.node?.schema; assert.deepEqual(schema, { type: "number", minLength: 2 }); }); }); }); describe("step", () => { it("should return undefined for unknown types", () => { const { node, error } = compileSchema({ type: "unknown" }).getNodeChild(0, {}); assert.equal(node, undefined); assert.equal(error, undefined); }); describe("object", () => { it("should return object property", () => { const { node } = compileSchema({ type: "object", properties: { title: { type: "string" } } }).getNodeChild("title"); assert.deepEqual(node?.schema, { type: "string" }); }); it("should return error undefined for undefined schema", () => { const { node, error } = compileSchema({ type: "object", properties: { title: { type: "string" } } }).getNodeChild("wrongkey"); assert.equal(node, undefined); assert.equal(error, undefined); }); it("should return undefined for unresolved then-schema (unknown schema)", () => { const { node, error } = compileSchema({ type: "object", properties: { test: { type: "string" } }, if: { required: ["test"], properties: { test: { minLength: 10 } } }, then: { required: ["thenValue"], properties: { thenValue: { description: "then", type: "string", default: "from then" } } } }).getNodeChild("thenValue"); assert.equal(node, undefined); assert.equal(error, undefined); }); it("should return undefined for schema matching `additionalProperties=true`", () => { const { node, error } = compileSchema({ type: "object", additionalProperties: true }).getNodeChild("any", { any: "i am valid" }); assert.equal(node, undefined); assert.equal(error, undefined); }); it("should treat `additionalProperties` as `true` per default", () => { const { node, error } = compileSchema({ type: "object" }).getNodeChild("any", { any: "i am valid" }); assert.equal(node, undefined); assert.equal(error, undefined); }); it("should return an error if `additionalProperties=false` and property unknown", () => { const { error } = compileSchema({ type: "object", additionalProperties: false }).getNodeChild("any", { any: "i am valid" }); assert(isJsonError(error)); }); it("should return matching oneOf", () => { const { node } = compileSchema({ oneOf: [ { type: "object", properties: { title: { type: "string" } } }, { type: "object", properties: { title: { type: "number" } } } ] }).getNodeChild("title", { title: 4 }); assert.deepEqual(node?.schema, { type: "number" }); }); it("should return matching oneOf, for objects missing properties", () => { const { node } = compileSchema({ oneOf: [ { type: "object", additionalProperties: { type: "string" } }, { type: "object", additionalProperties: { type: "number" } } ] }).getNodeChild("title", { title: 4, test: 2 }); assert.deepEqual(node?.schema, { type: "number" }); }); it("should return matching anyOf", () => { const { node } = compileSchema({ anyOf: [ { type: "object", additionalProperties: { type: "string" } }, { type: "object", additionalProperties: { type: "number" } } ] }).getNodeChild("title", { title: 4, test: 2 }); assert.deepEqual(node?.schema, { type: "number" }); }); it("should return combined anyOf schema", () => { const { node } = compileSchema({ anyOf: [ { type: "object", additionalProperties: { type: "string" } }, { type: "object", additionalProperties: { type: "number" } }, { type: "object", additionalProperties: { minimum: 2 } } ] }).getNodeChild("title", { title: 4, test: 2 }); assert.deepEqual(node?.schema, { type: "number", minimum: 2 }); }); it("should resolve references from anyOf schema", () => { const { node } = compileSchema({ definitions: { string: { type: "object", additionalProperties: { type: "string" } }, number: { type: "object", additionalProperties: { type: "number" } }, min: { type: "object", additionalProperties: { minimum: 2 } } }, anyOf: [ { $ref: "#/definitions/string" }, { $ref: "#/definitions/number" }, { $ref: "#/definitions/min" } ] }).getNodeChild("title", { title: 4, test: 2 }); assert.deepEqual(node?.schema, { type: "number", minimum: 2 }); }); it("should return matching allOf schema", () => { const { node } = compileSchema({ allOf: [{ type: "object" }, { additionalProperties: { type: "number" } }] }).getNodeChild("title", { title: 4, test: 2 }); assert.deepEqual(node?.schema, { type: "number" }); }); it("should resolve references in allOf schema", () => { const { node } = compileSchema({ definitions: { object: { type: "object" }, additionalNumber: { additionalProperties: { type: "number" } } }, allOf: [{ $ref: "#/definitions/object" }, { $ref: "#/definitions/additionalNumber" }] }).getNodeChild("title", { title: 4, test: 2 }); assert.deepEqual(node?.schema, { type: "number" }); }); it("should return matching patternProperty", () => { const { node } = compileSchema({ type: "object", patternProperties: { "^first$": { type: "number", id: "first" }, "^second$": { type: "string", id: "second" } } }).getNodeChild("second"); assert.deepEqual(node?.schema, { type: "string", id: "second" }); }); it("should return additionalProperties schema for not matching patternProperty", () => { const { node } = compileSchema({ type: "object", patternProperties: { "^first$": { type: "number", id: "first" }, "^second$": { type: "string", id: "second" } }, additionalProperties: { type: "object" } }).getNodeChild("third"); assert.deepEqual(node?.schema, { type: "object" }); }); }); describe("array", () => { it("should return undefined for unknown item schema", () => { const { node, error } = compileSchema({ type: "array" }).getNodeChild(0, []); assert.equal(node, undefined); assert.equal(error, undefined); }); it("should return item property", () => { const { node } = compileSchema({ type: "array", items: { type: "string" } }).getNodeChild(0); assert.deepEqual(node?.schema, { type: "string" }); }); it("should return item at index", () => { const { node } = compileSchema({ type: "array", prefixItems: [{ type: "string" }, { type: "number" }, { type: "boolean" }] }).getNodeChild(1, ["3", 2]); assert.deepEqual(node?.schema, { type: "number" }); }); it("should return matching item in oneOf", () => { const { node: res } = compileSchema({ type: "array", items: { oneOf: [ { type: "object", properties: { title: { type: "string" } } }, { type: "object", properties: { title: { type: "number" } } } ] } }).getNodeChild(0, [{ title: 2 }]); const { node } = res.reduceNode({ title: 2 }); assert.deepEqual(node?.schema, { type: "object", properties: { title: { type: "number" } } }); }); it("should return matching anyOf", () => { const { node: res } = compileSchema({ items: { anyOf: [ { type: "object", properties: { title: { type: "string" } } }, { type: "object", properties: { title: { type: "number" } } } ] } }).getNodeChild(1, [{ title: "two" }, { title: 4 }]); assert(res, "should have returned node"); const { node } = res.reduceNode({ title: 4 }); assert.deepEqual(node?.schema, { type: "object", properties: { title: { type: "number" } } }); }); it("should return combined anyOf schema", () => { const { node: res } = compileSchema({ items: { anyOf: [ { type: "object", properties: { title: { type: "string" } } }, { type: "object", properties: { title: { type: "number" } } }, { type: "object", properties: { title: { minimum: 2 } } } ] } }).getNodeChild(1, [{ title: "two" }, { title: 4 }]); assert(res, "should have returned node"); const { node } = res.reduceNode({ title: 4 }); assert.deepEqual(node?.schema, { type: "object", properties: { title: { type: "number", minimum: 2 } } }); }); it("should return combined allOf schema", () => { const { node: res } = compileSchema({ items: { allOf: [ { type: "object", properties: { title: { type: "number" } } }, { type: "object", properties: { title: { minimum: 3 } } } ] } }).getNodeChild(1, [{ title: "two" }, { title: 4 }]); assert(res, "should have returned node"); const { node } = res.reduceNode({ title: "two" }); assert.deepEqual(node?.schema, { type: "object", properties: { title: { type: "number", minimum: 3 } } }); }); it("should return a generated schema from `items:true`", () => { const { node: res } = compileSchema({ type: "array", items: true }).getNodeChild(1, ["3", 2], { createSchema: true }); assert(isSchemaNode(res)); assert.deepEqual(res.schema.type, "number"); }); }); });