"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const assert = require("assert");
const parser = require("../src/languageservice/parser/yamlParser07");
const SchemaService = require("../src/languageservice/services/yamlSchemaService");
const url = require("url");
const path = require("path");
const request_light_1 = require("request-light");
const yamlSchemaService_1 = require("../src/languageservice/services/yamlSchemaService");
const schemaUrls_1 = require("../src/languageservice/utils/schemaUrls");
const chai_1 = require("chai");
const serviceSetup_1 = require("./utils/serviceSetup");
const testHelper_1 = require("./utils/testHelper");
const src_1 = require("../src");
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const yaml_1 = require("yaml");
const modelineUtil_1 = require("../src/languageservice/services/modelineUtil");
const requestServiceMock = function (uri) {
    return Promise.reject(`Resource ${uri} not found.`);
};
const workspaceContext = {
    resolveRelativePath: (relativePath, resource) => {
        return url.resolve(resource, relativePath);
    },
};
const schemaRequestServiceForURL = (uri) => {
    const headers = { 'Accept-Encoding': 'gzip, deflate' };
    return (0, request_light_1.xhr)({ url: uri, followRedirects: 5, headers }).then((response) => {
        return response.responseText;
    }, (error) => {
        return Promise.reject(error.responseText || error.toString());
    });
};
describe('JSON Schema', () => {
    let languageSettingsSetup;
    let languageService;
    beforeEach(() => {
        languageSettingsSetup = new serviceSetup_1.ServiceSetup()
            .withValidate()
            .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://json.schemastore.org/composer', fileMatch: ['test.yml'] });
        const { languageService: langService } = (0, testHelper_1.setupLanguageService)(languageSettingsSetup.languageSettings);
        languageService = langService;
    });
    it('Resolving $refs', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://myschemastore/main': {
                    id: 'https://myschemastore/main',
                    type: 'object',
                    properties: {
                        child: {
                            $ref: 'https://myschemastore/child',
                        },
                    },
                },
                'https://myschemastore/child': {
                    id: 'https://myschemastore/child',
                    type: 'bool',
                    description: 'Test description',
                },
            },
        });
        service
            .getResolvedSchema('https://myschemastore/main')
            .then((solvedSchema) => {
            assert.deepEqual(solvedSchema.schema.properties['child'], {
                id: 'https://myschemastore/child',
                type: 'bool',
                description: 'Test description',
                _$ref: 'https://myschemastore/child',
                url: 'https://myschemastore/child',
            });
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Resolving $refs 2', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://json.schemastore.org/swagger-2.0': {
                    id: 'https://json.schemastore.org/swagger-2.0',
                    type: 'object',
                    properties: {
                        responseValue: {
                            $ref: '#/definitions/jsonReference',
                        },
                    },
                    definitions: {
                        jsonReference: {
                            type: 'object',
                            required: ['$ref'],
                            properties: {
                                $ref: {
                                    type: 'string',
                                },
                            },
                        },
                    },
                },
            },
        });
        service
            .getResolvedSchema('https://json.schemastore.org/swagger-2.0')
            .then((fs) => {
            assert.deepEqual(fs.schema.properties['responseValue'], {
                type: 'object',
                required: ['$ref'],
                properties: { $ref: { type: 'string' } },
                _$ref: '#/definitions/jsonReference',
            });
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Resolving $refs 3', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://myschemastore/main/schema1.json': {
                    id: 'https://myschemastore/schema1.json',
                    type: 'object',
                    properties: {
                        p1: {
                            $ref: 'schema2.json#/definitions/hello',
                        },
                        p2: {
                            $ref: './schema2.json#/definitions/hello',
                        },
                        p3: {
                            $ref: '/main/schema2.json#/definitions/hello',
                        },
                    },
                },
                'https://myschemastore/main/schema2.json': {
                    id: 'https://myschemastore/main/schema2.json',
                    definitions: {
                        hello: {
                            type: 'string',
                            enum: ['object'],
                        },
                    },
                },
            },
        });
        service
            .getResolvedSchema('https://myschemastore/main/schema1.json')
            .then((fs) => {
            assert.deepEqual(fs.schema.properties['p1'], {
                type: 'string',
                enum: ['object'],
                _$ref: 'schema2.json#/definitions/hello',
                url: 'https://myschemastore/main/schema2.json',
            });
            assert.deepEqual(fs.schema.properties['p2'], {
                type: 'string',
                enum: ['object'],
                _$ref: './schema2.json#/definitions/hello',
                url: 'https://myschemastore/main/schema2.json',
            });
            assert.deepEqual(fs.schema.properties['p3'], {
                type: 'string',
                enum: ['object'],
                _$ref: '/main/schema2.json#/definitions/hello',
                url: 'https://myschemastore/main/schema2.json',
            });
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('FileSchema', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                main: {
                    id: 'main',
                    type: 'object',
                    properties: {
                        child: {
                            type: 'object',
                            properties: {
                                grandchild: {
                                    type: 'number',
                                    description: 'Meaning of Life',
                                },
                            },
                        },
                    },
                },
            },
        });
        service
            .getResolvedSchema('main')
            .then((fs) => {
            const section = fs.getSection(['child', 'grandchild']);
            assert.equal(section.description, 'Meaning of Life');
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Array FileSchema', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                main: {
                    id: 'main',
                    type: 'object',
                    properties: {
                        child: {
                            type: 'array',
                            items: {
                                type: 'object',
                                properties: {
                                    grandchild: {
                                        type: 'number',
                                        description: 'Meaning of Life',
                                    },
                                },
                            },
                        },
                    },
                },
            },
        });
        service
            .getResolvedSchema('main')
            .then((fs) => {
            const section = fs.getSection(['child', '0', 'grandchild']);
            assert.equal(section.description, 'Meaning of Life');
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Missing subschema', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                main: {
                    id: 'main',
                    type: 'object',
                    properties: {
                        child: {
                            type: 'object',
                        },
                    },
                },
            },
        });
        service
            .getResolvedSchema('main')
            .then((fs) => {
            const section = fs.getSection(['child', 'grandchild']);
            assert.strictEqual(section, undefined);
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Preloaded Schema', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        const id = 'https://myschemastore/test1';
        const schema = {
            type: 'object',
            properties: {
                child: {
                    type: 'object',
                    properties: {
                        grandchild: {
                            type: 'number',
                            description: 'Meaning of Life',
                        },
                    },
                },
            },
        };
        service.registerExternalSchema(id, ['*.json'], schema);
        service
            .getSchemaForResource('test.json', undefined)
            .then((schema) => {
            const section = schema.getSection(['child', 'grandchild']);
            assert.equal(section.description, 'Meaning of Life');
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Schema has url', async () => {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        const id = 'https://myschemastore/test1';
        const schema = {
            type: 'object',
            properties: {
                child: {
                    type: 'object',
                    properties: {
                        grandchild: {
                            type: 'number',
                            description: 'Meaning of Life',
                        },
                    },
                },
            },
        };
        service.registerExternalSchema(id, ['*.json'], schema);
        const result = await service.getSchemaForResource('test.json', undefined);
        (0, chai_1.expect)(result.schema.url).equal(id);
    });
    it('Null Schema', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service
            .getSchemaForResource('test.json', undefined)
            .then((schema) => {
            assert.equal(schema, null);
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Schema not found', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service
            .loadSchema('test.json')
            .then((schema) => {
            assert.notEqual(schema.errors.length, 0);
        })
            .then(() => {
            return testDone();
        }, (error) => {
            testDone(error);
        });
    });
    it('Schema with non uri registers correctly', function (testDone) {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        const non_uri = 'non_uri';
        service.registerExternalSchema(non_uri, ['*.yml', '*.yaml'], {
            properties: {
                test_node: {
                    description: 'my test_node description',
                    enum: ['test 1', 'test 2'],
                },
            },
        });
        service.getResolvedSchema(non_uri).then((schema) => {
            assert.notEqual(schema, undefined);
            testDone();
        });
    });
    it('Modifying schema', async () => {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://myschemastore/main/schema1.json': {
                    type: 'object',
                    properties: {
                        apiVersion: {
                            type: 'string',
                            enum: ['v1'],
                        },
                        kind: {
                            type: 'string',
                            enum: ['Pod'],
                        },
                    },
                },
            },
        });
        await service.addContent({
            action: yamlSchemaService_1.MODIFICATION_ACTIONS.add,
            path: 'properties/apiVersion',
            key: 'enum',
            content: ['v2', 'v3'],
            schema: 'https://myschemastore/main/schema1.json',
        });
        const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json');
        assert.deepEqual(fs.schema.properties['apiVersion'], {
            type: 'string',
            enum: ['v2', 'v3'],
        });
        assert.deepEqual(fs.schema.properties['kind'], {
            type: 'string',
            enum: ['Pod'],
        });
    });
    it('Deleting schema', async () => {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://myschemastore/main/schema1.json': {
                    type: 'object',
                    properties: {
                        apiVersion: {
                            type: 'string',
                            enum: ['v1'],
                        },
                        kind: {
                            type: 'string',
                            enum: ['Pod'],
                        },
                    },
                },
            },
        });
        await service.deleteContent({
            action: yamlSchemaService_1.MODIFICATION_ACTIONS.delete,
            path: 'properties',
            key: 'apiVersion',
            schema: 'https://myschemastore/main/schema1.json',
        });
        const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json');
        assert.notDeepEqual(fs.schema.properties['apiVersion'], {
            type: 'string',
            enum: ['v2', 'v3'],
        });
        assert.equal(fs.schema.properties['apiVersion'], undefined);
        assert.deepEqual(fs.schema.properties['kind'], {
            type: 'string',
            enum: ['Pod'],
        });
    });
    it('Deleting schemas', async () => {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://myschemastore/main/schema1.json': {
                    type: 'object',
                },
            },
        });
        await service.deleteSchemas({
            action: yamlSchemaService_1.MODIFICATION_ACTIONS.deleteAll,
            schemas: ['https://myschemastore/main/schema1.json'],
        });
        const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json');
        assert.equal(fs, undefined);
    });
    it('Modifying schema works with kubernetes resolution', async () => {
        const service = new SchemaService.YAMLSchemaService(schemaRequestServiceForURL, workspaceContext);
        service.registerExternalSchema(schemaUrls_1.KUBERNETES_SCHEMA_URL);
        await service.addContent({
            action: yamlSchemaService_1.MODIFICATION_ACTIONS.add,
            path: '/oneOf/1/properties',
            key: 'foobar',
            content: ['hello', 'world'],
            schema: schemaUrls_1.KUBERNETES_SCHEMA_URL,
        });
        const fs = await service.getResolvedSchema(schemaUrls_1.KUBERNETES_SCHEMA_URL);
        assert.deepEqual(fs.schema.oneOf[1].properties['foobar'], ['hello', 'world']);
    });
    it('Deleting schema works with Kubernetes resolution', async () => {
        const service = new SchemaService.YAMLSchemaService(schemaRequestServiceForURL, workspaceContext);
        service.registerExternalSchema(schemaUrls_1.KUBERNETES_SCHEMA_URL);
        await service.deleteContent({
            action: yamlSchemaService_1.MODIFICATION_ACTIONS.delete,
            path: 'oneOf/1',
            key: 'properties',
            schema: schemaUrls_1.KUBERNETES_SCHEMA_URL,
        });
        const fs = await service.getResolvedSchema(schemaUrls_1.KUBERNETES_SCHEMA_URL);
        assert.equal(fs.schema.oneOf[1].properties, undefined);
    });
    it('Adding a brand new schema', async () => {
        const service = new SchemaService.YAMLSchemaService(schemaRequestServiceForURL, workspaceContext);
        service.saveSchema('hello_world', {
            enum: ['test1', 'test2'],
        });
        const hello_world_schema = await service.getResolvedSchema('hello_world');
        assert.deepEqual(hello_world_schema.schema.enum, ['test1', 'test2']);
    });
    it('Deleting an existing schema', async () => {
        const service = new SchemaService.YAMLSchemaService(schemaRequestServiceForURL, workspaceContext);
        service.saveSchema('hello_world', {
            enum: ['test1', 'test2'],
        });
        await service.deleteSchema('hello_world');
        const hello_world_schema = await service.getResolvedSchema('hello_world');
        assert.equal(hello_world_schema, null);
    });
    describe('Test schema priority', function () {
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        const schemaAssociationSample = require(path.join(__dirname, './fixtures/sample-association.json'));
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        const schemaStoreSample = require(path.join(__dirname, './fixtures/sample-schemastore.json'));
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        const schemaSettingsSample = require(path.join(__dirname, './fixtures/sample-settings.json'));
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        const schemaModelineSample = path.join(__dirname, './fixtures/sample-modeline.json');
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        const schemaDefaultSnippetSample = require(path.join(__dirname, './fixtures/defaultSnippets-const-if-else.json'));
        const languageSettingsSetup = new serviceSetup_1.ServiceSetup().withCompletion();
        it('Modeline Schema takes precendence over all other schema APIs', async () => {
            languageSettingsSetup
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaStore,
                schema: schemaStoreSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaAssociation,
                schema: schemaAssociationSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.Settings,
                schema: schemaSettingsSample,
            });
            languageService.configure(languageSettingsSetup.languageSettings);
            languageService.registerCustomSchemaProvider((uri) => Promise.resolve(uri));
            const testTextDocument = (0, testHelper_1.setupTextDocument)(`# yaml-language-server: $schema=${schemaModelineSample}\n\n`);
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(1, 0), false);
            assert.strictEqual(result.items.length, 1);
            assert.strictEqual(result.items[0].label, 'modeline');
        });
        it('Manually setting schema takes precendence over all other lower priority schemas', async () => {
            languageSettingsSetup
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaStore,
                schema: schemaStoreSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaAssociation,
                schema: schemaAssociationSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.Settings,
                schema: schemaSettingsSample,
            });
            languageService.configure(languageSettingsSetup.languageSettings);
            const testTextDocument = (0, testHelper_1.setupTextDocument)('');
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(0, 0), false);
            assert.strictEqual(result.items.length, 1);
            assert.strictEqual(result.items[0].label, 'settings');
        });
        it('SchemaAssociation takes precendence over SchemaStore', async () => {
            languageSettingsSetup
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaStore,
                schema: schemaStoreSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaAssociation,
                schema: schemaAssociationSample,
            });
            languageService.configure(languageSettingsSetup.languageSettings);
            const testTextDocument = (0, testHelper_1.setupTextDocument)('');
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(0, 0), false);
            assert.strictEqual(result.items.length, 1);
            assert.strictEqual(result.items[0].label, 'association');
        });
        it('SchemaStore is highest priority if nothing else is available', async () => {
            languageSettingsSetup.withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaStore,
                schema: schemaStoreSample,
            });
            languageService.configure(languageSettingsSetup.languageSettings);
            const testTextDocument = (0, testHelper_1.setupTextDocument)('');
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(0, 0), false);
            assert.strictEqual(result.items.length, 1);
            assert.strictEqual(result.items[0].label, 'schemastore');
        });
        it('Default snippet with description', async () => {
            languageSettingsSetup.withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaStore,
                schema: schemaDefaultSnippetSample,
            });
            languageService.configure(languageSettingsSetup.languageSettings);
            const testTextDocument = (0, testHelper_1.setupTextDocument)('foo:  ');
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(0, 5), false);
            assert.strictEqual(result.items.length, 2);
            assert.notStrictEqual(result.items[0].documentation, undefined);
            assert.notStrictEqual(result.items[1].documentation, undefined);
            assert.strictEqual(result.items[0].documentation.value, '# FooBar\n```Foo Bar```');
            assert.strictEqual(result.items[1].documentation.value, '# FooBaz\n```Foo Baz```');
        });
    });
    describe('Test getSchemaFromModeline', function () {
        it('simple case', async () => {
            checkReturnSchemaUrl('# yaml-language-server: $schema=expectedUrl', 'expectedUrl');
        });
        it('with several spaces between # and yaml-language-server', async () => {
            checkReturnSchemaUrl('#    yaml-language-server: $schema=expectedUrl', 'expectedUrl');
        });
        it('with several spaces between yaml-language-server and :', async () => {
            checkReturnSchemaUrl('# yaml-language-server   : $schema=expectedUrl', 'expectedUrl');
        });
        it('with several spaces between : and $schema', async () => {
            checkReturnSchemaUrl('# yaml-language-server:    $schema=expectedUrl', 'expectedUrl');
        });
        it('with several spaces at the end', async () => {
            checkReturnSchemaUrl('# yaml-language-server: $schema=expectedUrl   ', 'expectedUrl');
        });
        it('with several spaces at several places', async () => {
            checkReturnSchemaUrl('#   yaml-language-server  :   $schema=expectedUrl   ', 'expectedUrl');
        });
        it('with several attributes', async () => {
            checkReturnSchemaUrl('# yaml-language-server: anotherAttribute=test $schema=expectedUrl aSecondAttribtute=avalue', 'expectedUrl');
        });
        it('with tabs', async () => {
            checkReturnSchemaUrl('#\tyaml-language-server:\t$schema=expectedUrl', 'expectedUrl');
        });
        it('with several $schema - pick the first', async () => {
            checkReturnSchemaUrl('# yaml-language-server: $schema=url1 $schema=url2', 'url1');
        });
        it('no schema returned if not yaml-language-server', async () => {
            checkReturnSchemaUrl('# somethingelse: $schema=url1', undefined);
        });
        it('no schema returned if not $schema', async () => {
            checkReturnSchemaUrl('# yaml-language-server: $notschema=url1', undefined);
        });
        function checkReturnSchemaUrl(modeline, expectedResult) {
            const yamlDoc = new parser.SingleYAMLDocument(new yaml_1.LineCounter());
            yamlDoc.lineComments = [modeline];
            assert.strictEqual((0, modelineUtil_1.getSchemaFromModeline)(yamlDoc), expectedResult);
        }
    });
});
//# sourceMappingURL=schema.test.js.map