/*--------------------------------------------------------------------------------------------- * Copyright (c) Red Hat. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { configureLanguageService, setupTextDocument, } from './utils/testHelper'; import { createExpectedError } from './utils/verifyError'; import { ServiceSetup } from './utils/serviceSetup'; import { StringTypeError, BooleanTypeError, ArrayTypeError, ObjectTypeError, IncludeWithoutValueError, ColonMissingError, BlockMappingEntryError, } from './utils/errorMessages'; import assert = require('assert'); const uri = 'http://json.schemastore.org/bowerrc'; const fileMatch = ['*.yml', '*.yaml']; const languageSettingsSetup = new ServiceSetup() .withValidate() .withCustomTags(['!Test', '!Ref sequence']) .withSchemaFileMatch({ uri, fileMatch: fileMatch }); const languageService = configureLanguageService( languageSettingsSetup.languageSettings ); // Defines a Mocha test describe to group tests of similar kind together describe('Validation Tests', () => { // Tests for validator describe('Validation', function() { function parseSetup(content: string) { const testTextDocument = setupTextDocument(content); return languageService.doValidation(testTextDocument, false); } //Validating basic nodes describe('Test that validation does not throw errors', function() { it('Basic test', done => { const content = 'analytics: true'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Test that boolean value without quotations is valid', done => { const content = 'analytics: no'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Test that boolean is valid when inside strings', done => { const content = 'cwd: "no"'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Basic test', done => { const content = 'analytics: true'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Basic test on nodes with children', done => { const content = 'scripts:\n preinstall: test1\n postinstall: test2'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Advanced test on nodes with children', done => { const content = 'analytics: true\ncwd: this\nscripts:\n preinstall: test1\n postinstall: test2'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Type string validates under children', done => { const content = 'registry:\n register: file://test_url'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Include with value should not error', done => { const content = 'customize: !include customize.yaml'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Null scalar value should be treated as string', done => { const content = 'cwd: Null'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Anchor should not not error', done => { const content = 'default: &DEFAULT\n name: Anchor\nanchor_test:\n <<: *DEFAULT'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Anchor with multiple references should not not error', done => { const content = 'default: &DEFAULT\n name: Anchor\nanchor_test:\n <<: *DEFAULT\nanchor_test2:\n <<: *DEFAULT'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Multiple Anchor in array of references should not not error', done => { const content = 'default: &DEFAULT\n name: Anchor\ncustomname: &CUSTOMNAME\n custom_name: Anchor\nanchor_test:\n <<: [*DEFAULT, *CUSTOMNAME]'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Multiple Anchors being referenced in same level at same time', done => { const content = 'default: &DEFAULT\n name: Anchor\ncustomname: &CUSTOMNAME\n custom_name: Anchor\nanchor_test:\n <<: *DEFAULT\n <<: *CUSTOMNAME\n'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Custom Tags without type', done => { const content = 'analytics: !Test false'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Custom Tags with type', done => { const content = 'resolvers: !Ref\n - test'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); describe('Type tests', function() { it('Type String does not error on valid node', done => { const content = 'cwd: this'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Type Boolean does not error on valid node', done => { const content = 'analytics: true'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Type Number does not error on valid node', done => { const content = 'timeout: 60000'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Type Object does not error on valid node', done => { const content = 'registry:\n search: file://test_url'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Type Array does not error on valid node', done => { const content = 'resolvers:\n - test\n - test\n - test'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); it('Do not error when there are multiple types in schema and theyre valid', done => { const content = 'license: MIT'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 0); }) .then(done, done); }); }); }); describe('Test that validation DOES throw errors', function() { it('Error when theres a finished untyped item', done => { const content = 'cwd: hello\nan'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 2); assert.deepEqual( result[0], createExpectedError(BlockMappingEntryError, 1, 13, 1, 13) ); assert.deepEqual( result[1], createExpectedError(ColonMissingError, 1, 13, 1, 13) ); }) .then(done, done); }); it('Error when theres no value for a node', done => { const content = 'cwd:'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(StringTypeError, 0, 4, 0, 4) ); }) .then(done, done); }); it('Error on incorrect value type (number)', done => { const content = 'cwd: 100000'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(StringTypeError, 0, 5, 0, 11) ); }) .then(done, done); }); it('Error on incorrect value type (boolean)', done => { const content = 'cwd: False'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(StringTypeError, 0, 5, 0, 10) ); }) .then(done, done); }); it('Error on incorrect value type (string)', done => { const content = 'analytics: hello'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(BooleanTypeError, 0, 11, 0, 16) ); }) .then(done, done); }); it('Error on incorrect value type (object)', done => { const content = 'scripts: test'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(ObjectTypeError, 0, 9, 0, 13) ); }) .then(done, done); }); it('Error on incorrect value type (array)', done => { const content = 'resolvers: test'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(ArrayTypeError, 0, 11, 0, 15) ); }) .then(done, done); }); it('Include without value should error', done => { const content = 'customize: !include'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(IncludeWithoutValueError, 0, 19, 0, 19) ); }) .then(done, done); }); it('Test that boolean value in quotations is not interpreted as boolean i.e. it errors', done => { const content = 'analytics: "no"'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(BooleanTypeError, 0, 11, 0, 15) ); }) .then(done, done); }); it('Test that boolean is invalid when no strings present and schema wants string', done => { const content = 'cwd: no'; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 1); assert.deepEqual( result[0], createExpectedError(StringTypeError, 0, 5, 0, 7) ); }) .then(done, done); }); }); describe('Test with custom schemas', function() { function parseSetup(content: string) { const testTextDocument = setupTextDocument(content); return languageService.doValidation(testTextDocument, true); } it('Test that properties that match multiple enums get validated properly', done => { const schema = { definitions: { ImageStreamImport: { type: 'object', properties: { kind: { type: 'string', enum: ['ImageStreamImport'], }, }, }, ImageStreamLayers: { type: 'object', properties: { kind: { type: 'string', enum: ['ImageStreamLayers'], }, }, }, }, oneOf: [ { $ref: '#/definitions/ImageStreamImport', }, { $ref: '#/definitions/ImageStreamLayers', }, ], }; languageService.configure({ schemas: [ { uri: 'file://test.yaml', fileMatch: ['*.yaml', '*.yml'], schema, }, ], validate: true, isKubernetes: true, }); const content = 'kind: '; const validator = parseSetup(content); validator .then(function(result) { assert.equal(result.length, 2); // tslint:disable-next-line:quotemark assert.equal( result[1].message, `Value is not accepted. Valid values: "ImageStreamImport", "ImageStreamLayers".` ); }) .then(done, done); }); }); }); });