"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 path = require("path");
const os_1 = require("os");
const internal_1 = require("../../../internal");
const testHelper_1 = require("../../testHelper");
const projectionTestUtils_1 = require("../../Utilities/projectionTestUtils");
const AttributeContextUtil_1 = require("../Projection/AttributeContextUtil");
/**
 * Test to validate calculateEntityGraphAsync function
 */
describe('Cdm/Relationship/CalculateRelationshipTest', () => {
    /**
     * Platform-specific line separator
     */
    const endOfLine = os_1.EOL;
    /**
     * The path between TestDataPath and TestName.
     */
    const testsSubpath = 'Cdm/Relationship/TestCalculateRelationship';
    /**
     * Non projection scenario with the referenced entity having a primary key
     */
    it('TestSimpleWithId', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestSimpleWithId';
        const entityName = 'Sales';
        yield testRun(testName, entityName, false);
    }));
    /**
     * Projection scenario with the referenced entity not having any primary key
     */
    it('TestWithoutIdProj', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestWithoutIdProj';
        const entityName = 'Sales';
        yield testRun(testName, entityName, true);
    }));
    /**
     *
     */
    it('TestDiffRefLocation', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestDiffRefLocation';
        const entityName = 'Sales';
        yield testRun(testName, entityName, true);
    }));
    /**
     * Projection with composite keys
     */
    it('TestCompositeProj', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestCompositeProj';
        const entityName = 'Sales';
        yield testRun(testName, entityName, true);
    }));
    /**
     * Projection with nested composite keys
     */
    it('TestNestedCompositeProj', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestNestedCompositeProj';
        const entityName = 'Sales';
        yield testRun(testName, entityName, true);
    }));
    /**
     * Non projection scenario with selectsSubAttribute set to one
     */
    it('TestPolymorphicWithoutProj', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestPolymorphicWithoutProj';
        const entityName = 'CustomPerson';
        yield testRun(testName, entityName, false);
    }));
    /**
     * Projection with IsPolymorphicSource property set to true
     */
    it('TestPolymorphicProj', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestPolymorphicProj';
        const entityName = 'Person';
        yield testRun(testName, entityName, true);
    }));
    /**
     * Test a composite key relationship with a polymorphic entity
     */
    it('TestCompositeKeyPolymorphicRelationship', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestCompositeKeyPolymorphicRelationship';
        const entityName = 'Person';
        yield testRun(testName, entityName, true);
    }));
    /**
     * Test a composite key relationship with multiple entity attribute but not polymorphic
     */
    it('TestCompositeKeyNonPolymorphicRelationship', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestCompositeKeyNonPolymorphicRelationship';
        const entityName = 'Person';
        yield testRun(testName, entityName, true);
    }));
    /**
     * Common test code for these test cases
     */
    function testRun(testName, entityName, isEntitySet, updateExpectedOutput = false) {
        return __awaiter(this, void 0, void 0, function* () {
            const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, testName);
            const inputFolder = testHelper_1.testHelper.getInputFolderPath(testsSubpath, testName);
            const expectedOutputFolder = testHelper_1.testHelper.getExpectedOutputFolderPath(testsSubpath, testName);
            const actualOutputFolder = testHelper_1.testHelper.getActualOutputFolderPath(testsSubpath, testName);
            if (!fs.existsSync(actualOutputFolder)) {
                fs.mkdirSync(actualOutputFolder, { recursive: true });
            }
            const manifest = yield corpus.fetchObjectAsync('local:/default.manifest.cdm.json');
            expect(manifest)
                .toBeTruthy();
            const entity = yield corpus.fetchObjectAsync(`local:/${entityName}.cdm.json/${entityName}`, manifest);
            expect(entity)
                .toBeTruthy();
            const resolvedEntity = yield projectionTestUtils_1.projectionTestUtils.getResolvedEntity(corpus, entity, ['referenceOnly']);
            assertEntityShapeInReslovedEntity(resolvedEntity, isEntitySet);
            yield AttributeContextUtil_1.AttributeContextUtil.validateAttributeContext(expectedOutputFolder, entityName, resolvedEntity, updateExpectedOutput);
            yield corpus.calculateEntityGraphAsync(manifest);
            yield manifest.populateManifestRelationshipsAsync();
            const actualRelationshipsString = listRelationships(corpus, entity, actualOutputFolder, entityName);
            const relationshipsFilename = `REL_${entityName}.txt`;
            fs.writeFileSync(path.join(actualOutputFolder, relationshipsFilename), actualRelationshipsString);
            const expectedRelationshipsStringFilePath = path.resolve(path.join(expectedOutputFolder, relationshipsFilename));
            const expectedRelationshipsString = fs.readFileSync(expectedRelationshipsStringFilePath).toString();
            expect(actualRelationshipsString)
                .toEqual(expectedRelationshipsString);
            const outputFolder = corpus.storage.fetchRootFolder('output');
            outputFolder.documents.push(manifest);
            const manifestFileName = 'saved.manifest.cdm.json';
            yield manifest.saveAsAsync(manifestFileName, true);
            const actualManifestPath = `${actualOutputFolder}/${manifestFileName}`;
            if (!fs.existsSync(actualManifestPath)) {
                fail('Unable to save manifest with relationship');
            }
            else {
                const savedManifest = yield corpus.fetchObjectAsync(`output:/${manifestFileName}`);
                const actualSavedManifestRel = getRelationshipStrings(savedManifest.relationships);
                const manifestRelationshipsFilename = `MANIFEST_REL_${entityName}.txt`;
                fs.writeFileSync(path.join(actualOutputFolder, manifestRelationshipsFilename), actualSavedManifestRel);
                const expectedSavedManifestRel = fs.readFileSync(path.join(expectedOutputFolder, manifestRelationshipsFilename)).toString();
                expect(actualSavedManifestRel)
                    .toEqual(expectedSavedManifestRel);
            }
        });
    }
    /**
     * Check if the entity shape is correct in entity reference.
     */
    function assertEntityShapeInReslovedEntity(resolvedEntity, isEntitySet) {
        for (const att of resolvedEntity.attributes) {
            if (att && att.appliedTraits) {
                for (const traitRef of att.appliedTraits) {
                    if (traitRef.namedReference === 'is.linkedEntity.identifier' && traitRef.arguments.length > 0) {
                        const constEnt = traitRef.arguments.allItems[0].value.fetchObjectDefinition();
                        if (constEnt) {
                            if (isEntitySet) {
                                expect('entitySet')
                                    .toEqual(constEnt.entityShape.namedReference);
                            }
                            else {
                                expect('entityGroupSet')
                                    .toEqual(constEnt.entityShape.namedReference);
                            }
                            return;
                        }
                    }
                }
            }
        }
        fail('Unable to find entity shape from resolved model.');
    }
    /**
     * Get a string version of the relationship collection
     */
    function getRelationshipStrings(relationships) {
        let bldr = '';
        for (const rel of relationships.allItems) {
            bldr += `${rel.name ? rel.name : ''}|${rel.toEntity}|${rel.toEntityAttribute}|${rel.fromEntity}|${rel.fromEntityAttribute}`;
            bldr += endOfLine;
        }
        return bldr;
    }
    /**
 * Get a string version of one relationship
 */
    function getRelationshipString(rel) {
        let nameAndPipe = '';
        if (rel.name) {
            nameAndPipe = `${rel.name}|`;
        }
        return `${nameAndPipe}${rel.toEntity}|${rel.toEntityAttribute}|${rel.fromEntity}|${rel.fromEntityAttribute}`;
    }
    /**
     * List the incoming and outgoing relationships
     */
    function listRelationships(corpus, entity, actualOutputFolder, entityName) {
        let bldr = '';
        const relCache = new Set();
        bldr += `Incoming Relationships For: ${entity.entityName}:` + endOfLine;
        // Loop through all the relationships where other entities point to this entity.
        for (const relationship of corpus.fetchIncomingRelationships(entity)) {
            const cacheKey = getRelationshipString(relationship);
            if (!relCache.has(cacheKey)) {
                bldr += printRelationship(relationship) + endOfLine;
                relCache.add(cacheKey);
            }
        }
        console.log(`Outgoing Relationships For: ${entity.entityName}:`);
        // Now loop through all the relationships where this entity points to other entities.
        for (const relationship of corpus.fetchOutgoingRelationships(entity)) {
            const cacheKey = getRelationshipString(relationship);
            if (!relCache.has(cacheKey)) {
                bldr += printRelationship(relationship) + endOfLine;
                relCache.add(cacheKey);
            }
        }
        return bldr;
    }
    /**
     * Print the relationship
     */
    function printRelationship(relationship) {
        let bldr = '';
        if (relationship.name) {
            bldr += `  Name: ${relationship.name}` + endOfLine;
        }
        bldr += `  FromEntity: ${relationship.fromEntity}` + endOfLine;
        bldr += `  FromEntityAttribute: ${relationship.fromEntityAttribute}` + endOfLine;
        bldr += `  ToEntity: ${relationship.toEntity}` + endOfLine;
        bldr += `  ToEntityAttribute: ${relationship.toEntityAttribute}` + endOfLine;
        if (relationship.exhibitsTraits != null && relationship.exhibitsTraits.length != 0) {
            bldr += '  ExhibitsTraits:' + endOfLine;
            var orderedAppliedTraits = relationship.exhibitsTraits.allItems.sort((a, b) => { var _a; return (_a = a.namedReference) === null || _a === void 0 ? void 0 : _a.localeCompare(b.namedReference); });
            orderedAppliedTraits.forEach((trait) => {
                bldr += `      ${trait.namedReference}` + endOfLine;
                if (trait instanceof internal_1.CdmTraitReference) {
                    trait.arguments.allItems.forEach((args) => {
                        var attrCtxUtil = new AttributeContextUtil_1.AttributeContextUtil();
                        bldr += `          ${attrCtxUtil.getArgumentValuesAsString(args)}` + endOfLine;
                    });
                }
            });
        }
        bldr += endOfLine;
        console.log(bldr);
        return bldr;
    }
    /**
     * Check the attribute context for these test scenarios
     */
    function getAttributeContextString(resolvedEntity, entityName, actualOutputFolder) {
        return (new AttributeContextUtil_1.AttributeContextUtil()).getAttributeContextStrings(resolvedEntity);
    }
});
//# sourceMappingURL=CalculateRelationship.test.js.map