"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const parser = __importStar(require("../src/languageservice/parser/yamlParser07"));
const SchemaService = __importStar(require("../src/languageservice/services/yamlSchemaService"));
const url = __importStar(require("url"));
const path = __importStar(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 yamlSettings_1 = require("../src/yamlSettings");
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const modelineUtil_1 = require("../src/languageservice/services/modelineUtil");
const k8sSchemaUtil_1 = require("../src/languageservice/services/k8sSchemaUtil");
const KUBERNETES_SCHEMA_URL = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/${schemaUrls_1.DEFAULT_KUBERNETES_SCHEMA_VERSION}-standalone-strict/all.json`;
const requestServiceMock = function (uri) {
    return Promise.reject(`Resource ${uri} not found.`);
};
const workspaceContext = {
    resolveRelativePath: (relativePath, resource) => {
        return url.resolve(resource, relativePath);
    },
};
const schemaRequestServiceForURL = async (uri) => {
    const headers = { 'Accept-Encoding': 'gzip, deflate' };
    try {
        const response = await (0, request_light_1.xhr)({ url: uri, followRedirects: 5, headers });
        return response.responseText;
    }
    catch (error) {
        throw error.responseText || error.toString();
    }
};
describe('JSON Schema', () => {
    let languageSettingsSetup;
    let languageService;
    beforeEach(() => {
        languageSettingsSetup = new serviceSetup_1.ServiceSetup()
            .withValidate()
            .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://json.schemastore.org/composer', fileMatch: ['test.yml'] });
        const { languageService: langService } = (0, testHelper_1.setupLanguageService)(languageSettingsSetup.languageSettings);
        languageService = langService;
    });
    it('Resolving $refs', async function () {
        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',
                },
            },
        });
        const solvedSchema = await service.getResolvedSchema('https://myschemastore/main');
        assert_1.default.deepEqual(solvedSchema.schema.properties['child'], {
            id: 'https://myschemastore/child',
            type: 'bool',
            description: 'Test description',
            _$ref: 'https://myschemastore/child',
            url: 'https://myschemastore/child',
            _baseUri: 'https://myschemastore/child',
            _sourceUri: 'https://myschemastore/child',
        });
    });
    it('Resolving $refs 2', async function () {
        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',
                                },
                            },
                        },
                    },
                },
            },
        });
        const fs = await service.getResolvedSchema('https://json.schemastore.org/swagger-2.0');
        assert_1.default.deepEqual(fs.schema.properties['responseValue'], {
            type: 'object',
            required: ['$ref'],
            properties: {
                $ref: {
                    type: 'string',
                    _sourceUri: 'https://json.schemastore.org/swagger-2.0',
                },
            },
            _$ref: '#/definitions/jsonReference',
            _sourceUri: 'https://json.schemastore.org/swagger-2.0',
        });
    });
    it('Resolving $refs 3', async function () {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://myschemastore/main/schema1.json': {
                    id: 'https://myschemastore/main/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'],
                        },
                    },
                },
            },
        });
        const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json');
        assert_1.default.deepEqual(fs.schema.properties['p1'], {
            type: 'string',
            enum: ['object'],
            _$ref: 'schema2.json#/definitions/hello',
            _baseUri: 'https://myschemastore/main/schema2.json',
            _sourceUri: 'https://myschemastore/main/schema2.json',
            url: 'https://myschemastore/main/schema2.json',
        });
        assert_1.default.deepEqual(fs.schema.properties['p2'], {
            type: 'string',
            enum: ['object'],
            _$ref: './schema2.json#/definitions/hello',
            _baseUri: 'https://myschemastore/main/schema2.json',
            _sourceUri: 'https://myschemastore/main/schema2.json',
            url: 'https://myschemastore/main/schema2.json',
        });
        assert_1.default.deepEqual(fs.schema.properties['p3'], {
            type: 'string',
            enum: ['object'],
            _$ref: '/main/schema2.json#/definitions/hello',
            _baseUri: 'https://myschemastore/main/schema2.json',
            _sourceUri: 'https://myschemastore/main/schema2.json',
            url: 'https://myschemastore/main/schema2.json',
        });
    });
    it('Resolving absolute-path $refs without top-level id', async function () {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'file:///main/schema.json': {
                    type: 'object',
                    properties: {
                        name: {
                            $ref: '/main/schema2.json',
                        },
                    },
                    required: ['name'],
                },
                'file:///main/schema2.json': {
                    type: 'string',
                    enum: ['alice', 'bob'],
                    description: 'Allowed names.',
                },
            },
        });
        const fs = await service.getResolvedSchema('file:///main/schema.json');
        assert_1.default.deepEqual(fs.errors, []);
        assert_1.default.deepEqual(fs.schema.properties['name'], {
            type: 'string',
            enum: ['alice', 'bob'],
            description: 'Allowed names.',
            _$ref: '/main/schema2.json',
            _baseUri: 'file:///main/schema2.json',
            _sourceUri: 'file:///main/schema2.json',
            url: 'file:///main/schema2.json',
        });
    });
    it('Preserves markdownDescription on $ref siblings', async function () {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                'https://myschemastore/main/schema.json': {
                    $schema: 'http://json-schema.org/draft-07/schema#',
                    id: 'https://myschemastore/main/schema.json',
                    type: 'object',
                    properties: {
                        bar: {
                            $ref: '#/$defs/veggie',
                            title: 'bar title',
                            description: 'bar desc',
                            markdownDescription: 'bar md desc **bold** \n * line \n* another \n\n',
                        },
                    },
                    $defs: {
                        veggie: {
                            type: 'string',
                            enum: ['potato', 'carrot'],
                        },
                    },
                },
            },
        });
        const resolvedSchema = await service.getResolvedSchema('https://myschemastore/main/schema.json');
        const barSchema = resolvedSchema.schema.properties['bar'];
        assert_1.default.strictEqual(barSchema.description, 'bar desc');
        assert_1.default.strictEqual(barSchema.markdownDescription, 'bar md desc **bold** \n * line \n* another \n\n');
        assert_1.default.deepStrictEqual(barSchema.enum, ['potato', 'carrot']);
    });
    describe('Compound Schema Documents', () => {
        let validationHandler;
        let yamlSettings;
        let schemaProvider;
        before(() => {
            const languageSettingsSetup = new serviceSetup_1.ServiceSetup().withValidate();
            const { validationHandler: valHandler, yamlSettings: settings, schemaProvider: provider, } = (0, testHelper_1.setupLanguageService)(languageSettingsSetup.languageSettings);
            validationHandler = valHandler;
            yamlSettings = settings;
            schemaProvider = provider;
        });
        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);
        }
        describe('embedded resources', () => {
            const ROOT_URI = 'https://example.com/schema/customer';
            const rootSchema = {
                $schema: 'https://json-schema.org/draft/2020-12/schema',
                $id: 'https://example.com/schema/customer',
                type: 'object',
                properties: {
                    name: { type: 'string' },
                    phone: { $ref: '/schema/common#/$defs/phone' },
                    address: { $ref: '/schema/address' },
                },
                required: ['name', 'phone', 'address'],
                additionalProperties: false,
                $defs: {
                    'https://example.com/schema/address': {
                        $id: 'https://example.com/schema/address',
                        type: 'object',
                        properties: {
                            address: { type: 'string' },
                            city: { type: 'string' },
                            postalCode: { $ref: '/schema/common#USZip' },
                            state: { $ref: '#/$defs/states' },
                        },
                        required: ['address', 'city', 'postalCode', 'state'],
                        additionalProperties: false,
                        $defs: {
                            states: {
                                enum: ['CA', 'NY'],
                            },
                        },
                    },
                    'https://example.com/schema/common': {
                        $schema: 'https://json-schema.org/draft/2019-09/schema',
                        $id: 'https://example.com/schema/common',
                        $defs: {
                            phone: {
                                type: 'string',
                                pattern: '^[+]?[(]?[0-9]{3}[)]?[-s.]?[0-9]{3}[-s.]?[0-9]{4,6}$',
                            },
                            usaPostalCode: {
                                $anchor: 'USZip',
                                type: 'string',
                                pattern: '^[0-9]{5}(?:-[0-9]{4})?$',
                            },
                            unsignedInt: {
                                type: 'integer',
                                minimum: 0,
                            },
                        },
                    },
                },
            };
            beforeEach(() => {
                schemaProvider.addSchemaWithUri(ROOT_URI, ROOT_URI, rootSchema);
            });
            afterEach(() => {
                schemaProvider.deleteSchema(ROOT_URI);
            });
            it('accepts valid instances that reference embedded resources', async () => {
                const content = `name: "Customer1"
phone: "123-123-1234"
address:
  address: "123 King St"
  city: "San Francisco"
  postalCode: "12345-6789"
  state: "CA"
`;
                const result = await parseSetup(content, ROOT_URI);
                (0, chai_1.expect)(result).to.be.empty;
            });
            it('reports validation errors across embedded resources', async () => {
                const content = `name: 123
phone: "not a phone"
address:
  address: "123 King St"
  city: "Toronto"
  postalCode: "ABCDE"
  state: "ZZ"
`;
                const result = await parseSetup(content, ROOT_URI);
                (0, chai_1.expect)(result).to.have.length(4);
                (0, chai_1.expect)(result[0].message).to.include('Incorrect type.');
                (0, chai_1.expect)(result[0].message).to.include('string');
                (0, chai_1.expect)(result[1].message).to.include('String does not match the pattern');
                (0, chai_1.expect)(result[2].message).to.include('String does not match the pattern');
                (0, chai_1.expect)(result[3].message).to.include('Value is not accepted. Valid values:');
                (0, chai_1.expect)(result[3].message).to.include('CA');
                (0, chai_1.expect)(result[3].message).to.include('NY');
            });
        });
        describe('cross-dialect behavior', () => {
            describe('ref sibling semantics across dialects', () => {
                const ROOT_URI = 'https://example.com/schema/root';
                describe('draft-07 root with draft-2019-09 embedded resource', () => {
                    const rootSchema = {
                        $schema: 'http://json-schema.org/draft-07/schema#',
                        $id: 'https://example.com/schema/root',
                        type: 'object',
                        properties: {
                            code: { $ref: '/schema/embedded' },
                        },
                        required: ['code'],
                        additionalProperties: false,
                        $defs: {
                            'https://example.com/schema/embedded': {
                                $schema: 'https://json-schema.org/draft/2019-09/schema',
                                $id: 'https://example.com/schema/embedded',
                                $defs: {
                                    BaseString: { type: 'string' },
                                },
                                allOf: [
                                    {
                                        $ref: '#/$defs/BaseString',
                                        minLength: 5,
                                        pattern: '^foo',
                                    },
                                ],
                            },
                        },
                    };
                    beforeEach(() => {
                        schemaProvider.addSchemaWithUri(ROOT_URI, ROOT_URI, rootSchema);
                    });
                    afterEach(() => {
                        schemaProvider.deleteSchema(ROOT_URI);
                    });
                    it('applies $ref sibling constraints inside embedded 2019-09 resource', async () => {
                        const content = `code: "bar"`;
                        const result = await parseSetup(content, ROOT_URI);
                        (0, chai_1.expect)(result).to.have.length(2);
                        (0, chai_1.expect)(result[0].message).to.include('String is shorter than the minimum length of 5.');
                        (0, chai_1.expect)(result[1].message).to.include('String does not match the pattern');
                    });
                    it('accepts value that satisfies embedded 2019-09 constraints', async () => {
                        const content = `code: "foobar"`;
                        const result = await parseSetup(content, ROOT_URI);
                        (0, chai_1.expect)(result).to.be.empty;
                    });
                });
                describe('draft-2019-09 root with draft-07 embedded resource', () => {
                    const rootSchema = {
                        $schema: 'https://json-schema.org/draft/2019-09/schema',
                        $id: 'https://example.com/schema/root',
                        type: 'object',
                        properties: {
                            code: { $ref: '/schema/embedded' },
                        },
                        required: ['code'],
                        additionalProperties: false,
                        $defs: {
                            'https://example.com/schema/embedded': {
                                $schema: 'http://json-schema.org/draft-07/schema#',
                                $id: 'https://example.com/schema/embedded',
                                $defs: {
                                    BaseString: { type: 'string' },
                                },
                                allOf: [
                                    {
                                        $ref: '#/$defs/BaseString',
                                        minLength: 999,
                                        pattern: '^SHOULD_NOT_APPLY$',
                                    },
                                ],
                            },
                        },
                    };
                    beforeEach(() => {
                        schemaProvider.addSchemaWithUri(ROOT_URI, ROOT_URI, rootSchema);
                    });
                    afterEach(() => {
                        schemaProvider.deleteSchema(ROOT_URI);
                    });
                    it('ignores $ref sibling constraints inside embedded draft-07 resource', async () => {
                        const content = `code: "bar"`;
                        const result = await parseSetup(content, ROOT_URI);
                        (0, chai_1.expect)(result).to.be.empty;
                    });
                    it('still enforces the referenced target schema (type string)', async () => {
                        const content = `code: 123`;
                        const result = await parseSetup(content, ROOT_URI);
                        (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');
                    });
                });
            });
            describe('meta validation for mixed-dialect subschemas', () => {
                afterEach(() => {
                    schemaProvider.deleteSchema(testHelper_1.SCHEMA_ID);
                });
                it('draft-2020 root with draft-04 subschema', async () => {
                    const schema = {
                        $schema: 'https://json-schema.org/draft/2020-12/schema',
                        type: 'object',
                        properties: {
                            age: {
                                allOf: [
                                    {
                                        $schema: 'http://json-schema.org/draft-04/schema#',
                                        type: 'number',
                                        minimum: 0,
                                        exclusiveMinimum: 0,
                                    },
                                ],
                            },
                        },
                    };
                    schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
                    const failResult = await parseSetup('age: 0');
                    (0, chai_1.expect)(failResult).to.have.length(1);
                    (0, chai_1.expect)(failResult[0].message).to.include('is not valid:');
                    (0, chai_1.expect)(failResult[0].message).to.include(testHelper_1.SCHEMA_ID);
                    (0, chai_1.expect)(failResult[0].message).to.include('exclusiveMinimum : must be boolean');
                });
                it('draft-2020 root with draft-07 subschema', async () => {
                    const schema = {
                        $schema: 'https://json-schema.org/draft/2020-12/schema',
                        type: 'object',
                        properties: {
                            score: {
                                anyOf: [
                                    {
                                        $schema: 'http://json-schema.org/draft-07/schema#',
                                        type: 'number',
                                        exclusiveMinimum: true,
                                    },
                                ],
                            },
                        },
                    };
                    schemaProvider.addSchema(testHelper_1.SCHEMA_ID, schema);
                    const failResult = await parseSetup('score: 0');
                    (0, chai_1.expect)(failResult).to.have.length(1);
                    (0, chai_1.expect)(failResult[0].message).to.include('is not valid:');
                    (0, chai_1.expect)(failResult[0].message).to.include(testHelper_1.SCHEMA_ID);
                    (0, chai_1.expect)(failResult[0].message).to.include('exclusiveMinimum : must be number');
                });
            });
        });
    });
    it('FileSchema', async function () {
        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',
                                },
                            },
                        },
                    },
                },
            },
        });
        const fs = await service.getResolvedSchema('main');
        const section = fs.getSection(['child', 'grandchild']);
        assert_1.default.equal(section.description, 'Meaning of Life');
    });
    it('Array FileSchema', async function () {
        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',
                                    },
                                },
                            },
                        },
                    },
                },
            },
        });
        const fs = await service.getResolvedSchema('main');
        const section = fs.getSection(['child', '0', 'grandchild']);
        assert_1.default.equal(section.description, 'Meaning of Life');
    });
    it('Missing subschema', async function () {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        service.setSchemaContributions({
            schemas: {
                main: {
                    id: 'main',
                    type: 'object',
                    properties: {
                        child: {
                            type: 'object',
                        },
                    },
                },
            },
        });
        const fs = await service.getResolvedSchema('main');
        const section = fs.getSection(['child', 'grandchild']);
        assert_1.default.strictEqual(section, undefined);
    });
    it('Preloaded Schema', async function () {
        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 fs = await service.getSchemaForResource('test.json', undefined);
        const section = fs.getSection(['child', 'grandchild']);
        assert_1.default.equal(section.description, 'Meaning of Life');
    });
    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', async function () {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        const schema = await service.getSchemaForResource('test.json', undefined);
        assert_1.default.equal(schema, null);
    });
    it('Schema not found', async function () {
        const service = new SchemaService.YAMLSchemaService(requestServiceMock, workspaceContext);
        const schema = await service.loadSchema('test.json');
        assert_1.default.notEqual(schema.errors.length, 0);
    });
    it('Schema with non uri registers correctly', async function () {
        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'],
                },
            },
        });
        const schema = await service.getResolvedSchema(non_uri);
        assert_1.default.notEqual(schema, undefined);
    });
    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_1.default.deepEqual(fs.schema.properties['apiVersion'], {
            type: 'string',
            enum: ['v2', 'v3'],
            _sourceUri: 'https://myschemastore/main/schema1.json',
        });
        assert_1.default.deepEqual(fs.schema.properties['kind'], {
            type: 'string',
            enum: ['Pod'],
            _sourceUri: 'https://myschemastore/main/schema1.json',
        });
    });
    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_1.default.notDeepEqual(fs.schema.properties['apiVersion'], {
            type: 'string',
            enum: ['v2', 'v3'],
        });
        assert_1.default.equal(fs.schema.properties['apiVersion'], undefined);
        assert_1.default.deepEqual(fs.schema.properties['kind'], {
            type: 'string',
            enum: ['Pod'],
            _sourceUri: 'https://myschemastore/main/schema1.json',
        });
    });
    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_1.default.equal(fs, undefined);
    });
    it('Modifying schema works with kubernetes resolution', async () => {
        const service = new SchemaService.YAMLSchemaService(schemaRequestServiceForURL, workspaceContext);
        service.registerExternalSchema(KUBERNETES_SCHEMA_URL);
        await service.addContent({
            action: yamlSchemaService_1.MODIFICATION_ACTIONS.add,
            path: '/oneOf/1/properties',
            key: 'foobar',
            content: ['hello', 'world'],
            schema: KUBERNETES_SCHEMA_URL,
        });
        const fs = await service.getResolvedSchema(KUBERNETES_SCHEMA_URL);
        assert_1.default.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(KUBERNETES_SCHEMA_URL);
        await service.deleteContent({
            action: yamlSchemaService_1.MODIFICATION_ACTIONS.delete,
            path: 'oneOf/1',
            key: 'properties',
            schema: KUBERNETES_SCHEMA_URL,
        });
        const fs = await service.getResolvedSchema(KUBERNETES_SCHEMA_URL);
        assert_1.default.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_1.default.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_1.default.equal(hello_world_schema, null);
    });
    describe('Test schema priority', function () {
        // eslint-disable-next-line @typescript-eslint/no-require-imports
        const schemaAssociationSample = require(path.join(__dirname, './fixtures/sample-association.json'));
        // eslint-disable-next-line @typescript-eslint/no-require-imports
        const schemaStoreSample = require(path.join(__dirname, './fixtures/sample-schemastore.json'));
        // eslint-disable-next-line @typescript-eslint/no-require-imports
        const schemaSettingsSample = require(path.join(__dirname, './fixtures/sample-settings.json'));
        const schemaModelineSample = path.join(__dirname, './fixtures/sample-modeline.json');
        const schemaDollarSample = path.join(__dirname, './fixtures/sample-dollar-schema.json');
        // eslint-disable-next-line @typescript-eslint/no-require-imports
        const schemaDefaultSnippetSample = require(path.join(__dirname, './fixtures/defaultSnippets-const-if-else.json'));
        let languageSettingsSetup;
        beforeEach(() => {
            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,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: schemaUrls_1.EMPTY_SCHEMA_URL,
                priority: src_1.SchemaPriority.SchemaDetectionDisabled,
                schema: true,
            });
            languageService.configure(languageSettingsSetup.languageSettings);
            languageService.registerCustomSchemaProvider((uri) => Promise.resolve(uri));
            const testTextDocument = (0, testHelper_1.setupTextDocument)(`# yaml-language-server: $schema=${schemaModelineSample}\n$schema: ${schemaDollarSample}\n\n`);
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(1, 0), false);
            assert_1.default.strictEqual(result.items.length, 1);
            assert_1.default.strictEqual(result.items[0].label, 'modeline');
        });
        it('Explicit $schema takes precedence 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);
            languageService.registerCustomSchemaProvider((uri) => Promise.resolve(uri));
            const testTextDocument = (0, testHelper_1.setupTextDocument)(`$schema: ${schemaDollarSample}\n\n`);
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(1, 0), false);
            assert_1.default.strictEqual(result.items.length, 1);
            assert_1.default.strictEqual(result.items[0].label, 'dollar-schema');
        });
        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_1.default.strictEqual(result.items.length, 1);
            assert_1.default.strictEqual(result.items[0].label, 'settings');
        });
        it('SchemaDetectionDisabled takes precedence over manually configured schemas', async () => {
            languageSettingsSetup
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.Settings,
                schema: schemaSettingsSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: schemaUrls_1.EMPTY_SCHEMA_URL,
                priority: src_1.SchemaPriority.SchemaDetectionDisabled,
                schema: true,
            });
            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_1.default.strictEqual(result.items.length, 0);
        });
        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_1.default.strictEqual(result.items.length, 1);
            assert_1.default.strictEqual(result.items[0].label, 'association');
        });
        it('SchemaDetectionDisabled takes precedence over SchemaAssociation', async () => {
            const schemaDetectionDisabledSetup = new serviceSetup_1.ServiceSetup()
                .withCompletion()
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaAssociation,
                schema: schemaAssociationSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: schemaUrls_1.EMPTY_SCHEMA_URL,
                priority: src_1.SchemaPriority.SchemaDetectionDisabled,
                schema: true,
            });
            languageService.configure(schemaDetectionDisabledSetup.languageSettings);
            const testTextDocument = (0, testHelper_1.setupTextDocument)('');
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(0, 0), false);
            assert_1.default.strictEqual(result.items.length, 0);
        });
        it('SchemaDetectionDisabled takes precedence over SchemaStore', async () => {
            const schemaDetectionDisabledSetup = new serviceSetup_1.ServiceSetup()
                .withCompletion()
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: testHelper_1.TEST_URI,
                priority: src_1.SchemaPriority.SchemaStore,
                schema: schemaStoreSample,
            })
                .withSchemaFileMatch({
                fileMatch: ['test.yaml'],
                uri: schemaUrls_1.EMPTY_SCHEMA_URL,
                priority: src_1.SchemaPriority.SchemaDetectionDisabled,
                schema: true,
            });
            languageService.configure(schemaDetectionDisabledSetup.languageSettings);
            const testTextDocument = (0, testHelper_1.setupTextDocument)('');
            const result = await languageService.doComplete(testTextDocument, vscode_languageserver_types_1.Position.create(0, 0), false);
            assert_1.default.strictEqual(result.items.length, 0);
        });
        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_1.default.strictEqual(result.items.length, 1);
            assert_1.default.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_1.default.strictEqual(result.items.length, 2);
            assert_1.default.notStrictEqual(result.items[0].documentation, undefined);
            assert_1.default.notStrictEqual(result.items[1].documentation, undefined);
            assert_1.default.strictEqual(result.items[0].documentation.value, '# FooBar\n```Foo Bar```');
            assert_1.default.strictEqual(result.items[1].documentation.value, '# FooBaz\n```Foo Baz```');
        });
    });
    describe('Test getGroupVersionKindFromDocument', function () {
        it('builtin kubernetes resource group should not get resolved', async () => {
            checkReturnGroupVersionKind('apiVersion: v1\nkind: Pod', true, undefined, 'v1', 'Pod');
        });
        it('builtin kubernetes resource with complex apiVersion should get resolved ', async () => {
            checkReturnGroupVersionKind('apiVersion: admissionregistration.k8s.io/v1\nkind: MutatingWebhook', false, 'admissionregistration.k8s.io', 'v1', 'MutatingWebhook');
        });
        it('custom argo application CRD should get resolved', async () => {
            checkReturnGroupVersionKind('apiVersion: argoproj.io/v1alpha1\nkind: Application', false, 'argoproj.io', 'v1alpha1', 'Application');
        });
        it('custom argo application CRD with whitespace should get resolved', async () => {
            checkReturnGroupVersionKind('apiVersion: argoproj.io/v1alpha1\nkind: Application ', false, 'argoproj.io', 'v1alpha1', 'Application');
        });
        it('custom argo application CRD with other fields should get resolved', async () => {
            checkReturnGroupVersionKind('someOtherVal: test\napiVersion: argoproj.io/v1alpha1\nkind: Application\nmetadata:\n  name: my-app', false, 'argoproj.io', 'v1alpha1', 'Application');
        });
        function checkReturnGroupVersionKind(content, error, expectedGroup, expectedVersion, expectedKind) {
            const yamlDoc = parser.parse(content);
            const res = (0, k8sSchemaUtil_1.getGroupVersionKindFromDocument)(yamlDoc.documents[0]);
            if (error) {
                assert_1.default.strictEqual(res, undefined);
            }
            else {
                assert_1.default.strictEqual(res.group, expectedGroup);
                assert_1.default.strictEqual(res.version, expectedVersion);
                assert_1.default.strictEqual(res.kind, expectedKind);
            }
        }
    });
    describe('Test schema validation disabling via modeline', function () {
        it('should not validate when schema is disabled via modeline with $schema=none', async () => {
            const schema = {
                type: 'object',
                properties: {
                    name: {
                        type: 'string',
                    },
                    age: {
                        type: 'number',
                        minimum: 0,
                    },
                },
                required: ['name'],
            };
            const languageSettingsSetup = new serviceSetup_1.ServiceSetup().withValidate().withSchemaFileMatch({
                uri: 'http://test-schema.org/person',
                fileMatch: ['*.yml'],
                schema,
            });
            const { languageService: langService } = (0, testHelper_1.setupLanguageService)(languageSettingsSetup.languageSettings);
            const content = '# yaml-language-server: $schema=none\nage: not-a-number';
            const testTextDocument = (0, testHelper_1.setupTextDocument)(content);
            const result = await langService.doValidation(testTextDocument, false);
            (0, chai_1.expect)(result).to.be.empty;
        });
    });
    describe('Test getSchemaFromModeline', function () {
        it('simple case', async () => {
            checkReturnSchemaUrl('# yaml-language-server: $schema=expectedUrl', 'expectedUrl');
        });
        it('simple $schema case', async () => {
            checkReturnSchemaUrl('# $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);
        });
        it('uses first modeline from initial comment block', async () => {
            const yamlDoc = parser.parse('# first comment\n# yaml-language-server: $schema=expectedUrl\nfoo: bar').documents[0];
            assert_1.default.strictEqual((0, modelineUtil_1.getSchemaFromModeline)(yamlDoc), 'expectedUrl');
        });
        it('uses first modeline when initial comment block has multiple modelines', async () => {
            const yamlDoc = parser.parse('# first comment\n # second comment\n# yaml-language-server: $schema=expectedUrl\n# yaml-language-server: $schema=unexpectedUrl\nfoo: bar').documents[0];
            assert_1.default.strictEqual((0, modelineUtil_1.getSchemaFromModeline)(yamlDoc), 'expectedUrl');
        });
        it('no schema returned if modeline is in the middle of the file after yaml content', async () => {
            checkReturnSchemaUrl('foo: bar\n# yaml-language-server: $schema=unexpectedUrl', undefined);
        });
        it('no schema returned from commented out $schema block after yaml content', async () => {
            checkReturnSchemaUrl(['- title: test', '  output:', '    scores:', '      $lte: 0.3', '     # $schema:', '     #   type: array'].join('\n'), undefined);
        });
        function checkReturnSchemaUrl(modeline, expectedResult, documentIndex) {
            const yamlDoc = parser.parse(modeline).documents[documentIndex ?? 0];
            assert_1.default.strictEqual((0, modelineUtil_1.getSchemaFromModeline)(yamlDoc), expectedResult);
        }
    });
});
//# sourceMappingURL=schema.test.js.map