Files
chesco/node_modules/yaml-language-server/out/server/test/schema2019Validation.test.js
T

3630 lines
169 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/*---------------------------------------------------------------------------------------------
* Copyright (c) IBM Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const testHelper_1 = require("./utils/testHelper");
const serviceSetup_1 = require("./utils/serviceSetup");
const chai_1 = require("chai");
const yamlSettings_1 = require("../src/yamlSettings");
const schemaUrls_1 = require("../src/languageservice/utils/schemaUrls");
describe('Validation Tests', () => {
let languageSettingsSetup;
let validationHandler;
let yamlSettings;
let schemaProvider;
const toContent = (data) => JSON.stringify(data, null, 2);
before(() => {
languageSettingsSetup = new serviceSetup_1.ServiceSetup()
.withValidate()
.withCompletion()
.withCustomTags(['!Test', '!Ref sequence'])
.withSchemaFileMatch({ uri: schemaUrls_1.KUBERNETES_SCHEMA_URL, fileMatch: ['.drone.yml'] })
.withSchemaFileMatch({ uri: 'https://json.schemastore.org/drone', fileMatch: ['.drone.yml'] })
.withSchemaFileMatch({ uri: schemaUrls_1.KUBERNETES_SCHEMA_URL, fileMatch: ['test.yml'] })
.withSchemaFileMatch({
uri: 'https://raw.githubusercontent.com/composer/composer/master/res/composer-schema.json',
fileMatch: ['test.yml'],
});
const { validationHandler: valHandler, yamlSettings: settings, schemaProvider: testSchemaProvider, } = (0, testHelper_1.setupLanguageService)(languageSettingsSetup.languageSettings);
validationHandler = valHandler;
yamlSettings = settings;
schemaProvider = testSchemaProvider;
});
function parseSetup(content, customSchemaID) {
const testTextDocument = (0, testHelper_1.setupSchemaIDTextDocument)(content, customSchemaID);
yamlSettings.documents = new yamlSettings_1.TextDocumentTestManager();
yamlSettings.documents.set(testTextDocument);
return validationHandler.validateTextDocument(testTextDocument);
}
afterEach(() => {
schemaProvider.deleteSchema(testHelper_1.SCHEMA_ID);
});
describe('$ref resolution', () => {
it('root pointer ref', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
foo: { $ref: '#' },
},
additionalProperties: false,
});
// match
let content = toContent({ foo: false });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// recursive match
content = toContent({ foo: { foo: false } });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// mismatch
content = toContent({ bar: false });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('not allowed');
// recursive mismatch
content = toContent({ foo: { bar: false } });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('not allowed');
});
it('relative pointer ref to object', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
foo: { type: 'integer' },
bar: { $ref: '#/properties/foo' },
},
});
// match
let content = toContent({ bar: 3 });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// mismatch
content = toContent({ bar: true });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
});
it('relative pointer ref to array', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ type: 'integer' }, { $ref: '#/items/0' }],
});
// match array
let content = toContent([1, 2]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// mismatch array
content = toContent([1, 'foo']);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
});
it('escaped pointer ref', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
'tilde~field': { type: 'integer' },
'slash/field': { type: 'integer' },
'percent%field': { type: 'integer' },
},
properties: {
tilde: { $ref: '#/$defs/tilde~0field' },
slash: { $ref: '#/$defs/slash~1field' },
percent: { $ref: '#/$defs/percent%25field' },
},
});
// slash invalid
let content = toContent({ slash: 'aoeu' });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
// tilde invalid
content = toContent({ tilde: 'aoeu' });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
// percent invalid
content = toContent({ percent: 'aoeu' });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
// slash valid
content = toContent({ slash: 123 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// tilde valid
content = toContent({ tilde: 123 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// percent valid
content = toContent({ percent: 123 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('nested refs', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
a: { type: 'integer' },
b: { $ref: '#/$defs/a' },
c: { $ref: '#/$defs/b' },
},
$ref: '#/$defs/c',
});
// nested ref valid
let content = toContent(5);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// nested ref invalid
content = toContent('a');
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
});
it('ref applies alongside sibling keywords', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
reffed: {
type: 'array',
},
},
properties: {
foo: {
$ref: '#/$defs/reffed',
maxItems: 2,
},
},
});
// ref valid, maxItems valid
let content = toContent({ foo: [] });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// ref valid, maxItems invalid
content = toContent({ foo: [1, 2, 3] });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items');
// ref invalid
content = toContent({ foo: 'string' });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
});
it('property named $ref that is not a reference', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
$ref: { type: 'string' },
},
});
// property named $ref valid
let content = toContent({ $ref: 'a' });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// property named $ref invalid
content = toContent({ $ref: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
});
it('property named $ref, containing an actual $ref', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
$ref: { $ref: '#/$defs/is-string' },
},
$defs: {
'is-string': {
type: 'string',
},
},
});
// property named $ref valid
let content = toContent({ $ref: 'a' });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// property named $ref invalid
content = toContent({ $ref: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
});
it('$ref to boolean schema true', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$ref: '#/$defs/bool',
$defs: {
bool: true,
},
});
// any value is valid
const content = toContent('foo');
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('$ref to boolean schema false', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$ref: '#/$defs/bool',
$defs: {
bool: false,
},
});
// any value is invalid
const content = toContent('foo');
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches a schema that is not allowed');
});
});
describe('$anchor resolution', () => {
it('Location-independent identifier', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$ref: '#foo',
$defs: {
A: {
$anchor: 'foo',
type: 'integer',
},
},
});
// match
let content = toContent(1);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// mismatch
content = toContent('a');
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
(0, chai_1.expect)(result[0].message).to.include('integer');
});
it('Location-independent identifier with absolute URI', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$ref: 'http://localhost:1234/draft2019-09/bar#foo',
$defs: {
A: {
$id: 'http://localhost:1234/draft2019-09/bar',
$anchor: 'foo',
type: 'integer',
},
},
});
// match
let content = toContent(1);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// mismatch
content = toContent('a');
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
(0, chai_1.expect)(result[0].message).to.include('integer');
});
it('Location-independent identifier with base URI change in subschema', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:1234/draft2019-09/root',
$ref: 'http://localhost:1234/draft2019-09/nested.json#foo',
$defs: {
A: {
$id: 'nested.json',
$defs: {
B: {
$anchor: 'foo',
type: 'integer',
},
},
},
},
});
// match
let content = toContent(1);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// mismatch
content = toContent('a');
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
(0, chai_1.expect)(result[0].message).to.include('integer');
});
it('same $anchor with different base uri', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:1234/draft2019-09/foobar',
$defs: {
A: {
$id: 'child1',
allOf: [
{
$id: 'child2',
$anchor: 'my_anchor',
type: 'number',
},
{
$anchor: 'my_anchor',
type: 'string',
},
],
},
},
$ref: 'child1#my_anchor',
});
// $ref resolves to /$defs/A/allOf/1
let content = toContent('a');
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// $ref does not resolve to /$defs/A/allOf/0
content = toContent(1);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
(0, chai_1.expect)(result[0].message).to.include('string');
});
it('resolves $ref "#name" via $anchor in same document', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
Name: {
$anchor: 'name',
type: 'string',
minLength: 2,
},
},
$ref: '#name',
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `A`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('String is shorter than the minimum length of 2.');
});
it('resolves external $ref to a root $anchor', async () => {
const rootSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$anchor: 'rootThing',
type: 'object',
properties: {
x: {
type: 'number',
},
},
required: ['x'],
};
const useRootSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$ref: 'file:///root.schema.json#rootThing',
};
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'file:///root.schema.json', rootSchema);
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'file:///use-root.schema.json', useRootSchema);
const content = `# yaml-language-server: $schema=file:///use-root.schema.json\n{}`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
});
it('resolves external $ref to $anchor in another schema', async () => {
const typesSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
Port: {
$anchor: 'port',
type: 'integer',
minimum: 1,
maximum: 65535,
},
},
};
const serverSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
port: { $ref: 'file:///types.schema.json#port' },
},
};
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'file:///types.schema.json', typesSchema);
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'file:///server.schema.json', serverSchema);
const content = `# yaml-language-server: $schema=file:///server.schema.json\nport: 70000`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Value is above the maximum of 65535.');
});
});
describe('keyword: unevaluatedProperties', () => {
it('unevaluatedProperties as schema validates remaining property values', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
known: { type: 'string' },
},
unevaluatedProperties: { type: 'number' },
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `known: ok\nextra: hi`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('number');
});
it('unevaluatedProperties=false sees evaluated props across allOf', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [
{ type: 'object', properties: { a: { type: 'string' } } },
{ type: 'object', properties: { b: { type: 'number' } } },
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `a: ok\nb: 1\nc: 2`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property c is not allowed');
});
it('unevaluatedProperties sees properties defined across $ref', async () => {
const baseSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
a: { type: 'string' },
},
};
const strictSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [{ $ref: 'file:///base-uneval.schema.json' }],
unevaluatedProperties: false,
};
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'file:///base-uneval.schema.json', baseSchema);
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'file:///strict-uneval.schema.json', strictSchema);
const content = `# yaml-language-server: $schema=file:///strict-uneval.schema.json\na: ok\nc: nope`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property c is not allowed');
});
it('unevaluatedProperties true', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
unevaluatedProperties: true,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({}))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
});
it('unevaluatedProperties schema', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
unevaluatedProperties: {
type: 'string',
minLength: 3,
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({}))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'fo' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('String is shorter than the minimum length of 3.');
});
it('unevaluatedProperties false', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({}))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it('unevaluatedProperties with adjacent properties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('unevaluatedProperties with adjacent patternProperties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
patternProperties: {
'^foo': { type: 'string' },
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('unevaluatedProperties with adjacent additionalProperties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
additionalProperties: true,
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
});
it('unevaluatedProperties with nested properties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
allOf: [
{
properties: {
bar: { type: 'string' },
},
},
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
});
it('unevaluatedProperties with nested patternProperties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
allOf: [
{
patternProperties: {
'^bar': { type: 'string' },
},
},
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
});
it('unevaluatedProperties with nested additionalProperties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
allOf: [
{
additionalProperties: true,
},
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
});
it('unevaluatedProperties with nested unevaluatedProperties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
allOf: [
{
unevaluatedProperties: true,
},
],
unevaluatedProperties: {
type: 'string',
maxLength: 2,
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
});
it('unevaluatedProperties with anyOf', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
anyOf: [
{
properties: {
bar: { const: 'bar' },
},
required: ['bar'],
},
{
properties: {
baz: { const: 'baz' },
},
required: ['baz'],
},
{
properties: {
quux: { const: 'quux' },
},
required: ['quux'],
},
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
let result = await parseSetup(toContent({ foo: 'foo', bar: 'bar', baz: 'not-baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar', baz: 'baz' }))).to.be.empty;
result = await parseSetup(toContent({ foo: 'foo', bar: 'bar', baz: 'baz', quux: 'not-quux' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property quux is not allowed.');
});
it('unevaluatedProperties with oneOf', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
oneOf: [
{
properties: {
bar: { const: 'bar' },
},
required: ['bar'],
},
{
properties: {
baz: { const: 'baz' },
},
required: ['baz'],
},
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar', quux: 'quux' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property quux is not allowed.');
});
it('unevaluatedProperties with not', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
not: {
not: {
properties: {
bar: { const: 'bar' },
},
required: ['bar'],
},
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('unevaluatedProperties with if/then/else', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
if: {
properties: {
foo: { const: 'then' },
},
required: ['foo'],
},
then: {
properties: {
bar: { type: 'string' },
},
required: ['bar'],
},
else: {
properties: {
baz: { type: 'string' },
},
required: ['baz'],
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'then', bar: 'bar' }))).to.be.empty;
let result = await parseSetup(toContent({ foo: 'then', bar: 'bar', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
(0, chai_1.expect)(await parseSetup(toContent({ baz: 'baz' }))).to.be.empty;
result = await parseSetup(toContent({ foo: 'else', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it('unevaluatedProperties with if/then/else, then not defined', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
if: {
properties: {
foo: { const: 'then' },
},
required: ['foo'],
},
else: {
properties: {
baz: { type: 'string' },
},
required: ['baz'],
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'then' }))).to.be.empty;
let result = await parseSetup(toContent({ foo: 'then', bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
(0, chai_1.expect)(await parseSetup(toContent({ baz: 'baz' }))).to.be.empty;
result = await parseSetup(toContent({ foo: 'else', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it('unevaluatedProperties with if/then/else, else not defined', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
if: {
properties: {
foo: { const: 'then' },
},
required: ['foo'],
},
then: {
properties: {
bar: { type: 'string' },
},
required: ['bar'],
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'then', bar: 'bar' }))).to.be.empty;
let result = await parseSetup(toContent({ foo: 'then', bar: 'bar', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
result = await parseSetup(toContent({ baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
result = await parseSetup(toContent({ foo: 'else', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(2);
const messages = result.map((entry) => entry.message).join(' | ');
(0, chai_1.expect)(messages).to.include('Property foo is not allowed.');
(0, chai_1.expect)(messages).to.include('Property baz is not allowed.');
});
it('unevaluatedProperties with dependentSchemas', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
dependentSchemas: {
foo: {
properties: {
bar: { const: 'bar' },
},
required: ['bar'],
},
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
const result = await parseSetup(toContent({ bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('unevaluatedProperties with boolean schemas', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
allOf: [true],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
const result = await parseSetup(toContent({ bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('unevaluatedProperties with $ref', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
$ref: '#/$defs/bar',
properties: {
foo: { type: 'string' },
},
unevaluatedProperties: false,
$defs: {
bar: {
properties: {
bar: { type: 'string' },
},
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
});
it('unevaluatedProperties before $ref', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
unevaluatedProperties: false,
properties: {
foo: { type: 'string' },
},
$ref: '#/$defs/bar',
$defs: {
bar: {
properties: {
bar: { type: 'string' },
},
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar', baz: 'baz' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property baz is not allowed.');
});
it('unevaluatedProperties with $recursiveRef', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'https://example.com/unevaluated-properties-with-recursive-ref/extended-tree',
$recursiveAnchor: true,
$ref: './tree',
properties: {
name: { type: 'string' },
},
$defs: {
tree: {
$id: './tree',
$recursiveAnchor: true,
type: 'object',
properties: {
node: true,
branches: {
$comment: "unevaluatedProperties comes first so it's more likely to bugs errors with implementations that are sensitive to keyword ordering",
unevaluatedProperties: false,
$recursiveRef: '#',
},
},
required: ['node'],
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({
name: 'a',
node: 1,
branches: {
name: 'b',
node: 2,
},
}))).to.be.empty;
const result = await parseSetup(toContent({
name: 'a',
node: 1,
branches: {
foo: 'b',
node: 2,
},
}));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it("unevaluatedProperties can't see inside cousins", async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [
{
properties: {
foo: true,
},
},
{
unevaluatedProperties: false,
},
],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result = await parseSetup(toContent({ foo: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it("unevaluatedProperties can't see inside cousins (reverse order)", async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [
{
unevaluatedProperties: false,
},
{
properties: {
foo: true,
},
},
],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result = await parseSetup(toContent({ foo: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it('nested unevaluatedProperties, outer false, inner true, properties outside', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
allOf: [
{
unevaluatedProperties: true,
},
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
});
it('nested unevaluatedProperties, outer false, inner true, properties inside', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
allOf: [
{
properties: {
foo: { type: 'string' },
},
unevaluatedProperties: true,
},
],
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo', bar: 'bar' }))).to.be.empty;
});
it('nested unevaluatedProperties, outer true, inner false, properties outside', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: { type: 'string' },
},
allOf: [
{
unevaluatedProperties: false,
},
],
unevaluatedProperties: true,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result1 = await parseSetup(toContent({ foo: 'foo' }));
(0, chai_1.expect)(result1).to.have.length(1);
(0, chai_1.expect)(result1[0].message).to.include('Property foo is not allowed.');
const result2 = await parseSetup(toContent({ foo: 'foo', bar: 'bar' }));
(0, chai_1.expect)(result2).to.have.length(2);
const messages = result2.map((entry) => entry.message).join(' | ');
(0, chai_1.expect)(messages).to.include('Property foo is not allowed.');
(0, chai_1.expect)(messages).to.include('Property bar is not allowed.');
});
it('nested unevaluatedProperties, outer true, inner false, properties inside', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
allOf: [
{
properties: {
foo: { type: 'string' },
},
unevaluatedProperties: false,
},
],
unevaluatedProperties: true,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('cousin unevaluatedProperties, true and false, true with properties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
allOf: [
{
properties: {
foo: { type: 'string' },
},
unevaluatedProperties: true,
},
{
unevaluatedProperties: false,
},
],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result1 = await parseSetup(toContent({ foo: 'foo' }));
(0, chai_1.expect)(result1).to.have.length(1);
(0, chai_1.expect)(result1[0].message).to.include('Property foo is not allowed.');
const result2 = await parseSetup(toContent({ foo: 'foo', bar: 'bar' }));
(0, chai_1.expect)(result2).to.have.length(2);
const messages = result2.map((entry) => entry.message).join(' | ');
(0, chai_1.expect)(messages).to.include('Property foo is not allowed.');
(0, chai_1.expect)(messages).to.include('Property bar is not allowed.');
});
it('cousin unevaluatedProperties, true and false, false with properties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
allOf: [
{
unevaluatedProperties: true,
},
{
properties: {
foo: { type: 'string' },
},
unevaluatedProperties: false,
},
],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'foo' }))).to.be.empty;
const result = await parseSetup(toContent({ foo: 'foo', bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('property is evaluated in an uncle schema to unevaluatedProperties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
foo: {
type: 'object',
properties: {
bar: {
type: 'string',
},
},
unevaluatedProperties: false,
},
},
anyOf: [
{
properties: {
foo: {
properties: {
faz: {
type: 'string',
},
},
},
},
},
],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: { bar: 'test' } }))).to.be.empty;
const result = await parseSetup(toContent({ foo: { bar: 'test', faz: 'test' } }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property faz is not allowed.');
});
describe('in-place applicator siblings, allOf has unevaluated', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
allOf: [
{
properties: {
foo: true,
},
unevaluatedProperties: false,
},
],
anyOf: [
{
properties: {
bar: true,
},
},
],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('base case: both properties present', async () => {
const result = await parseSetup(toContent({ foo: 1, bar: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('in place applicator siblings, bar is missing', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ foo: 1 }))).to.be.empty;
});
it('in place applicator siblings, foo is missing', async () => {
const result = await parseSetup(toContent({ bar: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
});
describe('in-place applicator siblings, anyOf has unevaluated', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
allOf: [
{
properties: {
foo: true,
},
},
],
anyOf: [
{
properties: {
bar: true,
},
unevaluatedProperties: false,
},
],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('base case: both properties present', async () => {
const result = await parseSetup(toContent({ foo: 1, bar: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it('in place applicator siblings, bar is missing', async () => {
const result = await parseSetup(toContent({ foo: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it('in place applicator siblings, foo is missing', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ bar: 1 }))).to.be.empty;
});
});
describe('unevaluatedProperties + single cyclic ref', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
x: { $ref: '#' },
},
unevaluatedProperties: false,
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('Empty is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({}))).to.be.empty;
});
it('Single is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ x: {} }))).to.be.empty;
});
it('Unevaluated on 1st level is invalid', async () => {
const result = await parseSetup(toContent({ x: {}, y: {} }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property y is not allowed.');
});
it('Nested is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ x: { x: {} } }))).to.be.empty;
});
it('Unevaluated on 2nd level is invalid', async () => {
const result = await parseSetup(toContent({ x: { x: {}, y: {} } }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property y is not allowed.');
});
it('Deep nested is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ x: { x: { x: {} } } }))).to.be.empty;
});
it('Unevaluated on 3rd level is invalid', async () => {
const result = await parseSetup(toContent({ x: { x: { x: {}, y: {} } } }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property y is not allowed.');
});
});
describe('unevaluatedProperties + ref inside allOf / oneOf', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
one: {
properties: { a: true },
},
two: {
required: ['x'],
properties: { x: true },
},
},
allOf: [
{ $ref: '#/$defs/one' },
{ properties: { b: true } },
{
oneOf: [
{ $ref: '#/$defs/two' },
{
required: ['y'],
properties: { y: true },
},
],
},
],
unevaluatedProperties: false,
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('Empty is invalid (no x or y)', async () => {
const result = await parseSetup(toContent({}));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
(0, chai_1.expect)(result[0].message).to.include('x');
});
it('a and b are invalid (no x or y)', async () => {
const result = await parseSetup(toContent({ a: 1, b: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
(0, chai_1.expect)(result[0].message).to.include('x');
});
it('x and y are invalid', async () => {
const result = await parseSetup(toContent({ x: 1, y: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('a and x are valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ a: 1, x: 1 }))).to.be.empty;
});
it('a and y are valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ a: 1, y: 1 }))).to.be.empty;
});
it('a and b and x are valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ a: 1, b: 1, x: 1 }))).to.be.empty;
});
it('a and b and y are valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ a: 1, b: 1, y: 1 }))).to.be.empty;
});
it('a and b and x and y are invalid', async () => {
const result = await parseSetup(toContent({ a: 1, b: 1, x: 1, y: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
});
describe('dynamic evalation inside nested refs', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
one: {
oneOf: [
{ $ref: '#/$defs/two' },
{ required: ['b'], properties: { b: true } },
{ required: ['xx'], patternProperties: { x: true } },
{ required: ['all'], unevaluatedProperties: true },
],
},
two: {
oneOf: [
{ required: ['c'], properties: { c: true } },
{ required: ['d'], properties: { d: true } },
],
},
},
oneOf: [{ $ref: '#/$defs/one' }, { required: ['a'], properties: { a: true } }],
unevaluatedProperties: false,
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('Empty is invalid', async () => {
const result = await parseSetup(toContent({}));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
});
it('a is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ a: 1 }))).to.be.empty;
});
it('b is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ b: 1 }))).to.be.empty;
});
it('c is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ c: 1 }))).to.be.empty;
});
it('d is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ d: 1 }))).to.be.empty;
});
it('a + b is invalid', async () => {
const result = await parseSetup(toContent({ a: 1, b: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('a + c is invalid', async () => {
const result = await parseSetup(toContent({ a: 1, c: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('a + d is invalid', async () => {
const result = await parseSetup(toContent({ a: 1, d: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('b + c is invalid', async () => {
const result = await parseSetup(toContent({ b: 1, c: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('b + d is invalid', async () => {
const result = await parseSetup(toContent({ b: 1, d: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('c + d is invalid', async () => {
const result = await parseSetup(toContent({ c: 1, d: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('xx is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ xx: 1 }))).to.be.empty;
});
it('xx + foox is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ xx: 1, foox: 1 }))).to.be.empty;
});
it('xx + foo is invalid', async () => {
const result = await parseSetup(toContent({ xx: 1, foo: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property foo is not allowed.');
});
it('xx + a is invalid', async () => {
const result = await parseSetup(toContent({ xx: 1, a: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('xx + b is invalid', async () => {
const result = await parseSetup(toContent({ xx: 1, b: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('xx + c is invalid', async () => {
const result = await parseSetup(toContent({ xx: 1, c: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('xx + d is invalid', async () => {
const result = await parseSetup(toContent({ xx: 1, d: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
it('all is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ all: 1 }))).to.be.empty;
});
it('all + foo is valid', async () => {
(0, chai_1.expect)(await parseSetup(toContent({ all: 1, foo: 1 }))).to.be.empty;
});
it('all + a is invalid', async () => {
const result = await parseSetup(toContent({ all: 1, a: 1 }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches multiple schemas when only one must validate.');
});
});
it('non-object instances are valid', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent(true))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent(123))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent(1.0))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent([]))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent('foo'))).to.be.empty;
(0, chai_1.expect)(await parseSetup(toContent(null))).to.be.empty;
});
it('unevaluatedProperties with null valued instance properties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedProperties: {
type: 'null',
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: null }))).to.be.empty;
});
it('unevaluatedProperties not affected by propertyNames', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
propertyNames: { maxLength: 1 },
unevaluatedProperties: {
type: 'number',
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ a: 1 }))).to.be.empty;
const result = await parseSetup(toContent({ a: 'b' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
(0, chai_1.expect)(result[0].message).to.include('number');
});
it('unevaluatedProperties can see annotations from if without then and else', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
if: {
patternProperties: {
foo: {
type: 'string',
},
},
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(toContent({ foo: 'a' }))).to.be.empty;
const result = await parseSetup(toContent({ bar: 'a' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('dependentSchemas with unevaluatedProperties', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: { foo2: {} },
dependentSchemas: {
foo: {},
foo2: {
properties: {
bar: {},
},
},
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result1 = await parseSetup(toContent({ foo: '' }));
(0, chai_1.expect)(result1).to.have.length(1);
(0, chai_1.expect)(result1[0].message).to.include('Property foo is not allowed.');
const result2 = await parseSetup(toContent({ bar: '' }));
(0, chai_1.expect)(result2).to.have.length(1);
(0, chai_1.expect)(result2[0].message).to.include('Property bar is not allowed.');
(0, chai_1.expect)(await parseSetup(toContent({ foo2: '', bar: '' }))).to.be.empty;
});
it('Evaluated properties collection needs to consider instance location', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
foo: {
properties: {
bar: { type: 'string' },
},
},
},
unevaluatedProperties: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result = await parseSetup(toContent({ foo: { bar: 'foo' }, bar: 'bar' }));
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
});
describe('keyword: unevaluatedItems', () => {
it('unevaluatedItems true', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: true,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`[]`)).to.be.empty;
(0, chai_1.expect)(await parseSetup(`- foo`)).to.be.empty;
});
it('unevaluatedItems false', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`[]`)).to.be.empty;
const content = `- foo`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 0 or fewer.');
});
it('unevaluatedItems as schema', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: { type: 'string' },
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`[]`)).to.be.empty;
(0, chai_1.expect)(await parseSetup(`- foo`)).to.be.empty;
const result = await parseSetup(`- 42`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type. Expected');
(0, chai_1.expect)(result[0].message).to.include('string');
});
it('unevaluatedItems with uniform items', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: { type: 'string' },
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- foo\n- bar`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('unevaluatedItems with tuple', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ type: 'string' }],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- foo`)).to.be.empty;
const content = `- foo\n- bar`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 1 or fewer.');
});
it('unevaluatedItems with items and additionalItems', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ type: 'string' }],
additionalItems: true,
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- foo\n- 42`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('unevaluatedItems with ignored additionalItems', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
additionalItems: { type: 'number' },
unevaluatedItems: { type: 'string' },
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- foo\n- 1`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type. Expected');
(0, chai_1.expect)(result[0].message).to.include('string');
(0, chai_1.expect)(await parseSetup(`- foo\n- bar\n- baz`)).to.be.empty;
});
it('unevaluatedItems with ignored applicator additionalItems', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [{ additionalItems: { type: 'number' } }],
unevaluatedItems: { type: 'string' },
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- foo\n- 1`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type. Expected');
(0, chai_1.expect)(result[0].message).to.include('string');
(0, chai_1.expect)(await parseSetup(`- foo\n- bar\n- baz`)).to.be.empty;
});
it('unevaluatedItems with nested tuple', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ type: 'string' }],
allOf: [
{
items: [true, { type: 'number' }],
},
],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- foo\n- 42`)).to.be.empty;
const content = `- foo\n- 42\n- true`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 2 or fewer.');
});
it('unevaluatedItems with nested items', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: { type: 'boolean' },
anyOf: [{ items: { type: 'string' } }, true],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content1 = `- true\n- false`;
const result1 = await parseSetup(content1);
(0, chai_1.expect)(result1).to.be.empty;
const content2 = `- "yes"\n- false`;
const result2 = await parseSetup(content2);
(0, chai_1.expect)(result2).to.have.length(1);
(0, chai_1.expect)(result2[0].message).to.include('Incorrect type.');
const content3 = `- "yes"\n- "no"`;
const result3 = await parseSetup(content3);
(0, chai_1.expect)(result3).to.be.empty;
});
it('unevaluatedItems with nested items and additionalItems', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [
{
items: [{ type: 'string' }],
additionalItems: true,
},
],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- foo`)).to.be.empty;
(0, chai_1.expect)(await parseSetup(`- foo\n- 42\n- true`)).to.be.empty;
});
it('unevaluatedItems with nested unevaluatedItems', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [
{
items: [{ type: 'string' }],
},
{ unevaluatedItems: true },
],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- foo`)).to.be.empty;
const content = `- foo\n- 42\n- true`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('unevaluatedItems with anyOf', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ const: 'foo' }],
anyOf: [
{
items: [true, { const: 'bar' }],
},
{
items: [true, true, { const: 'baz' }],
},
],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content1 = `- foo\n- bar`;
const result1 = await parseSetup(content1);
(0, chai_1.expect)(result1).to.be.empty;
const content2 = `- foo\n- bar\n- 42`;
const result2 = await parseSetup(content2);
(0, chai_1.expect)(result2).to.have.length(1);
(0, chai_1.expect)(result2[0].message).to.include('Array has too many items according to schema. Expected 2 or fewer.');
const content3 = `- foo\n- bar\n- baz`;
const result3 = await parseSetup(content3);
(0, chai_1.expect)(result3).to.be.empty;
const content4 = `- foo\n- bar\n- baz\n- 42`;
const result4 = await parseSetup(content4);
(0, chai_1.expect)(result4).to.have.length(1);
(0, chai_1.expect)(result4[0].message).to.include('Array has too many items according to schema. Expected 3 or fewer.');
});
it('unevaluatedItems with oneOf', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ const: 'foo' }],
oneOf: [
{
items: [true, { const: 'bar' }],
},
{
items: [true, { const: 'baz' }],
},
],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- foo\n- bar`)).to.be.empty;
const content = `- foo\n- bar\n- 42`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 2 or fewer.');
});
it('unevaluatedItems with not', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ const: 'foo' }],
not: {
not: {
items: [true, { const: 'bar' }],
},
},
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- foo\n- bar`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 1 or fewer.');
});
it('unevaluatedItems with if/then/else', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [{ const: 'foo' }],
if: {
items: [true, { const: 'bar' }],
},
then: {
items: [true, true, { const: 'then' }],
},
else: {
items: [true, true, true, { const: 'else' }],
},
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const result1 = await parseSetup(`- foo\n- bar\n- then`);
(0, chai_1.expect)(result1).to.be.empty;
const result2 = await parseSetup(`- foo\n- bar\n- then\n- else`);
(0, chai_1.expect)(result2).to.have.length(1);
(0, chai_1.expect)(result2[0].message).to.include('Array has too many items according to schema. Expected 3 or fewer.');
const result3 = await parseSetup(`- foo\n- 42\n- 42\n- else`);
(0, chai_1.expect)(result3).to.be.empty;
const result = await parseSetup(`- foo\n- 42\n- 42\n- else\n- 42`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 4 or fewer.');
});
it('unevaluatedItems with boolean schemas', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [true],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`[]`)).to.be.empty;
const content = `- foo`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 0 or fewer.');
});
it('unevaluatedItems with $ref', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$ref: '#/$defs/bar',
items: [{ type: 'string' }],
unevaluatedItems: false,
$defs: {
bar: {
items: [true, { type: 'string' }],
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- foo\n- bar`)).to.be.empty;
const content = `- foo\n- bar\n- baz`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 2 or fewer.');
});
it('unevaluatedItems before $ref', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
items: [{ type: 'string' }],
$ref: '#/$defs/bar',
$defs: {
bar: {
items: [true, { type: 'string' }],
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- foo\n- bar`)).to.be.empty;
const content = `- foo\n- bar\n- baz`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 2 or fewer.');
});
it('unevaluatedItems with $recursiveRef', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'https://example.com/unevaluated-items-with-recursive-ref/extended-tree',
$recursiveAnchor: true,
$ref: './tree',
items: [true, true, { type: 'string' }],
$defs: {
tree: {
$id: './tree',
$recursiveAnchor: true,
type: 'array',
items: [
{ type: 'number' },
{
unevaluatedItems: false,
$recursiveRef: '#',
},
],
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- 1\n- - 2\n - []\n - b\n- a`)).to.be.empty;
const content = `- 1\n- - 2\n - []\n - b\n - too many\n- a`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 3 or fewer.');
});
it("unevaluatedItems can't see inside cousins", async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
allOf: [{ items: [true] }, { unevaluatedItems: false }],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- 1`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 0 or fewer.');
});
it('item is evaluated in an uncle schema to unevaluatedItems', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
foo: {
items: [{ type: 'string' }],
unevaluatedItems: false,
},
},
anyOf: [
{
properties: {
foo: {
items: [true, { type: 'string' }],
},
},
},
],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`foo:\n - test`)).to.be.empty;
const content = `foo:\n - test\n - test`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 1 or fewer.');
});
describe('non-array instances are valid', () => {
it('', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `true`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('ignores integers', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `123`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('ignores floats', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `1.0`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('ignores objects', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `{}`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('ignores strings', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `foo`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('ignores null', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `null`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
});
it('unevaluatedItems with null instance elements', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
unevaluatedItems: {
type: 'null',
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- null`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('unevaluatedItems can see annotations from if without then and else', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
if: {
items: [{ const: 'a' }],
},
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
(0, chai_1.expect)(await parseSetup(`- a`)).to.be.empty;
const content = `- b`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 0 or fewer.');
});
it('Evaluated items collection needs to consider instance location', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: [
{
items: [true, { type: 'string' }],
},
],
unevaluatedItems: false,
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `- - foo\n - bar\n- bar`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items according to schema. Expected 1 or fewer.');
});
});
describe('keyword: contains + minContains/maxContains', () => {
it('minContains fails when too few items match contains subschema', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'array',
contains: {
type: 'object',
properties: {
kind: { const: 'ok' },
id: { type: 'number' },
},
required: ['kind', 'id'],
},
minContains: 2,
});
const content = `- kind: ok\n id: 1\n- kind: ok\n id: "2"\n- kind: nope\n id: 3`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
(0, chai_1.expect)(result[0].message).to.include('contains');
(0, chai_1.expect)(result[0].message).to.include('Expected 2 or more.');
});
it('maxContains fails when too many items match contains subschema', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'array',
contains: {
type: 'object',
properties: {
kind: { const: 'ok' },
id: { type: 'number' },
},
required: ['kind', 'id'],
},
maxContains: 3,
});
const content = `- kind: ok\n id: 1\n- kind: ok\n id: 2\n- kind: ok\n id: 3\n- kind: ok\n id: 4`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items matching');
(0, chai_1.expect)(result[0].message).to.include('contains');
(0, chai_1.expect)(result[0].message).to.include('Expected 3 or fewer.');
});
it('minContains/maxContains passes when match count is within bounds', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'array',
contains: {
type: 'object',
properties: {
kind: { const: 'ok' },
id: { type: 'number' },
},
required: ['kind', 'id'],
},
minContains: 2,
maxContains: 3,
});
const content = `
- kind: ok
id: 1
- kind: ok
id: 2
- kind: nope
id: 3
`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('contains matching is based on subschema, not just item type', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'array',
contains: {
type: 'object',
required: ['tag'],
properties: {
tag: { const: 'match' },
},
},
minContains: 1,
});
const okYaml = `
- tag: other
- tag: match
- nope: 1
`;
let result = await parseSetup(okYaml);
(0, chai_1.expect)(result).to.be.empty;
const badYaml = `
- tag: other
- tag: nope
- nope: 1`;
result = await parseSetup(badYaml);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
(0, chai_1.expect)(result[0].message).to.include('contains');
(0, chai_1.expect)(result[0].message).to.include('Expected 1 or more.');
});
});
describe('keyword: dependentRequired', () => {
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
billing_address: { type: 'string' },
credit_card: { type: 'string' },
},
dependentRequired: {
billing_address: ['credit_card'],
},
});
});
it('requires dependent properties when the trigger property is present', async () => {
const content = `billing_address: "123 King St"`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property credit_card required by property billing_address.');
});
it('passes when required dependent properties are present', async () => {
const content = `billing_address: "123 King St"\ncredit_card: "4111-1111"`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
});
describe('keyword: dependentSchemas', () => {
it('does not apply when the trigger property is absent', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
kind: { type: 'string' },
port: { type: 'number' },
},
dependentSchemas: {
kind: { required: ['port'] },
},
});
const content = `port: 8080`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('applies dependent schema when the trigger property is present', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
kind: { type: 'string' },
port: { type: 'number' },
},
dependentSchemas: {
kind: { required: ['port'] },
},
});
const content = `kind: service`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
(0, chai_1.expect)(result[0].message).to.include('port');
});
it('can enforce additional constraints from the dependent schema', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
tls: { type: 'boolean' },
port: { type: 'number' },
},
dependentSchemas: {
tls: {
required: ['port'],
properties: {
port: { minimum: 1024 },
},
},
},
});
const content = `tls: true\nport: 80`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Value is below the minimum of 1024.');
});
it('applies multiple dependentSchemas when multiple triggers are present', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
kind: { type: 'string' },
tls: { type: 'boolean' },
port: { type: 'number' },
},
dependentSchemas: {
kind: { required: ['port'] },
tls: {
required: ['port'],
properties: { port: { minimum: 1024 } },
},
},
});
const content = `kind: service\ntls: true\nport: 80`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Value is below the minimum of 1024.');
});
});
describe('keyword: dependencies (backward compatibility)', () => {
describe('property dependencies tests', () => {
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
credit_card: { type: 'string' },
billing_address: { type: 'string' },
},
dependencies: {
credit_card: ['billing_address'],
},
});
});
it('requires dependent properties when the trigger property is present', async () => {
const content = `credit_card: "4111-1111-1111-1111"`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property billing_address required by property credit_card.');
});
it('does not apply when the trigger property is absent', async () => {
const content = `billing_address: "123 Main St"`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
});
describe('schema dependencies tests', () => {
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
mode: { type: 'string' },
port: { type: 'number' },
},
dependencies: {
mode: {
required: ['port'],
properties: {
port: { minimum: 1024 },
},
},
},
});
});
it('enforces dependent schema constraints when trigger property is present', async () => {
const content = `mode: "server"\nport: 80`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Value is below the minimum of 1024.');
});
it('enforces dependent schema required properties when trigger property is present', async () => {
const content = `mode: "server"`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
(0, chai_1.expect)(result[0].message).to.include('port');
});
it('does not apply the dependent schema when trigger property is absent', async () => {
const content = `port: 80`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
});
});
describe('$ref resolution should support sibling keywords', () => {
it('should apply sibling keywords next to $ref', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
value: {
$ref: '#/$defs/A',
type: 'number',
},
},
$defs: {
A: { type: 'string' },
},
});
// both should fail: must be both string and number
(0, chai_1.expect)((await parseSetup(`value: hello`)).length).to.be.greaterThan(0);
(0, chai_1.expect)((await parseSetup(`value: 1`)).length).to.be.greaterThan(0);
});
it('should apply sibling keywords next to $ref (top level)', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
definitions: {
obj1: {
type: 'object',
properties: {
value: { type: 'string' },
},
required: ['value'],
},
},
$ref: '#/definitions/obj1',
additionalProperties: false,
properties: {
value: {},
extra: { type: 'number' },
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
const content = `value: hello
extra: notANumber
unknown: 1
`;
const result = await parseSetup(content);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type. Expected');
(0, chai_1.expect)(result[0].message).to.include('number');
(0, chai_1.expect)(result[1].message).to.include('Property unknown is not allowed.');
});
});
describe('$id resolution', () => {
it('$id inside an enum is not a real identifier (the implementation must not be confused by an $id buried in the enum)', async () => {
const root = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$defs: {
id_in_enum: {
enum: [
{
$id: 'https://localhost:1234/draft2019-09/id/my_identifier.json',
type: 'null',
},
],
},
real_id_in_schema: {
$id: 'https://localhost:1234/draft2019-09/id/my_identifier.json',
type: 'string',
},
zzz_id_in_const: {
const: {
$id: 'https://localhost:1234/draft2019-09/id/my_identifier.json',
type: 'null',
},
},
},
anyOf: [{ $ref: '#/$defs/id_in_enum' }, { $ref: 'https://localhost:1234/draft2019-09/id/my_identifier.json' }],
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, root);
// exact match to enum, and type matches
(0, chai_1.expect)(await parseSetup(toContent({
$id: 'https://localhost:1234/draft2019-09/id/my_identifier.json',
type: 'null',
}))).to.be.empty;
// match $ref to $id
(0, chai_1.expect)(await parseSetup('a string to match #/$defs/id_in_enum')).to.be.empty;
// no match on enum or $ref to $id
const result = await parseSetup(`1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Value is not accepted. Valid values:');
});
it('should resolve embedded resource $id for relative $ref without external load', async () => {
const root = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
x: { $ref: 'other.json#bar' },
},
required: ['x'],
$defs: {
B: {
$id: 'other.json',
$defs: {
X: {
$anchor: 'bar',
type: 'string',
minLength: 2,
},
},
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, root);
const yaml = `x: A`;
const result = await parseSetup(yaml);
(0, chai_1.expect)(result.some((d) => /Problems loading reference/i.test(d.message))).to.eq(false);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('String is shorter than the minimum length of 2.');
});
it('should handle $id changing base URI for nested $anchor resolution', async () => {
const root = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
properties: {
address: { $ref: 'schemas/address.json#USAddress' },
},
$defs: {
addressSchema: {
$id: 'schemas/address.json',
$defs: {
us: {
$anchor: 'USAddress',
type: 'object',
properties: {
zipCode: { type: 'string', minLength: 5 },
},
required: ['zipCode'],
},
},
},
},
};
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, root);
const yaml = `address:\n zipCode: "123"`;
const result = await parseSetup(yaml);
(0, chai_1.expect)(result.some((d) => /Problems loading reference/i.test(d.message))).to.eq(false);
(0, chai_1.expect)(result[0].message).to.include('String is shorter than the minimum length of 5.');
});
});
describe('$recursiveAnchor and $recursiveRef resolution', () => {
describe('$recursiveRef without $recursiveAnchor works like $ref', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
foo: { $recursiveRef: '#' },
},
additionalProperties: false,
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('match', async () => {
(0, chai_1.expect)(await parseSetup('foo: false')).to.be.empty;
});
it('recursive match', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
foo: false`)).to.be.empty;
});
it('mismatch', async () => {
const result = await parseSetup('bar: false');
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
it('recursive mismatch', async () => {
const result = await parseSetup(`foo:
bar: false`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Property bar is not allowed.');
});
});
describe('$recursiveRef without using nesting', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:4242/draft2019-09/recursiveRef2/schema.json',
$defs: {
myobject: {
$id: 'myobject.json',
$recursiveAnchor: true,
anyOf: [
{ type: 'string' },
{
type: 'object',
additionalProperties: { $recursiveRef: '#' },
},
],
},
},
anyOf: [{ type: 'integer' }, { $ref: '#/$defs/myobject' }],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('integer matches at the outer level', async () => {
(0, chai_1.expect)(await parseSetup(`1`)).to.be.empty;
});
it('single level match', async () => {
(0, chai_1.expect)(await parseSetup(`foo: hi`)).to.be.empty;
});
it('integer does not match as a property value', async () => {
const result = await parseSetup(`foo: 1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('string | object');
});
it('two levels, properties match with inner definition', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
bar: hi`)).to.be.empty;
});
it('two levels, no match', async () => {
const result = await parseSetup(`foo:
bar: 1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('string | object');
});
});
describe('$recursiveRef with nesting', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:4242/draft2019-09/recursiveRef3/schema.json',
$recursiveAnchor: true,
$defs: {
myobject: {
$id: 'myobject.json',
$recursiveAnchor: true,
anyOf: [
{ type: 'string' },
{
type: 'object',
additionalProperties: { $recursiveRef: '#' },
},
],
},
},
anyOf: [{ type: 'integer' }, { $ref: '#/$defs/myobject' }],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('integer matches at the outer level', async () => {
(0, chai_1.expect)(await parseSetup(`1`)).to.be.empty;
});
it('single level match', async () => {
(0, chai_1.expect)(await parseSetup(`foo: hi`)).to.be.empty;
});
it('integer now matches as a property value', async () => {
(0, chai_1.expect)(await parseSetup(`foo: 1`)).to.be.empty;
});
it('two levels, properties match with inner definition', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
bar: hi`)).to.be.empty;
});
it('two levels, properties match with $recursiveRef', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
bar: 1`)).to.be.empty;
});
});
describe('$recursiveRef with $recursiveAnchor: false works like $ref', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:4242/draft2019-09/recursiveRef4/schema.json',
$recursiveAnchor: false,
$defs: {
myobject: {
$id: 'myobject.json',
$recursiveAnchor: false,
anyOf: [
{ type: 'string' },
{
type: 'object',
additionalProperties: { $recursiveRef: '#' },
},
],
},
},
anyOf: [{ type: 'integer' }, { $ref: '#/$defs/myobject' }],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('integer matches at the outer level', async () => {
(0, chai_1.expect)(await parseSetup(`1`)).to.be.empty;
});
it('single level match', async () => {
(0, chai_1.expect)(await parseSetup(`foo: hi`)).to.be.empty;
});
it('integer does not match as a property value', async () => {
const result = await parseSetup(`foo: 1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('string | object');
});
it('two levels, properties match with inner definition', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
bar: hi`)).to.be.empty;
});
it('two levels, integer does not match as a property value', async () => {
const result = await parseSetup(`foo:
bar: 1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('string | object');
});
});
describe('$recursiveRef with no $recursiveAnchor works like $ref', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:4242/draft2019-09/recursiveRef5/schema.json',
$defs: {
myobject: {
$id: 'myobject.json',
$recursiveAnchor: false,
anyOf: [
{ type: 'string' },
{
type: 'object',
additionalProperties: { $recursiveRef: '#' },
},
],
},
},
anyOf: [{ type: 'integer' }, { $ref: '#/$defs/myobject' }],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('integer matches at the outer level', async () => {
(0, chai_1.expect)(await parseSetup(`1`)).to.be.empty;
});
it('single level match', async () => {
(0, chai_1.expect)(await parseSetup(`foo: hi`)).to.be.empty;
});
it('integer does not match as a property value', async () => {
const result = await parseSetup(`foo: 1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('string | object');
});
it('two levels, properties match with inner definition', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
bar: hi`)).to.be.empty;
});
it('two levels, integer does not match as a property value', async () => {
const result = await parseSetup(`foo:
bar: 1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('string | object');
});
});
describe('$recursiveRef with no $recursiveAnchor in the initial target schema resource', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:4242/draft2019-09/recursiveRef6/base.json',
$recursiveAnchor: true,
anyOf: [
{ type: 'boolean' },
{
type: 'object',
additionalProperties: {
$id: 'http://localhost:4242/draft2019-09/recursiveRef6/inner.json',
$comment: 'there is no $recursiveAnchor: true here, so we do NOT recurse to the base',
anyOf: [{ type: 'integer' }, { type: 'object', additionalProperties: { $recursiveRef: '#' } }],
},
},
],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('leaf node does not match; no recursion', async () => {
const result = await parseSetup(`foo: true`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('integer | object');
});
it('leaf node matches: recursion uses the inner schema', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
bar: 1`)).to.be.empty;
});
it('leaf node does not match: recursion uses the inner schema', async () => {
const result = await parseSetup(`foo:
bar: true`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('integer | object');
});
});
describe('$recursiveRef with no $recursiveAnchor in the outer schema resource', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:4242/draft2019-09/recursiveRef7/base.json',
anyOf: [
{ type: 'boolean' },
{
type: 'object',
additionalProperties: {
$id: 'http://localhost:4242/draft2019-09/recursiveRef7/inner.json',
$recursiveAnchor: true,
anyOf: [{ type: 'integer' }, { type: 'object', additionalProperties: { $recursiveRef: '#' } }],
},
},
],
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('leaf node does not match; no recursion', async () => {
const result = await parseSetup(`foo: true`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('integer | object');
});
it('leaf node matches: recursion only uses inner schema', async () => {
(0, chai_1.expect)(await parseSetup(`foo:
bar: 1`)).to.be.empty;
});
it('leaf node does not match: recursion only uses inner schema', async () => {
const result = await parseSetup(`foo:
bar: true`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('integer | object');
});
});
describe('multiple dynamic paths to the $recursiveRef keyword', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'https://example.com/recursiveRef8_main.json',
$defs: {
inner: {
$id: 'recursiveRef8_inner.json',
$recursiveAnchor: true,
title: 'inner',
additionalProperties: {
$recursiveRef: '#',
},
},
},
if: {
propertyNames: {
pattern: '^[a-m]',
},
},
then: {
title: 'any type of node',
$id: 'recursiveRef8_anyLeafNode.json',
$recursiveAnchor: true,
$ref: 'recursiveRef8_inner.json',
},
else: {
title: 'integer node',
$id: 'recursiveRef8_integerNode.json',
$recursiveAnchor: true,
type: ['object', 'integer'],
$ref: 'recursiveRef8_inner.json',
},
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('recurse to anyLeafNode - floats are allowed', async () => {
(0, chai_1.expect)(await parseSetup(`alpha: 1.1`)).to.be.empty;
});
it('recurse to integerNode - floats are not allowed', async () => {
const result = await parseSetup(`november: 1.1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type. Expected one of object, integer.');
});
});
describe('dynamic $recursiveRef destination (not predictable at schema compile time)', () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'https://example.com/main.json',
$defs: {
inner: {
$id: 'inner.json',
$recursiveAnchor: true,
title: 'inner',
additionalProperties: {
$recursiveRef: '#',
},
},
},
if: { propertyNames: { pattern: '^[a-m]' } },
then: {
title: 'any type of node',
$id: 'anyLeafNode.json',
$recursiveAnchor: true,
$ref: 'main.json#/$defs/inner',
},
else: {
title: 'integer node',
$id: 'integerNode.json',
$recursiveAnchor: true,
type: ['object', 'integer'],
$ref: 'main.json#/$defs/inner',
},
};
beforeEach(() => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
});
it('numeric node', async () => {
(0, chai_1.expect)(await parseSetup(`alpha: 1.1`)).to.be.empty;
});
it('integer node', async () => {
const result = await parseSetup(`november: 1.1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type. Expected one of object, integer.');
});
});
it('does not infinite loop on self-recursive $recursiveRef', async () => {
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://localhost:4242/recursive.json',
$recursiveAnchor: true,
anyOf: [
{ type: 'string' },
{
type: 'object',
additionalProperties: { $recursiveRef: '#' },
},
],
};
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'http://localhost:4242/recursive.json', schema);
const result = await parseSetup(`# yaml-language-server: $schema=http://localhost:4242/recursive.json
foo:
bar:
baz: 1`);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
(0, chai_1.expect)(result[0].message).to.include('string | object');
});
describe('tree schema with unevaluatedProperties', () => {
it('$recursiveRef resolves to outermost schema with $recursiveAnchor in dynamic scope', async () => {
const treeSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://example.com/tree.json',
$recursiveAnchor: true,
type: 'object',
properties: {
value: { type: 'integer' },
children: { type: 'array', items: { $recursiveRef: '#' } },
},
required: ['value'],
};
const treeWithMetaSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://example.com/tree-with-meta.json',
$recursiveAnchor: true,
allOf: [{ $ref: 'http://example.com/tree.json' }],
properties: {
meta: { type: 'string' },
},
required: ['meta'],
unevaluatedProperties: false,
};
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'http://example.com/tree.json', treeSchema);
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'http://example.com/tree-with-meta.json', treeWithMetaSchema);
const content = `# yaml-language-server: $schema=http://example.com/tree-with-meta.json
value: 1
meta: root
children:
- value: 2
`;
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
(0, chai_1.expect)(result[0].message).to.include('meta');
});
it('$recursiveRef with all required properties should pass', async () => {
const treeSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://example.com/tree2.json',
$recursiveAnchor: true,
type: 'object',
properties: {
value: { type: 'integer' },
children: { type: 'array', items: { $recursiveRef: '#' } },
},
required: ['value'],
};
const treeWithMetaSchema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
$id: 'http://example.com/tree-with-meta2.json',
$recursiveAnchor: true,
allOf: [{ $ref: 'http://example.com/tree2.json' }],
properties: {
meta: { type: 'string' },
},
required: ['meta'],
unevaluatedProperties: false,
};
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'http://example.com/tree2.json', treeSchema);
schemaProvider.addSchemaWithUri(testHelper_1.SCHEMA_ID, 'http://example.com/tree-with-meta2.json', treeWithMetaSchema);
const content = `# yaml-language-server: $schema=http://example.com/tree-with-meta2.json
value: 1
meta: root
children:
- value: 2
meta: child
`;
(0, chai_1.expect)(await parseSetup(content)).to.be.empty;
});
});
});
describe('keyword: contains', () => {
it('contains keyword validation', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { minimum: 5 },
});
// array with item matching schema (5) is valid
let content = toContent([3, 4, 5]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// array with item matching schema (6) is valid
content = toContent([3, 4, 6]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// array with two items matching schema (5, 6) is valid
content = toContent([3, 4, 5, 6]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// array without items matching schema is invalid
content = toContent([2, 3, 4]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// empty array is invalid
content = toContent([]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// not array is valid
content = toContent({});
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('contains keyword with const keyword', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 5 },
});
// array with item 5 is valid
let content = toContent([3, 4, 5]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// array with two items 5 is valid
content = toContent([3, 4, 5, 5]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// array without item 5 is invalid
content = toContent([1, 2, 3, 4]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
});
it('contains keyword with boolean schema true', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: true,
});
// any non-empty array is valid
let content = toContent(['foo']);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// empty array is invalid
content = toContent([]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
});
it('contains keyword with boolean schema false', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: false,
});
// any non-empty array is invalid
let content = toContent(['foo']);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// empty array is invalid
content = toContent([]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// non-arrays are valid
content = toContent('contains does not apply to strings');
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('items + contains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
items: { multipleOf: 2 },
contains: { multipleOf: 3 },
});
// matches items, does not match contains
let content = toContent([2, 4, 8]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// does not match items, matches contains
content = toContent([3, 6, 9]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(2);
(0, chai_1.expect)(result[0].message).to.include('Value is not divisible by 2.');
(0, chai_1.expect)(result[1].message).to.include('Value is not divisible by 2.');
// matches both items and contains
content = toContent([6, 12]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// matches neither items nor contains
content = toContent([1, 5]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(3);
(0, chai_1.expect)(result[0].message).to.include('Value is not divisible by 2.');
(0, chai_1.expect)(result[1].message).to.include('Value is not divisible by 2.');
(0, chai_1.expect)(result[2].message).to.include('Array has too few items matching');
});
it('contains with false if subschema', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: {
if: false,
else: true,
},
});
// any non-empty array is valid
let content = toContent(['foo']);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// empty array is invalid
content = toContent([]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
});
it('contains with null instance elements', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: {
type: 'null',
},
});
// allows null items
const content = toContent([null]);
const result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
});
describe('keyword: maxContains', () => {
it('maxContains without contains is ignored', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
maxContains: 1,
});
// one item valid against lone maxContains
let content = toContent([1]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// two items still valid against lone maxContains
content = toContent([1, 2]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('maxContains with contains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
maxContains: 1,
});
// empty data
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// all elements match, valid maxContains
content = toContent([1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// all elements match, invalid maxContains
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items matching');
// some elements match, valid maxContains
content = toContent([1, 2]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// some elements match, invalid maxContains
content = toContent([1, 2, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items matching');
});
it('maxContains with contains, value with a decimal', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
maxContains: 1.0,
});
// one element matches, valid maxContains
let content = toContent([1]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// too many elements match, invalid maxContains
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items matching');
});
it('minContains < maxContains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
minContains: 1,
maxContains: 3,
});
// actual < minContains < maxContains
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// minContains < actual < maxContains
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// minContains < maxContains < actual
content = toContent([1, 1, 1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items matching');
});
});
describe('keyword: minContains', () => {
it('minContains without contains is ignored', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
minContains: 1,
});
// one item valid against lone minContains
let content = toContent([1]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// zero items still valid against lone minContains
content = toContent([]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('minContains=1 with contains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
minContains: 1,
});
// empty data
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// no elements match
content = toContent([2]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// single element matches, valid minContains
content = toContent([1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// some elements match, valid minContains
content = toContent([1, 2]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// all elements match, valid minContains
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('minContains=2 with contains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
minContains: 2,
});
// empty data
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// all elements match, invalid minContains
content = toContent([1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// some elements match, invalid minContains
content = toContent([1, 2]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// all elements match, valid minContains (exactly as needed)
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// all elements match, valid minContains (more than needed)
content = toContent([1, 1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// some elements match, valid minContains
content = toContent([1, 2, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('minContains=2 with contains with a decimal value', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
minContains: 2.0,
});
// one element matches, invalid minContains
let content = toContent([1]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// both elements match, valid minContains
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('maxContains = minContains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
maxContains: 2,
minContains: 2,
});
// empty data
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// all elements match, invalid minContains
content = toContent([1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// all elements match, invalid maxContains
content = toContent([1, 1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items matching');
// all elements match, valid maxContains and minContains
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('maxContains < minContains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
maxContains: 1,
minContains: 3,
});
// empty data
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// invalid minContains
content = toContent([1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
// invalid maxContains
content = toContent([1, 1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(2);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
(0, chai_1.expect)(result[1].message).to.include('Array has too many items matching');
// invalid maxContains and minContains
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(2);
(0, chai_1.expect)(result[0].message).to.include('Array has too few items matching');
(0, chai_1.expect)(result[1].message).to.include('Array has too many items matching');
});
it('minContains = 0 with no maxContains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
minContains: 0,
});
// empty data
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// minContains = 0 makes contains always pass
content = toContent([2]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('minContains = 0 with maxContains', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
contains: { const: 1 },
minContains: 0,
maxContains: 1,
});
// empty data
let content = toContent([]);
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// not more than maxContains
content = toContent([1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// too many
content = toContent([1, 1]);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Array has too many items matching');
});
});
describe('keyword: dependentSchemas', () => {
it('single dependency', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
dependentSchemas: {
bar: {
properties: {
foo: { type: 'integer' },
bar: { type: 'integer' },
},
},
},
});
// valid
let content = toContent({ foo: 1, bar: 2 });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// no dependency
content = toContent({ foo: 'quux' });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// wrong type
content = toContent({ foo: 'quux', bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
// wrong type other
content = toContent({ foo: 2, bar: 'quux' });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
// wrong type both
content = toContent({ foo: 'quux', bar: 'quux' });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(2);
(0, chai_1.expect)(result[0].message).to.include('Incorrect type');
(0, chai_1.expect)(result[1].message).to.include('Incorrect type');
// ignores arrays
content = toContent(['bar']);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// ignores strings
content = toContent('foobar');
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// ignores other non-objects
content = toContent(12);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('boolean subschemas', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
dependentSchemas: {
foo: true,
bar: false,
},
});
// object with property having schema true is valid
let content = toContent({ foo: 1 });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// object with property having schema false is invalid
content = toContent({ bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches a schema that is not allowed');
// object with both properties is invalid
content = toContent({ foo: 1, bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Matches a schema that is not allowed');
// empty object is valid
content = toContent({});
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('dependencies with escaped characters', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
dependentSchemas: {
'foo\tbar': { minProperties: 4 },
"foo'bar": { required: ['foo"bar'] },
},
});
// quoted tab
let content = toContent({
'foo\tbar': 1,
a: 2,
b: 3,
c: 4,
});
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// quoted quote
content = toContent({
"foo'bar": { 'foo"bar': 1 },
});
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
// quoted tab invalid under dependent schema
content = toContent({
'foo\tbar': 1,
a: 2,
});
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object has fewer properties than the required number of 4');
// quoted quote invalid under dependent schema
content = toContent({ "foo'bar": 1 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Missing property');
});
it('dependent subschema incompatible with root', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
properties: {
foo: {},
},
dependentSchemas: {
foo: {
properties: {
bar: {},
},
additionalProperties: false,
},
},
});
// matches root
let content = toContent({ foo: 1 });
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('not allowed');
// matches dependency
content = toContent({ bar: 1 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// matches both
content = toContent({ foo: 1, bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('not allowed');
// no dependency
content = toContent({ baz: 1 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
});
describe('keyword: dependentRequired', () => {
it('single dependency', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
dependentRequired: { bar: ['foo'] },
});
// neither
let content = toContent({});
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// nondependant
content = toContent({ foo: 1 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// with dependency
content = toContent({ foo: 1, bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// missing dependency
content = toContent({ bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property foo required by property bar.');
// ignores arrays
content = toContent(['bar']);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// ignores strings
content = toContent('foobar');
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// ignores other non-objects
content = toContent(12);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('empty dependents', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
dependentRequired: { bar: [] },
});
// empty object
let content = toContent({});
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// object with one property
content = toContent({ bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// non-object is valid
content = toContent(1);
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
});
it('multiple dependents required', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
dependentRequired: { quux: ['foo', 'bar'] },
});
// neither
let content = toContent({});
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// nondependants
content = toContent({ foo: 1, bar: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// with dependencies
content = toContent({ foo: 1, bar: 2, quux: 3 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// missing dependency
content = toContent({ foo: 1, quux: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property bar required by property quux.');
// missing other dependency
content = toContent({ bar: 1, quux: 2 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property foo required by property quux.');
// missing both dependencies
content = toContent({ quux: 1 });
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(2);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property foo required by property quux.');
(0, chai_1.expect)(result[1].message).to.include('Object is missing property bar required by property quux.');
});
it('dependencies with escaped characters', async () => {
schemaProvider.addSchema(testHelper_1.SCHEMA_ID, {
$schema: 'https://json-schema.org/draft/2019-09/schema',
dependentRequired: {
'foo\nbar': ['foo\rbar'],
'foo"bar': ["foo'bar"],
},
});
// CRLF
let content = toContent({
'foo\nbar': 1,
'foo\rbar': 2,
});
let result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// quoted quotes
content = toContent({
"foo'bar": 1,
'foo"bar': 2,
});
result = await parseSetup(content);
(0, chai_1.expect)(result).to.be.empty;
// CRLF missing dependent
content = toContent({
'foo\nbar': 1,
foo: 2,
});
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property foo\rbar required by property foo\nbar.');
// quoted quotes missing dependent
content = toContent({
'foo"bar': 2,
});
result = await parseSetup(content);
(0, chai_1.expect)(result).to.have.length(1);
(0, chai_1.expect)(result[0].message).to.include('Object is missing property foo\'bar required by property foo"bar.');
});
});
});
//# sourceMappingURL=schema2019Validation.test.js.map