/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { type PlainObject, UnsupportedOperationError, usingConstantValueSchema, usingModelSchema, deserializeMap, } from '@finos/legend-shared'; import { createModelSchema, custom, deserialize, list, optional, primitive, raw, serialize, SKIP, } from 'serializr'; import { ATOMIC_TEST_TYPE } from '../../../../../../../graph/MetaModelConst.js'; import type { PureProtocolProcessorPlugin } from '../../../../PureProtocolProcessorPlugin.js'; import type { Testable_PureProtocolProcessorPlugin_Extension } from '../../../../extensions/Testable_PureProtocolProcessorPlugin_Extension.js'; import { V1_MappingTest } from '../../../model/packageableElements/mapping/V1_MappingTest.js'; import { V1_MappingTestSuite } from '../../../model/packageableElements/mapping/V1_MappingTestSuite.js'; import { V1_MultiExecutionServiceTestResult } from '../../../model/packageableElements/service/V1_MultiExecutionServiceTestResult.js'; import { V1_ServiceTest } from '../../../model/packageableElements/service/V1_ServiceTest.js'; import { V1_ServiceTestSuite } from '../../../model/packageableElements/service/V1_ServiceTestSuite.js'; import { V1_AssertFail } from '../../../model/test/assertion/status/V1_AssertFail.js'; import type { V1_AssertionStatus } from '../../../model/test/assertion/status/V1_AssertionStatus.js'; import { V1_AssertPass } from '../../../model/test/assertion/status/V1_AssertPass.js'; import { V1_EqualToJsonAssertFail } from '../../../model/test/assertion/status/V1_EqualToJsonAssertFail.js'; import { V1_EqualTo } from '../../../model/test/assertion/V1_EqualTo.js'; import { V1_EqualToJson } from '../../../model/test/assertion/V1_EqualToJson.js'; import { V1_EqualToTDS } from '../../../model/test/assertion/V1_EqualToTDS.js'; import type { V1_TestAssertion } from '../../../model/test/assertion/V1_TestAssertion.js'; import { type V1_TestResult, V1_TestError, V1_TestExecuted, } from '../../../model/test/result/V1_TestResult.js'; import type { V1_AtomicTest } from '../../../model/test/V1_AtomicTest.js'; import type { V1_TestSuite } from '../../../model/test/V1_TestSuite.js'; import { V1_UniqueTestId } from '../../../model/test/V1_UniqueTestId.js'; import { V1_externalFormatDataModelSchema } from './V1_DataElementSerializationHelper.js'; import { V1_mappingTestSuiteModelSchema, V1_mappingTestModelSchema, } from './V1_MappingSerializationHelper.js'; import { V1_serviceTestModelSchema, V1_serviceTestSuiteModelSchema, } from './V1_ServiceSerializationHelper.js'; import { V1_FunctionTestSuite } from '../../../model/packageableElements/function/test/V1_FunctionTestSuite.js'; import { V1_functionTestModelSchema, V1_functionTestSuiteModelSchema, } from './V1_FunctionSerializationHelper.js'; import { V1_FunctionTest } from '../../../model/packageableElements/function/test/V1_FunctionTest.js'; import { type V1_TestDebug, V1_TestExecutionPlanDebug, V1_UnknownTestDebug, } from '../../../engine/test/V1_DebugTestsResult.js'; enum V1_AssertionStatusType { ASSERT_FAIL = 'assertFail', ASSERT_PASS = 'assertPass', EQUAL_TO_JSON_ASSERT_FAIL = 'equalToJsonAssertFail', } export enum V1_TestAssertionType { EQUAL_TO = 'equalTo', EQUAL_TO_JSON = 'equalToJson', EQUAL_TO_TDS = 'equalToTDS', } enum V1_TestResultType { TEST_ERROR = 'testError', TEST_EXECUTED = 'testExecuted', MULTI_EXECUTION_TEST_RESULT = 'multiExecutionTestResult', // Remove once https://github.com/finos/legend-engine/pull/808 is released TEMPROARY_MULTI_EXECUTION_TEST_RESULT = 'MultiExecutionServiceTestResult', } enum V1_DebugTestResultType { PLAN_DEBUG = 'testExecutionPlanDebug', } export enum V1_TestSuiteType { SERVICE_TEST_SUITE = 'serviceTestSuite', MAPPING_TEST_SUITE = 'mappingTestSuite', FUNCTION_TEST_SUITE = 'functionTestSuite', } export const V1_uniqueTestIdModelSchema = createModelSchema(V1_UniqueTestId, { atomicTestId: primitive(), testSuiteId: primitive(), }); export const V1_assertFailModelSchema = createModelSchema(V1_AssertFail, { _type: usingConstantValueSchema(V1_AssertionStatusType.ASSERT_FAIL), id: primitive(), message: primitive(), }); export const V1_assertPassModelSchema = createModelSchema(V1_AssertPass, { _type: usingConstantValueSchema(V1_AssertionStatusType.ASSERT_PASS), id: primitive(), }); export const V1_equalToJsonAssertFailModelSchema = createModelSchema( V1_EqualToJsonAssertFail, { _type: usingConstantValueSchema( V1_AssertionStatusType.EQUAL_TO_JSON_ASSERT_FAIL, ), actual: primitive(), expected: primitive(), id: primitive(), message: primitive(), }, ); const V1_serializeAssertionStatus = ( protocol: V1_AssertionStatus, ): PlainObject => { if (protocol instanceof V1_EqualToJsonAssertFail) { return serialize(V1_equalToJsonAssertFailModelSchema, protocol); } else if (protocol instanceof V1_AssertFail) { return serialize(V1_assertFailModelSchema, protocol); } else if (protocol instanceof V1_AssertPass) { return serialize(V1_assertPassModelSchema, protocol); } throw new UnsupportedOperationError( `Can't serialize assertion status`, protocol, ); }; const V1_deserializeAssertionStatus = ( json: PlainObject, ): V1_AssertionStatus => { switch (json._type) { case V1_AssertionStatusType.ASSERT_FAIL: return deserialize(V1_assertFailModelSchema, json); case V1_AssertionStatusType.ASSERT_PASS: return deserialize(V1_assertPassModelSchema, json); case V1_AssertionStatusType.EQUAL_TO_JSON_ASSERT_FAIL: return deserialize(V1_equalToJsonAssertFailModelSchema, json); default: throw new UnsupportedOperationError( `Can't deserialize assertion status of type '${json._type}'`, ); } }; export const V1_equalToModelSchema = createModelSchema(V1_EqualTo, { _type: usingConstantValueSchema(V1_TestAssertionType.EQUAL_TO), expected: raw(), id: primitive(), }); export const V1_equalToJsonModelSchema = createModelSchema(V1_EqualToJson, { _type: usingConstantValueSchema(V1_TestAssertionType.EQUAL_TO_JSON), expected: usingModelSchema(V1_externalFormatDataModelSchema), id: primitive(), }); const V1_equalToTDSModelSchema = createModelSchema(V1_EqualToTDS, { _type: usingConstantValueSchema(V1_TestAssertionType.EQUAL_TO_TDS), expected: usingModelSchema(V1_externalFormatDataModelSchema), id: primitive(), }); export const V1_testErrorModelSchema = createModelSchema(V1_TestError, { atomicTestId: primitive(), error: primitive(), testable: primitive(), testSuiteId: primitive(), }); export const V1_TestExecutionPlanDebugSchema = createModelSchema( V1_TestExecutionPlanDebug, { atomicTestId: primitive(), error: optional(primitive()), testable: primitive(), testSuiteId: primitive(), executionPlan: optional(raw()), debug: optional(list(primitive())), }, ); export const V1_UnknownTestDebugSchema = createModelSchema( V1_UnknownTestDebug, { atomicTestId: primitive(), error: optional(primitive()), testable: primitive(), testSuiteId: primitive(), }, ); export const V1_testExecutedModelSchema = createModelSchema(V1_TestExecuted, { assertStatuses: list( custom( (val) => V1_serializeAssertionStatus(val), (val) => V1_deserializeAssertionStatus(val), ), ), atomicTestId: primitive(), testable: primitive(), testExecutionStatus: primitive(), testSuiteId: optional(primitive()), }); export const V1_MultiExecutionServiceTestResultModelSchema = createModelSchema( V1_MultiExecutionServiceTestResult, { atomicTestId: primitive(), keyIndexedTestResults: custom( () => SKIP, (val) => deserializeMap(val, (v) => V1_deserializeTestResult(v as PlainObject), ), ), testable: primitive(), testSuiteId: primitive(), }, ); export function V1_deserializeTestResult( json: PlainObject, ): V1_TestResult { switch (json._type) { case V1_TestResultType.TEST_ERROR: return deserialize(V1_testErrorModelSchema, json); case V1_TestResultType.TEST_EXECUTED: return deserialize(V1_testExecutedModelSchema, json); case V1_TestResultType.MULTI_EXECUTION_TEST_RESULT: case V1_TestResultType.TEMPROARY_MULTI_EXECUTION_TEST_RESULT: return deserialize(V1_MultiExecutionServiceTestResultModelSchema, json); default: throw new UnsupportedOperationError( `Can't deserialize atomic test of type '${json._type}'`, ); } } export function V1_deserializeDebugTestResult( json: PlainObject, ): V1_TestDebug { switch (json._type) { case V1_DebugTestResultType.PLAN_DEBUG: return deserialize(V1_TestExecutionPlanDebugSchema, json); default: { const unknown = deserialize(V1_UnknownTestDebugSchema, json); unknown.value = json; return unknown; } } } export const V1_serializeAtomicTest = ( protocol: V1_AtomicTest, plugins: PureProtocolProcessorPlugin[], ): PlainObject => { if (protocol instanceof V1_ServiceTest) { return serialize(V1_serviceTestModelSchema, protocol); } else if (protocol instanceof V1_MappingTest) { return serialize(V1_mappingTestModelSchema(plugins), protocol); } else if (protocol instanceof V1_FunctionTest) { return serialize(V1_functionTestModelSchema, protocol); } const extraAtomicTestSerializers = plugins.flatMap( (plugin) => ( plugin as Testable_PureProtocolProcessorPlugin_Extension ).V1_getExtraAtomicTestProtocolSerializers?.() ?? [], ); for (const serializer of extraAtomicTestSerializers) { const atomicTestProtocolJson = serializer(protocol, plugins); if (atomicTestProtocolJson) { return atomicTestProtocolJson; } } throw new UnsupportedOperationError( `Can't serialize atomic test: no compatible serializer available from plugins`, protocol, ); }; export const V1_deserializeAtomicTest = ( json: PlainObject, plugins: PureProtocolProcessorPlugin[], ): V1_AtomicTest => { switch (json._type) { case ATOMIC_TEST_TYPE.Service_Test: return deserialize(V1_serviceTestModelSchema, json); case ATOMIC_TEST_TYPE.Function_Test: return deserialize(V1_functionTestModelSchema, json); case ATOMIC_TEST_TYPE.Mapping_Test: return deserialize(V1_mappingTestModelSchema(plugins), json); default: { const extraAtomicTestProtocolDeserializers = plugins.flatMap( (plugin) => ( plugin as Testable_PureProtocolProcessorPlugin_Extension ).V1_getExtraAtomicTestProtocolDeserializers?.() ?? [], ); for (const deserializer of extraAtomicTestProtocolDeserializers) { const atomicTestProtocol = deserializer(json, plugins); if (atomicTestProtocol) { return atomicTestProtocol; } } throw new UnsupportedOperationError( `Can't deserialize atomic test of type '${json._type}': no compatible deserializer available from plugins`, ); } } }; export const V1_serializeTestAssertion = ( protocol: V1_TestAssertion, ): PlainObject => { if (protocol instanceof V1_EqualTo) { return serialize(V1_equalToModelSchema, protocol); } else if (protocol instanceof V1_EqualToJson) { return serialize(V1_equalToJsonModelSchema, protocol); } else if (protocol instanceof V1_EqualToTDS) { return serialize(V1_equalToTDSModelSchema, protocol); } throw new UnsupportedOperationError( `Can't serialize test assertion`, protocol, ); }; export const V1_deserializeTestAssertion = ( json: PlainObject, ): V1_TestAssertion => { switch (json._type) { case V1_TestAssertionType.EQUAL_TO: return deserialize(V1_equalToModelSchema, json); case V1_TestAssertionType.EQUAL_TO_JSON: return deserialize(V1_equalToJsonModelSchema, json); case V1_TestAssertionType.EQUAL_TO_TDS: return deserialize(V1_equalToTDSModelSchema, json); default: throw new UnsupportedOperationError( `Can't deserialize test assertion of type '${json._type}'`, ); } }; export const V1_serializeTestSuite = ( protocol: V1_TestSuite, plugins: PureProtocolProcessorPlugin[], ): PlainObject => { if (protocol instanceof V1_ServiceTestSuite) { return serialize(V1_serviceTestSuiteModelSchema(plugins), protocol); } else if (protocol instanceof V1_MappingTestSuite) { return serialize(V1_mappingTestSuiteModelSchema(plugins), protocol); } else if (protocol instanceof V1_FunctionTestSuite) { return serialize(V1_functionTestSuiteModelSchema(plugins), protocol); } throw new UnsupportedOperationError(`Can't serialize test suite`, protocol); }; export const V1_deserializeTestSuite = ( json: PlainObject, plugins: PureProtocolProcessorPlugin[], ): V1_TestSuite => { switch (json._type) { case V1_TestSuiteType.SERVICE_TEST_SUITE: return deserialize(V1_serviceTestSuiteModelSchema(plugins), json); case V1_TestSuiteType.MAPPING_TEST_SUITE: return deserialize(V1_mappingTestSuiteModelSchema(plugins), json); case V1_TestSuiteType.FUNCTION_TEST_SUITE: return deserialize(V1_functionTestSuiteModelSchema(plugins), json); default: throw new UnsupportedOperationError( `Can't deserialize test suite of type '${json._type}'`, ); } };