"use strict";
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 });
exports.projectionTestUtils = void 0;
const fs = require("fs");
const internal_1 = require("../../internal");
const AttributeContextUtil_1 = require("../Cdm/Projection/AttributeContextUtil");
const testHelper_1 = require("../testHelper");
/**
 * Common utility methods for projection tests
 * If you want to update the expected output txt files for all the tests that are ran,
 * please set the parameter updateExpectedOutput true in the method validateAttributeContext()
 */
class projectionTestUtils {
    /**
     * Resolves an entity
     * @param corpus The corpus
     * @param inputEntity The entity to resolve
     * @param directives The set of directives used for resolution
     */
    static getResolvedEntity(corpus, inputEntity, directives) {
        return __awaiter(this, void 0, void 0, function* () {
            const roHashSet = new Set();
            for (let i = 0; i < directives.length; i++) {
                roHashSet.add(directives[i]);
            }
            const resolvedEntityName = `Resolved_${inputEntity.entityName}`;
            const resOpt = new internal_1.resolveOptions(inputEntity.inDocument, new internal_1.AttributeResolutionDirectiveSet(roHashSet));
            const resolvedFolder = corpus.storage.fetchRootFolder('output');
            const resolvedEntity = yield inputEntity.createResolvedEntityAsync(resolvedEntityName, resOpt, resolvedFolder);
            return resolvedEntity;
        });
    }
    /**
     * Returns a suffix that contains the file name and resolution option used
     * @param directives The set of directives used for resolution
     */
    static getResolutionOptionNameSuffix(directives) {
        let fileNamePrefix = '';
        for (const directive of directives) {
            let shortenedDirective;
            switch (directive) {
                case 'normalized':
                    shortenedDirective = 'norm';
                    break;
                case 'referenceOnly':
                    shortenedDirective = 'refOnly';
                    break;
                case 'structured':
                    shortenedDirective = 'struc';
                    break;
                case 'virtual':
                    shortenedDirective = 'virt';
                    break;
                default:
                    throw 'Using unsupported directive';
            }
            fileNamePrefix = `${fileNamePrefix}_${shortenedDirective}`;
        }
        if (!fileNamePrefix) {
            fileNamePrefix = '_default';
        }
        return fileNamePrefix;
    }
    /**
     * Loads an entity, resolves it, and then validates the generated attribute contexts
     */
    static loadEntityForResolutionOptionAndSave(corpus, testName, testsSubpath, entityName, directives, updateExpectedOutput = false) {
        return __awaiter(this, void 0, void 0, function* () {
            const expectedOutputPath = testHelper_1.testHelper.getExpectedOutputFolderPath(testsSubpath, testName);
            const entity = yield corpus.fetchObjectAsync(`local:/${entityName}.cdm.json/${entityName}`);
            expect(entity)
                .not
                .toBeUndefined();
            const resolvedEntity = yield projectionTestUtils.getResolvedEntity(corpus, entity, directives);
            expect(resolvedEntity)
                .not
                .toBeUndefined();
            yield projectionTestUtils.validateAttributeContext(directives, expectedOutputPath, entityName, resolvedEntity, updateExpectedOutput);
            return resolvedEntity;
        });
    }
    /**
     * Creates an entity
     */
    static createEntity(corpus, localRoot) {
        const entityName = 'TestEntity';
        const entity = corpus.MakeObject(internal_1.cdmObjectType.entityDef, entityName);
        const entityDoc = corpus.MakeObject(internal_1.cdmObjectType.documentDef, `${entityName}.cdm.json`, false);
        entityDoc.imports.push(projectionTestUtils.foundationJsonPath);
        entityDoc.definitions.push(entity);
        localRoot.documents.push(entityDoc, entityDoc.name);
        return entity;
    }
    /**
     * Creates a source entity for a projection
     */
    static createSourceEntity(corpus, localRoot) {
        const entityName = 'SourceEntity';
        const entity = corpus.MakeObject(internal_1.cdmObjectType.entityDef, entityName);
        const attributeName1 = 'id';
        const attribute1 = corpus.MakeObject(internal_1.cdmObjectType.typeAttributeDef, attributeName1);
        attribute1.dataType = corpus.MakeRef(internal_1.cdmObjectType.dataTypeRef, 'string', true);
        entity.attributes.push(attribute1);
        const attributeName2 = 'name';
        const attribute2 = corpus.MakeObject(internal_1.cdmObjectType.typeAttributeDef, attributeName2);
        attribute2.dataType = corpus.MakeRef(internal_1.cdmObjectType.dataTypeRef, 'string', true);
        entity.attributes.push(attribute2);
        const attributeName3 = 'value';
        const attribute3 = corpus.MakeObject(internal_1.cdmObjectType.typeAttributeDef, attributeName3);
        attribute3.dataType = corpus.MakeRef(internal_1.cdmObjectType.dataTypeRef, 'integer', true);
        entity.attributes.push(attribute3);
        const attributeName4 = 'date';
        const attribute4 = corpus.MakeObject(internal_1.cdmObjectType.typeAttributeDef, attributeName4);
        attribute4.dataType = corpus.MakeRef(internal_1.cdmObjectType.dataTypeRef, 'date', true);
        entity.attributes.push(attribute4);
        const entityDoc = corpus.MakeObject(internal_1.cdmObjectType.documentDef, `${entityName}.cdm.json`, false);
        entityDoc.imports.push(projectionTestUtils.foundationJsonPath);
        entityDoc.definitions.push(entity);
        localRoot.documents.push(entityDoc, entityDoc.name);
        return entity;
    }
    /**
     * Creates a projection
     */
    static createProjection(corpus, localRoot) {
        // Create an entity reference to use as the source of the projection
        const projectionSource = corpus.MakeObject(internal_1.cdmObjectType.entityRef, undefined);
        projectionSource.explicitReference = projectionTestUtils.createSourceEntity(corpus, localRoot);
        // Create the projection
        const projection = corpus.MakeObject(internal_1.cdmObjectType.projectionDef);
        projection.source = projectionSource;
        return projection;
    }
    /**
     * Validates trait "has.expansionInfo.list" for array type.
     * @param attribute The type attribute
     * @param expectedAttrName The expected attribute name
     * @param ordinal The expected ordinal
     * @param expansionName The expected expansion name
     * @param memberAttribute The expected member attribute name
     * @internal
     */
    static validateExpansionInfoTrait(attribute, expectedAttrName, ordinal, expansionName, memberAttribute) {
        expect(attribute.name)
            .toEqual(expectedAttrName);
        const trait = attribute.appliedTraits.item('has.expansionInfo.list');
        expect(trait)
            .not
            .toBeUndefined();
        expect(trait.arguments.fetchValue('expansionName'))
            .toEqual(expansionName);
        expect(trait.arguments.fetchValue('ordinal'))
            .toEqual(ordinal.toString());
        expect(trait.arguments.fetchValue('memberAttribute'))
            .toEqual(memberAttribute);
    }
    /**
     * Validates the creation of an attribute group and return its definition
     * @param attributes The collection of attributes
     * @param attributeGroupName The attribute group name
     * @param attributesSize The expected size of the attributes collection
     * @internal
     */
    static validateAttributeGroup(attributes, attributeGroupName, attributesSize = 1, index = 0) {
        expect(attributes.length)
            .toEqual(attributesSize);
        expect(attributes.allItems[index].objectType)
            .toEqual(internal_1.cdmObjectType.attributeGroupRef);
        const attGroupReference = attributes.allItems[index];
        expect(attGroupReference.explicitReference)
            .not
            .toBeUndefined();
        const attGroupDefinition = attGroupReference.explicitReference;
        expect(attGroupDefinition.attributeGroupName)
            .toEqual(attributeGroupName);
        return attGroupDefinition;
    }
    /**
     * Validates if the attribute context of the resolved entity matches the expected output.
     * @param directives
     * @param expectedOutputPath
     * @param entityName
     * @param resolvedEntity
     * @param updateExpectedOutput If true, will update the expected output txt files for all the tests that are ran.
     */
    static validateAttributeContext(directives, expectedOutputPath, entityName, resolvedEntity, updateExpectedOutput = false) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!resolvedEntity.attributeContext) {
                throw new Error('ValidateAttributeContext called with not resolved entity.');
            }
            const fileNamePrefix = `AttrCtx_${entityName}`;
            let expectedStringFilePath;
            const fileNameSuffix = projectionTestUtils.getResolutionOptionNameSuffix(directives);
            const defaultFileNameSuffix = projectionTestUtils.getResolutionOptionNameSuffix([]);
            // Get actual text
            const attrCtxUtil = new AttributeContextUtil_1.AttributeContextUtil();
            const actualText = attrCtxUtil.getAttributeContextStrings(resolvedEntity);
            if (updateExpectedOutput) {
                expectedStringFilePath = `${expectedOutputPath}/${fileNamePrefix}${fileNameSuffix}.txt`;
                if (directives.length > 0) {
                    const defaultStringFilePath = `${expectedOutputPath}/${fileNamePrefix}${defaultFileNameSuffix}.txt`;
                    const defaultText = fs.existsSync(defaultStringFilePath) ? fs.readFileSync(defaultStringFilePath).toString() : undefined;
                    if (actualText === defaultText) {
                        if (fs.existsSync(expectedStringFilePath)) {
                            fs.unlinkSync(expectedStringFilePath);
                        }
                    }
                    else {
                        fs.writeFileSync(expectedStringFilePath, actualText);
                    }
                }
                else {
                    fs.writeFileSync(expectedStringFilePath, actualText);
                }
            }
            else {
                // Actual
                const actualStringFilePath = `${expectedOutputPath.replace('ExpectedOutput', testHelper_1.testHelper.getTestActualOutputFolderName())}/AttrCtx_${entityName}.txt`;
                // Save Actual AttrCtx_*.txt and Resolved_*.cdm.json
                fs.writeFileSync(actualStringFilePath, actualText);
                yield resolvedEntity.inDocument.saveAsAsync(`Resolved_${resolvedEntity.entityName}${fileNameSuffix}.cdm.json`, false);
                // Expected
                const expectedFileNameSuffix = projectionTestUtils.getResolutionOptionNameSuffix(directives);
                let expectedStringFilePath = `${expectedOutputPath}/${fileNamePrefix}${expectedFileNameSuffix}.txt`;
                // If a test file doesn't exist for this set of directives, fall back to the default file.
                if (!fs.existsSync(expectedStringFilePath)) {
                    expectedStringFilePath = `${expectedOutputPath}/${fileNamePrefix}${defaultFileNameSuffix}.txt`;
                }
                const expectedText = fs.readFileSync(expectedStringFilePath).toString();
                // Test if Actual is Equal to Expected
                expect(actualText)
                    .toEqual(expectedText);
            }
        });
    }
}
exports.projectionTestUtils = projectionTestUtils;
/**
 * Path to foundations
 */
projectionTestUtils.foundationJsonPath = 'cdm:/foundations.cdm.json';
//# sourceMappingURL=projectionTestUtils.js.map