"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 testHelper_1 = require("../../testHelper");
// tslint:disable-next-line: max-func-body-length
describe('Cdm/Relationship/Relationship', () => {
    const testsSubpath = 'Cdm/Relationship';
    /**
     * Testing calculation of relationships and that those relationships are properly added to manifest objects
     */
    it('TestCalculateRelationshipsAndPopulateManifests', () => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestCalculateRelationshipsAndPopulateManifests');
        const rootManifest = yield corpus.createRootManifest('local:/default.manifest.cdm.json');
        const subManifestPath = corpus.storage.createAbsoluteCorpusPath(rootManifest.subManifests.allItems[0].definition);
        const subManifest = yield corpus.fetchObjectAsync(subManifestPath);
        const expectedAllManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsAndPopulateManifests', 'expectedAllManifestRels.json'));
        const expectedAllSubManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsAndPopulateManifests', 'expectedAllSubManifestRels.json'));
        yield corpus.calculateEntityGraphAsync(rootManifest);
        yield rootManifest.populateManifestRelationshipsAsync();
        // check that each relationship has been created correctly
        verifyRelationships(rootManifest, expectedAllManifestRels);
        verifyRelationships(subManifest, expectedAllSubManifestRels);
    }));
    /**
     * Testing calculation of relationships and that those relationships are
     * properly added to manifest objects setting the populate flag to Exclusive
     */
    it('TestCalculateRelationshipsAndPopulateManifestsWithExclusiveFlag', () => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestCalculateRelationshipsAndPopulateManifests');
        const rootManifest = yield corpus.createRootManifest('local:/default.manifest.cdm.json');
        const subManifestPath = corpus.storage.createAbsoluteCorpusPath(rootManifest.subManifests.allItems[0].definition);
        const subManifest = yield corpus.fetchObjectAsync(subManifestPath);
        yield corpus.calculateEntityGraphAsync(rootManifest);
        // make sure only relationships where to and from entities are in the manifest are found with the "exclusive" option is passed in
        yield rootManifest.populateManifestRelationshipsAsync(internal_1.cdmRelationshipDiscoveryStyle.exclusive);
        const expectedExclusiveManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsAndPopulateManifests', 'expectedExclusiveManifestRels.json'));
        const expectedExclusiveSubManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsAndPopulateManifests', 'expectedExclusiveSubManifestRels.json'));
        // check that each relationship has been created correctly
        verifyRelationships(rootManifest, expectedExclusiveManifestRels);
        verifyRelationships(subManifest, expectedExclusiveSubManifestRels);
    }));
    /**
     * Testing calculation of relationships and that those relationships are
     * properly added to manifest objects setting the populate flag to None
     */
    it('TestCalculateRelationshipsAndPopulateManifestsWithNoneFlag', () => __awaiter(void 0, void 0, void 0, function* () {
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestCalculateRelationshipsAndPopulateManifests');
        const rootManifest = yield corpus.createRootManifest('local:/default.manifest.cdm.json');
        const subManifestPath = corpus.storage.createAbsoluteCorpusPath(rootManifest.subManifests.allItems[0].definition);
        const subManifest = yield corpus.fetchObjectAsync(subManifestPath);
        const expectedZeroRelationships = 0;
        yield corpus.calculateEntityGraphAsync(rootManifest);
        // make sure no relationships are added when "none" relationship option is passed in
        yield rootManifest.populateManifestRelationshipsAsync(internal_1.cdmRelationshipDiscoveryStyle.none);
        expect(rootManifest.relationships.length)
            .toBe(expectedZeroRelationships);
        expect(subManifest.relationships.length)
            .toBe(expectedZeroRelationships);
    }));
    /**
     * Testing calculation of relationships and that those relationships are
     * properly added to manifest objects
     */
    it('TestCalculateRelationshipsOnResolvedEntities', () => __awaiter(void 0, void 0, void 0, function* () {
        const expectedResolvedExcManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsOnResolvedEntities', 'expectedResolvedExcManifestRels.json'));
        const expectedResolvedExcSubManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsOnResolvedEntities', 'expectedResolvedExcSubManifestRels.json'));
        const expectedResolvedManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsOnResolvedEntities', 'expectedResolvedManifestRels.json'));
        const expectedResolvedSubManifestRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsOnResolvedEntities', 'expectedResolvedSubManifestRels.json'));
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestCalculateRelationshipsOnResolvedEntities');
        const rootManifest = yield corpus.fetchObjectAsync('local:/default.manifest.cdm.json');
        const resolvedManifest = yield loadAndResolveManifest(corpus, rootManifest, '-resolved');
        const subManifestPath = corpus.storage.createAbsoluteCorpusPath(resolvedManifest.subManifests.allItems[0].definition);
        const subManifest = yield corpus.fetchObjectAsync(subManifestPath);
        // using createResolvedManifest will only populate exclusive relationships
        verifyRelationships(resolvedManifest, expectedResolvedExcManifestRels);
        verifyRelationships(subManifest, expectedResolvedExcSubManifestRels);
        // check that each relationship has been created correctly with the all flag
        yield resolvedManifest.populateManifestRelationshipsAsync();
        yield subManifest.populateManifestRelationshipsAsync();
        verifyRelationships(resolvedManifest, expectedResolvedManifestRels);
        verifyRelationships(subManifest, expectedResolvedSubManifestRels);
        // it is not enough to check if the relationships are correct.
        // We need to check if the incoming and outgoing relationships are
        // correct as well. One being correct can cover up the other being wrong
        // A
        const aEnt = yield corpus.fetchObjectAsync(resolvedManifest.entities.allItems[0].entityPath, resolvedManifest);
        const aInRels = corpus.fetchIncomingRelationships(aEnt);
        const aOutRels = corpus.fetchOutgoingRelationships(aEnt);
        expect(aInRels.length)
            .toBe(0);
        expect(aOutRels.length)
            .toBe(1);
        expect(aOutRels[0].fromEntity)
            .toBe('local:/A-resolved.cdm.json/A');
        expect(aOutRels[0].toEntity)
            .toBe('local:/B-resolved.cdm.json/B');
        // B
        const bEnt = yield corpus.fetchObjectAsync(resolvedManifest.entities.allItems[1].entityPath, resolvedManifest);
        const bInRels = corpus.fetchIncomingRelationships(bEnt);
        const bOutRels = corpus.fetchOutgoingRelationships(bEnt);
        expect(bInRels.length)
            .toBe(2);
        expect(bInRels[0].fromEntity)
            .toBe('local:/A-resolved.cdm.json/A');
        expect(bInRels[0].toEntity)
            .toBe('local:/B-resolved.cdm.json/B');
        expect(bInRels[1].fromEntity)
            .toBe('local:/sub/C-resolved.cdm.json/C');
        expect(bInRels[1].toEntity)
            .toBe('local:/B-resolved.cdm.json/B');
        expect(bOutRels.length)
            .toBe(0);
        // C
        const cEnt = yield corpus.fetchObjectAsync(subManifest.entities.allItems[0].entityPath, subManifest);
        const cInRels = corpus.fetchIncomingRelationships(cEnt);
        const cOutRels = corpus.fetchOutgoingRelationships(cEnt);
        expect(cInRels.length)
            .toBe(0);
        expect(cOutRels.length)
            .toBe(2);
        expect(cOutRels[0].fromEntity)
            .toBe('local:/sub/C-resolved.cdm.json/C');
        expect(cOutRels[0].toEntity)
            .toBe('local:/B-resolved.cdm.json/B');
        expect(cOutRels[1].fromEntity)
            .toBe('local:/sub/C-resolved.cdm.json/C');
        expect(cOutRels[1].toEntity)
            .toBe('local:/sub/D-resolved.cdm.json/D');
        // D
        const dEnt = yield corpus.fetchObjectAsync(subManifest.entities.allItems[1].entityPath, subManifest);
        const dInRels = corpus.fetchIncomingRelationships(dEnt);
        const dOutRels = corpus.fetchOutgoingRelationships(dEnt);
        expect(dInRels.length)
            .toBe(1);
        expect(dInRels[0].fromEntity)
            .toBe('local:/sub/C-resolved.cdm.json/C');
        expect(dInRels[0].toEntity)
            .toBe('local:/sub/D-resolved.cdm.json/D');
        expect(dOutRels.length)
            .toBe(0);
        // E
        const eEnt = yield corpus.fetchObjectAsync(resolvedManifest.entities.allItems[2].entityPath, resolvedManifest);
        const eInRels = corpus.fetchIncomingRelationships(eEnt);
        const eOutRels = corpus.fetchOutgoingRelationships(eEnt);
        expect(eInRels.length)
            .toBe(1);
        expect(eInRels[0].fromEntity)
            .toBe('local:/sub/F-resolved.cdm.json/F');
        expect(eInRels[0].toEntity)
            .toBe('local:/E-resolved.cdm.json/E');
        expect(eOutRels.length)
            .toBe(0);
        // F
        const fEnt = yield corpus.fetchObjectAsync(subManifest.entities.allItems[2].entityPath, subManifest);
        const fInRels = corpus.fetchIncomingRelationships(fEnt);
        const fOutRels = corpus.fetchOutgoingRelationships(fEnt);
        expect(fInRels.length)
            .toBe(0);
        expect(fOutRels.length)
            .toBe(1);
        expect(fOutRels[0].fromEntity)
            .toBe('local:/sub/F-resolved.cdm.json/F');
        expect(fOutRels[0].toEntity)
            .toBe('local:/E-resolved.cdm.json/E');
    }));
    /**
     * Testing calculating relationships for the special kind of attribute that uses the "select one" directive
     */
    it('TestCalculateRelationshipsForSelectsOneAttribute', () => __awaiter(void 0, void 0, void 0, function* () {
        const expectedRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestCalculateRelationshipsForSelectsOneAttribute', 'expectedRels.json'));
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestCalculateRelationshipsForSelectsOneAttribute');
        corpus.storage.mount('cdm', new Storage_1.LocalAdapter(testHelper_1.testHelper.schemaDocumentsPath));
        const manifest = yield corpus.fetchObjectAsync('local:/selectsOne.manifest.cdm.json');
        yield corpus.calculateEntityGraphAsync(manifest);
        yield manifest.populateManifestRelationshipsAsync();
        // check that each relationship has been created correctly
        verifyRelationships(manifest, expectedRels);
    }));
    /**
     * Test the relationship calculation when using a replace as foreign key operation while extending an entity.
     */
    it('testExtendsEntityAndReplaceAsForeignKey', () => __awaiter(void 0, void 0, void 0, function* () {
        const testName = 'TestExtendsEntityAndReplaceAsForeignKey';
        var expectedLogCodes = new Set([internal_1.cdmLogCode.WarnProjFKWithoutSourceEntity]);
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, testName, undefined, false, expectedLogCodes);
        const manifest = yield corpus.fetchObjectAsync('local:/default.manifest.cdm.json');
        yield corpus.calculateEntityGraphAsync(manifest);
        // Check if the warning was logged.
        testHelper_1.testHelper.expectCdmLogCodeEquality(corpus, internal_1.cdmLogCode.WarnProjFKWithoutSourceEntity, true);
        yield manifest.populateManifestRelationshipsAsync();
        expect(manifest.relationships.length)
            .toEqual(0);
    }));
    /**
     * Test relationships are generated correctly when the document name and entity name do not match
     */
    it('TestRelationshipsEntityAndDocumentNameDifferent', () => __awaiter(void 0, void 0, void 0, function* () {
        const expectedRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestRelationshipsEntityAndDocumentNameDifferent', 'expectedRels.json'));
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestRelationshipsEntityAndDocumentNameDifferent');
        const manifest = yield corpus.fetchObjectAsync('local:/main.manifest.cdm.json');
        yield corpus.calculateEntityGraphAsync(manifest);
        yield manifest.populateManifestRelationshipsAsync();
        // check that each relationship has been created correctly
        verifyRelationships(manifest, expectedRels);
    }));
    /**
     * Test that multiple relationships are generated when there are references to multiple entities
     */
    it('TestRelationshipToMultipleEntities', () => __awaiter(void 0, void 0, void 0, function* () {
        const expectedRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestRelationshipToMultipleEntities', 'expectedRels.json'));
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestRelationshipToMultipleEntities');
        const manifest = yield corpus.fetchObjectAsync('local:/main.manifest.cdm.json');
        yield corpus.calculateEntityGraphAsync(manifest);
        yield manifest.populateManifestRelationshipsAsync();
        // check that each relationship has been created correctly
        verifyRelationships(manifest, expectedRels);
    }));
    /**
     * Test that relationships between entities in different namespaces are created correctly
     */
    it('TestRelationshipToDifferentNamespace', () => __awaiter(void 0, void 0, void 0, function* () {
        const expectedRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestRelationshipToDifferentNamespace', 'expectedRels.json'));
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestRelationshipToDifferentNamespace');
        // entity B will be in a different namespace
        corpus.storage.mount('differentNamespace', new Storage_1.LocalAdapter(`${testHelper_1.testHelper.getInputFolderPath(testsSubpath, 'TestRelationshipToDifferentNamespace')}\\differentNamespace`));
        const manifest = yield corpus.fetchObjectAsync('local:/main.manifest.cdm.json');
        yield corpus.calculateEntityGraphAsync(manifest);
        yield manifest.populateManifestRelationshipsAsync();
        // check that each relationship has been created correctly
        verifyRelationships(manifest, expectedRels);
    }));
    /**
     * Test that ensures relationships are updated correctly after entities are changed
     */
    it('TestUpdateRelationships', () => __awaiter(void 0, void 0, void 0, function* () {
        const expectedRels = JSON.parse(testHelper_1.testHelper.getExpectedOutputFileContent(testsSubpath, 'TestUpdateRelationships', 'expectedRels.json'));
        const tempFromFilePath = 'fromEntTemp.cdm.json';
        const tempFromEntityPath = 'local:/fromEntTemp.cdm.json/fromEnt';
        const tempToEntityPath = 'local:/toEnt.cdm.json/toEnt';
        // Initialize corpus and entity files
        const corpus = testHelper_1.testHelper.getLocalCorpus(testsSubpath, 'TestUpdateRelationships');
        const manifest = yield corpus.fetchObjectAsync('local:/main.manifest.cdm.json');
        const manifestNoToEnt = yield corpus.fetchObjectAsync('local:/mainNoToEnt.manifest.cdm.json');
        const fromEnt = yield corpus.fetchObjectAsync('local:/fromEnt.cdm.json/fromEnt');
        const options = new internal_1.copyOptions();
        options.saveConfigFile = false;
        yield fromEnt.inDocument.saveAsAsync(tempFromFilePath, false, options);
        const reloadFromEntity = () => __awaiter(void 0, void 0, void 0, function* () {
            yield fromEnt.inDocument.saveAsAsync(tempFromFilePath, false, options);
            // fetch again to reset the cache
            yield corpus.fetchObjectAsync(tempFromEntityPath, undefined, false, true);
        });
        try {
            // 1. test when entity attribute is removed
            yield corpus.calculateEntityGraphAsync(manifest);
            yield manifest.populateManifestRelationshipsAsync();
            // check that the relationship has been created correctly
            verifyRelationships(manifest, expectedRels);
            // now remove the entity attribute, which removes the relationship
            const removedAttribute = fromEnt.attributes.allItems[0];
            fromEnt.attributes.removeAt(0);
            yield reloadFromEntity();
            yield corpus.calculateEntityGraphAsync(manifest);
            yield manifest.populateManifestRelationshipsAsync();
            // check that the relationship has been removed
            verifyRelationships(manifest, []);
            // 2. test when the to entity is removed
            // restore the entity to the original state
            fromEnt.attributes.push(removedAttribute);
            yield reloadFromEntity();
            yield corpus.calculateEntityGraphAsync(manifest);
            yield manifest.populateManifestRelationshipsAsync();
            // check that the relationship has been created correctly
            verifyRelationships(manifest, expectedRels);
            // remove the to entity
            fromEnt.attributes.removeAt(0);
            yield reloadFromEntity();
            // fetch again to reset the cache
            yield corpus.fetchObjectAsync(tempToEntityPath, undefined, false, true);
            yield corpus.calculateEntityGraphAsync(manifestNoToEnt);
            yield manifestNoToEnt.populateManifestRelationshipsAsync();
            // check that the relationship has been removed
            verifyRelationships(manifestNoToEnt, []);
        }
        finally {
            // clean up created file created
            const fromPath = corpus.storage.corpusPathToAdapterPath(`local:/${tempFromFilePath}`);
            try {
                fs.unlinkSync(fromPath);
            }
            catch (err) { }
        }
    }));
});
function verifyRelationships(manifest, expectedRelationships) {
    expect(manifest.relationships.length)
        .toBe(expectedRelationships.length);
    for (const expectedRel of expectedRelationships) {
        const found = manifest.relationships.allItems.filter((x) => x.fromEntity === expectedRel.fromEntity
            && x.fromEntityAttribute === expectedRel.fromEntityAttribute
            && x.toEntity === expectedRel.toEntity
            && x.toEntityAttribute === expectedRel.toEntityAttribute);
        expect(found.length)
            .toBe(1);
    }
}
function loadAndResolveManifest(corpus, manifest, renameSuffix) {
    return __awaiter(this, void 0, void 0, function* () {
        const resolvedManifest = yield manifest.createResolvedManifestAsync(manifest.manifestName + renameSuffix, '{n}-resolved.cdm.json');
        for (const subManifestDecl of manifest.subManifests) {
            const subManifest = yield corpus.fetchObjectAsync(subManifestDecl.definition, manifest);
            const resolvedSubManifest = yield loadAndResolveManifest(corpus, subManifest, renameSuffix);
            const resolvedDecl = corpus.MakeObject(internal_1.cdmObjectType.manifestDeclarationDef, resolvedSubManifest.manifestName);
            resolvedDecl.definition = corpus.storage.createRelativeCorpusPath(resolvedSubManifest.atCorpusPath, resolvedManifest);
            resolvedManifest.subManifests.push(resolvedDecl);
        }
        return resolvedManifest;
    });
}
//# sourceMappingURL=Relationship.test.js.map