"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const internal_1 = require("../../../internal");
const Storage_1 = require("../../../Storage");
const AttributeResolutionDirectiveSet_1 = require("../../../Utilities/AttributeResolutionDirectiveSet");
const AttributeContextUtil_1 = require("../Projection/AttributeContextUtil");
const projectionTestUtils_1 = require("../../Utilities/projectionTestUtils");
const testHelper_1 = require("../../testHelper");
const ResolutionTestUtils_1 = require("./ResolutionTestUtils");
/**
 * Tests to verify if entity resolution performs as expected.
 */
// tslint:disable-next-line: max-func-body-length
describe('Cdm/Resolution/EntityResolution', () => {
    const testsSubpath = 'Cdm/Resolution/EntityResolutionTest';
    const schemaDocsRoot = testHelper_1.testHelper.schemaDocumentsPath;
    /**
     * Tests if the owner of the entity is not changed when calling CreatedResolvedEntityAsync
     */
    it('TestOwnerNotChanged', (done) => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestOwnerNotChanged');
        const entity = yield corpus.fetchObjectAsync('local:/Entity.cdm.json/Entity');
        const document = yield corpus.fetchObjectAsync('local:/Entity.cdm.json');
        expect(document)
            .toBe(entity.owner);
        yield entity.createResolvedEntityAsync('res-Entity');
        expect(document)
            .toBe(entity.owner);
        // Test that entity's attribute's owner should have remained unchanged (same as the owning entity)
        expect(entity.attributes.allItems[0].owner)
            .toBe(entity);
        done();
    }));
    /**
     * Test that entity references that do not point to valid entities are reported as an error instead of triggering an exception
     */
    it('TestEntRefNonexistent', () => __awaiter(void 0, void 0, void 0, function* () {
        const expectedLogCodes = new Set([internal_1.cdmLogCode.WarnResolveObjectFailed, internal_1.cdmLogCode.ErrResolveReferenceFailure]);
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestEntRefNonexistent', undefined, false, expectedLogCodes);
        const folder = corpus.storage.namespaceFolders.get('local');
        const doc = new internal_1.CdmDocumentDefinition(corpus.ctx, 'someDoc.cdm.json');
        folder.documents.push(doc);
        const entity = new internal_1.CdmEntityDefinition(corpus.ctx, 'someEntity');
        const entAtt = new internal_1.CdmEntityAttributeDefinition(corpus.ctx, 'entityAtt');
        entAtt.entity = new internal_1.CdmEntityReference(corpus.ctx, 'nonExistingEntity', true);
        entity.attributes.push(entAtt);
        doc.definitions.push(entity);
        const resolvedEnt = yield entity.createResolvedEntityAsync('resolvedSomeEntity');
        expect(resolvedEnt)
            .not.toBeUndefined();
    }));
    /**
     * Tests that resolution runs correctly when resolving a resolved entity
     */
    it('TestResolvingResolvedEntity', () => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestResolvingResolvedEntity');
        const entity = yield corpus.fetchObjectAsync('local:/Entity.cdm.json/Entity');
        const resEntity = yield entity.createResolvedEntityAsync('resEntity');
        const resResEntity = yield resEntity.createResolvedEntityAsync('resResEntity');
        expect(resResEntity)
            .not.toBeUndefined();
        expect(resResEntity.exhibitsTraits.length)
            .toBe(1);
        expect(resResEntity.exhibitsTraits.allItems[0].namedReference)
            .toBe('has.entitySchemaAbstractionLevel');
        expect(resResEntity.exhibitsTraits.allItems[0].arguments.length)
            .toBe(1);
        expect(resResEntity.exhibitsTraits.allItems[0].arguments.allItems[0].value)
            .toBe('resolved');
    }));
    /**
     * Test whether or not the test corpus can be resolved
     */
    it('TestResolveTestCorpus', () => __awaiter(void 0, void 0, void 0, function* () {
        // this test takes more time than jest expects tests to take
        jest.setTimeout(500000);
        // tslint:disable-next-line: non-literal-fs-path
        expect(fs.existsSync(schemaDocsRoot))
            .toBeTruthy();
        const cdmCorpus = new internal_1.CdmCorpusDefinition();
        // Set empty callback to avoid breaking tests due too many errors in logs,
        // change the event callback to console or file status report if wanted.
        // tslint:disable-next-line: no-empty
        cdmCorpus.setEventCallback(() => { }, internal_1.cdmStatusLevel.error);
        const localAdapter = new Storage_1.LocalAdapter(schemaDocsRoot);
        cdmCorpus.storage.mount('local', localAdapter);
        const manifest = yield cdmCorpus.createRootManifest(testHelper_1.testHelper.cdmStandardsSchemaPath);
        expect(manifest)
            .toBeTruthy();
        const directives = new AttributeResolutionDirectiveSet_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'referenceOnly']));
        const allResolved = yield ResolutionTestUtils_1.resolutionTestUtils.listAllResolved(cdmCorpus, directives, manifest, new internal_1.stringSpewCatcher());
        expect(allResolved)
            .toBeDefined();
        expect(allResolved.length)
            .toBeGreaterThanOrEqual(1);
        // setting back expected test timeout.
        jest.setTimeout(10000);
    }));
    /**
     * Test if the composite resolved entities match
     */
    it('TestResolvedComposites', () => __awaiter(void 0, void 0, void 0, function* () {
        yield ResolutionTestUtils_1.resolutionTestUtils.resolveSaveDebuggingFileAndAssert(testsSubpath, 'TestResolvedComposites', 'composites');
    }));
    /**
     * Test if the composite resolved entities match
     */
    it('TestResolvedE2E', () => __awaiter(void 0, void 0, void 0, function* () {
        yield ResolutionTestUtils_1.resolutionTestUtils.resolveSaveDebuggingFileAndAssert(testsSubpath, 'TestResolvedE2E', 'E2EResolution');
    }));
    /**
     * Test if the knowledge graph resolved entities match
     */
    it('TestareResolvedKnowledgeGraph', () => __awaiter(void 0, void 0, void 0, function* () {
        yield ResolutionTestUtils_1.resolutionTestUtils.resolveSaveDebuggingFileAndAssert(testsSubpath, 'TestResolvedKnowledgeGraph', 'KnowledgeGraph');
    }));
    /**
     * Test if the mini dyn resolved entities match
     */
    // it('TestResolvedMiniDyn', async () => {
    //     await resolveSaveDebuggingFileAndAssert('TestResolvedMiniDyn', 'MiniDyn');
    // });
    /**
     * Test if the overrides resolved entities match
     */
    it('TestResolvedOverrides', () => __awaiter(void 0, void 0, void 0, function* () {
        yield ResolutionTestUtils_1.resolutionTestUtils.resolveSaveDebuggingFileAndAssert(testsSubpath, 'TestResolvedOverrides', 'overrides');
    }));
    /**
     * Test if the POVResolution resolved entities match
     */
    it('TestResolvedPovResolution', () => __awaiter(void 0, void 0, void 0, function* () {
        yield ResolutionTestUtils_1.resolutionTestUtils.resolveSaveDebuggingFileAndAssert(testsSubpath, 'TestResolvedPovResolution', 'POVResolution');
    }));
    /**
     * Test if the WebClicks resolved entities match
     */
    it('TestResolvedWebClicks', () => __awaiter(void 0, void 0, void 0, function* () {
        yield ResolutionTestUtils_1.resolutionTestUtils.resolveSaveDebuggingFileAndAssert(testsSubpath, 'TestResolvedWebClicks', 'webClicks');
    }));
    /**
     * Test that monikered references on resolved entities can be resolved correctly, previously
     * the inclusion of the resolvedFrom moniker stopped the source document from being found
     */
    it('TestResolveWithExtended', (done) => __awaiter(void 0, void 0, void 0, function* () {
        const cdmCorpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestResolveWithExtended');
        cdmCorpus.setEventCallback((level, msg) => {
            expect(msg.indexOf('unable to resolve the reference'))
                .toBe(-1);
        }, internal_1.cdmStatusLevel.warning);
        const ent = yield cdmCorpus.fetchObjectAsync('local:/sub/Account.cdm.json/Account');
        yield ent.createResolvedEntityAsync('Account_');
        done();
    }));
    /**
     * Testing that attributes that have the same name in extended entity are properly replaced
     */
    it('TestAttributesThatAreReplaced', (done) => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestAttributesThatAreReplaced');
        const extendedEntity = yield corpus.fetchObjectAsync('local:/extended.cdm.json/extended');
        const resExtendedEnt = yield extendedEntity.createResolvedEntityAsync('resExtended');
        // the attribute from the base class should be merged with the attribute
        // from the extended class into a single attribute
        expect(resExtendedEnt.attributes.length)
            .toBe(1);
        // check that traits from the base class merged with the traits from the extended class
        const attribute = resExtendedEnt.attributes.allItems[0];
        // base trait
        expect(attribute.appliedTraits.indexOf('means.identity.brand')).not
            .toEqual(-1);
        // extended trait
        expect(attribute.appliedTraits.indexOf('means.identity.company.name')).not
            .toEqual(-1);
        // make sure the attribute context and entity foreign key were maintained correctly
        const foreignKeyForBaseAttribute = resExtendedEnt.attributeContext.contents.allItems[1]
            .contents.allItems[1];
        expect(foreignKeyForBaseAttribute.name)
            .toBe('_generatedAttributeSet');
        const fkReference = foreignKeyForBaseAttribute.contents.allItems[0].contents.allItems[0]
            .contents.allItems[0];
        expect(fkReference.namedReference)
            .toBe('resExtended/hasAttributes/regardingObjectId');
        done();
    }));
    /**
     * Test that resolved attribute limit is calculated correctly and respected
     */
    it('TestResolvedAttributeLimit', (done) => __awaiter(void 0, void 0, void 0, function* () {
        var expectedLogCodes = new Set([internal_1.cdmLogCode.ErrRelMaxResolvedAttrReached]);
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestResolvedAttributeLimit', undefined, false, expectedLogCodes);
        const mainEntity = yield corpus.fetchObjectAsync('local:/mainEntity.cdm.json/mainEntity');
        let resOpt = new internal_1.resolveOptions(mainEntity.inDocument, new AttributeResolutionDirectiveSet_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'referenceOnly'])));
        // if attribute limit is reached, entity should be null
        resOpt.resolvedAttributeLimit = 4;
        let resEnt = yield mainEntity.createResolvedEntityAsync(`${mainEntity.entityName}_zeroAtts`, resOpt);
        expect(resEnt)
            .toBeUndefined();
        // when the attribute limit is set to null, there should not be a limit on the possible number of attributes
        resOpt.resolvedAttributeLimit = undefined;
        let ras = mainEntity.fetchResolvedAttributes(resOpt);
        resEnt = yield mainEntity.createResolvedEntityAsync(`${mainEntity.entityName}_normalized_referenceOnly`, resOpt);
        // there are 5 total attributes
        expect(ras.resolvedAttributeCount)
            .toBe(5);
        expect(ras.set.length)
            .toBe(5);
        expect(mainEntity.attributes.length)
            .toBe(3);
        // there are 2 attributes grouped in an entity attribute
        // and 2 attributes grouped in an attribute group
        expect(mainEntity.attributes.allItems[2].explicitReference.members.length)
            .toBe(2);
        // using the default limit number
        resOpt = new internal_1.resolveOptions(mainEntity.inDocument);
        resOpt.directives = new AttributeResolutionDirectiveSet_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'referenceOnly']));
        ras = mainEntity.fetchResolvedAttributes(resOpt);
        resEnt = yield mainEntity.createResolvedEntityAsync(`${mainEntity.entityName}_normalized_referenceOnly`, resOpt);
        // there are 5 total attributes
        expect(ras.resolvedAttributeCount)
            .toBe(5);
        expect(ras.set.length)
            .toBe(5);
        expect(mainEntity.attributes.length)
            .toBe(3);
        // there are 2 attributes grouped in an entity attribute
        // and 2 attributes grouped in an attribute group
        expect(mainEntity.attributes.allItems[2].explicitReference.members.length)
            .toBe(2);
        resOpt.directives = new AttributeResolutionDirectiveSet_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'structured']));
        ras = mainEntity.fetchResolvedAttributes(resOpt);
        resEnt = yield mainEntity.createResolvedEntityAsync(`${mainEntity.entityName}_normalized_structured`, resOpt);
        // there are 5 total attributes
        expect(ras.resolvedAttributeCount)
            .toBe(5);
        // the attribute count is different because one attribute is a group that contains two different attributes
        expect(ras.set.length)
            .toBe(4);
        expect(mainEntity.attributes.length)
            .toBe(3);
        // again there are 2 attributes grouped in an entity attribute
        // and 2 attributes grouped in an attribute group
        expect(mainEntity.attributes.allItems[2].explicitReference.members.length)
            .toBe(2);
        done();
    }));
    /**
     * Test that "is.linkedEntity.name" and "is.linkedEntity.identifier" traits are set when "selectedTypeAttribute" and "foreignKeyAttribute"
     * are present in the entity's resolution guidance.
     */
    it('TestSettingTraitsForResolutionGuidanceAttributes', (done) => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestSettingTraitsForResolutionGuidanceAttributes');
        const entity = yield corpus.fetchObjectAsync('local:/Customer.cdm.json/Customer');
        // Resolve with default directives to get "is.linkedEntity.name" trait.
        let resOpt = new internal_1.resolveOptions(entity.inDocument, new AttributeResolutionDirectiveSet_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'referenceOnly'])));
        let resolvedEntity = yield entity.createResolvedEntityAsync('resolved', resOpt);
        expect(resolvedEntity.attributes.allItems[1].appliedTraits.item('is.linkedEntity.name'))
            .not
            .toBeUndefined();
        // Resolve with referenceOnly directives to get "is.linkedEntity.identifier" trait.
        resOpt = new internal_1.resolveOptions(entity.inDocument, new AttributeResolutionDirectiveSet_1.AttributeResolutionDirectiveSet(new Set(['referenceOnly'])));
        resolvedEntity = yield entity.createResolvedEntityAsync('resolved2', resOpt);
        expect(resolvedEntity.attributes.allItems[0].appliedTraits.item('is.linkedEntity.identifier'))
            .not
            .toBeUndefined();
        done();
    }));
    /**
     * Test that traits(including the ones inside of dataTypeRefence and PurposeReference) are applied to an entity attribute and type attribute.
     */
    it('TestAppliedTraitsInAttributes', (done) => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestAppliedTraitsInAttributes');
        const expectedOutputFolder = testHelper_1.testHelper.getExpectedOutputFolderPath(testsSubpath, 'TestAppliedTraitsInAttributes');
        const entity = yield corpus.fetchObjectAsync('local:/Sales.cdm.json/Sales');
        const resolvedEntity = yield projectionTestUtils_1.projectionTestUtils.getResolvedEntity(corpus, entity, ['referenceOnly']);
        yield AttributeContextUtil_1.AttributeContextUtil.validateAttributeContext(expectedOutputFolder, 'Sales', resolvedEntity);
        done();
    }));
    /**
     * Test that foundations import is added to resolved doc if it exists in the unresolved doc
     */
    it('TestFoundationsInResDoc', (done) => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestFoundationsInResDoc');
        const entity = yield corpus.fetchObjectAsync('Entity.cdm.json/Entity');
        const resEntity = yield entity.createResolvedEntityAsync('resolvedEntity');
        expect(resEntity.inDocument.imports.item('cdm:/foundations.cdm.json') != undefined)
            .toBeTruthy();
        done();
    }));
});
//# sourceMappingURL=EntityResolution.test.js.map