UNPKG

yaml-language-server

Version:
1,078 lines 173 kB
"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