yaml-language-server
Version:
1,078 lines • 173 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
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 KUBERNETES_SCHEMA_URL = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/${schemaUrls_1.DEFAULT_KUBERNETES_SCHEMA_VERSION}-standalone-strict/all.json`;
const toContent = (data) => JSON.stringify(data, null, 2);
before(() => {
languageSettingsSetup = new serviceSetup_1.ServiceSetup()
.withValidate()
.withCompletion()
.withCustomTags(['!Test', '!Ref sequence'])
.withSchemaFileMatch({ uri: KUBERNETES_SCHEMA_URL, fileMatch: ['.drone.yml'] })
.withSchemaFileMatch({ uri: 'https://json.schemastore.org/drone', fileMatch: ['.drone.yml'] })
.withSchemaFileMatch({ uri: 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,
},
],
unevalu