@eclipse-emfcloud/model-validation
Version:
Generic model validation framework.
229 lines • 13 kB
JavaScript
;
// *****************************************************************************
// Copyright (C) 2023-2024 STMicroelectronics.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: MIT License which is
// available at https://opensource.org/licenses/MIT.
//
// SPDX-License-Identifier: EPL-2.0 OR MIT
// *****************************************************************************
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = __importStar(require("chai"));
const sinon_1 = __importDefault(require("sinon"));
const sinon_chai_1 = __importDefault(require("sinon-chai"));
const model_validation_service_1 = require("../model-validation-service");
const diagnostic_1 = require("../diagnostic");
chai_1.default.use(sinon_chai_1.default);
describe('ModelValidationServiceImpl', () => {
let modelValidationService;
let validatorOK;
let validatorError;
let validatorException;
let validatorReject;
const customOKInstance = {
severity: 'ok',
source: '@example/test',
code: '0',
path: '',
message: 'Hello.',
};
const errorInstance = {
severity: 'error',
source: '@example/test',
code: '4',
path: 'foo/errorProp',
message: 'Error, error!',
};
let consoleWarnStub;
beforeEach(() => {
modelValidationService = new model_validation_service_1.ModelValidationServiceImpl();
validatorOK = {
validate: (_modelId, _model) => {
return Promise.resolve(customOKInstance);
},
};
validatorError = {
validate: (_modelId, _model) => {
return Promise.resolve(errorInstance);
},
};
validatorException = {
validate: (_modelId, _model) => {
throw new Error('This is an error');
},
};
validatorReject = {
validate: (_modelId, _model) => {
return Promise.reject('rejected Promise');
},
};
consoleWarnStub = sinon_1.default.stub(console, 'warn');
});
afterEach(() => {
consoleWarnStub.restore();
});
describe('validate', () => {
it('validate w/o validator', async () => {
const result = await modelValidationService.validate('key1', {});
(0, chai_1.expect)(result).to.be.deep.equal((0, diagnostic_1.ok)());
});
it('getValidationState for ok Diagnostic validate', async () => {
modelValidationService.addValidator(validatorOK);
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(modelValidationService.getValidationState('key1')).to.be.deep.equal((0, diagnostic_1.ok)());
});
it('getValidationState for multi validate', async () => {
modelValidationService.addValidator(validatorOK);
modelValidationService.addValidator(validatorError);
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(modelValidationService.getValidationState('key1')).to.be.deep.equal(errorInstance);
});
it('throws exception from validator do not crash the whole process', async () => {
modelValidationService.addValidator(validatorOK);
modelValidationService.addValidator(validatorException);
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(modelValidationService.getValidationState('key1')).to.be.deep.equal((0, diagnostic_1.ok)());
const msg = `An error occurred within a validator during the validation of 'key1'. Error: This is an error. Validation continues ignoring the failed validator`;
(0, chai_1.expect)(consoleWarnStub.calledWith('model-validation/model-validation-service:', msg)).to.be.true;
});
it('rejects the promises from validator do not crash the whole process', async () => {
modelValidationService.addValidator(validatorOK);
modelValidationService.addValidator(validatorReject);
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(modelValidationService.getValidationState('key1')).to.be.deep.equal((0, diagnostic_1.ok)());
const msg = `An error occurred within a validator during the validation of 'key1' (cause: rejected Promise). Validation continues ignoring the failed validator`;
(0, chai_1.expect)(consoleWarnStub.calledWith('model-validation/model-validation-service:', msg)).to.be.true;
});
});
describe('subscribe', () => {
it('subscribe to the updates to the validation state of a single model', async () => {
const subscription = modelValidationService.subscribe('key1');
subscription.onValidationChanged = sinon_1.default.spy();
await modelValidationService.validate('key1', {});
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledOnce;
await modelValidationService.validate('key2', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledOnce;
subscription.close();
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledOnce;
});
it('subscribe to the updates to the validation state w/o onValidationChanges', async () => {
const subscriptionA = modelValidationService.subscribe('key1');
const subscriptionB = modelValidationService.subscribe('key1');
subscriptionA.onValidationChanged = sinon_1.default.spy();
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(subscriptionA.onValidationChanged).to.be.calledOnce;
subscriptionB.close();
subscriptionB.close();
});
it('subscribe to the updates to the validation state of several models', async () => {
const subscription = modelValidationService.subscribe('key1', 'key2', 'key3');
subscription.onValidationChanged = sinon_1.default.spy();
await modelValidationService.validate('key1', {});
await modelValidationService.validate('key2', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledTwice;
subscription.close();
await modelValidationService.validate('key1', {});
await modelValidationService.validate('key2', {});
await modelValidationService.validate('key3', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledTwice;
});
it('subscribe to the updates to the validation state of all models', async () => {
const subscription = modelValidationService.subscribe();
subscription.onValidationChanged = sinon_1.default.spy();
await modelValidationService.validate('key1', {});
await modelValidationService.validate('key2', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledTwice;
await modelValidationService.validate('key2', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledTwice;
subscription.close();
await modelValidationService.validate('key3', {});
(0, chai_1.expect)(subscription.onValidationChanged).to.be.calledTwice;
});
it('subscribe to the updates to the validation state of all models w/o onValidationChanges', async () => {
const subscriptionA = modelValidationService.subscribe();
subscriptionA.onValidationChanged = sinon_1.default.spy();
const subscriptionB = modelValidationService.subscribe();
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(subscriptionA.onValidationChanged).to.be.calledOnce;
subscriptionB.close();
subscriptionB.close();
});
it('subscribe to the updates of the validation state of several models with one onValidationChanged throwing an error', async () => {
const subscriptionA = modelValidationService.subscribe('key1', 'key2', 'key3');
subscriptionA.onValidationChanged = (_modelId, _model, _diagnostic) => {
throw new Error('This is an onValidationChanged exception');
};
const subscriptionB = modelValidationService.subscribe('key1', 'key2', 'key3');
subscriptionB.onValidationChanged = sinon_1.default.spy();
await modelValidationService.validate('key1', {});
const msg1 = `An error occurred within the onValidationChanged callback for 'key1'. Error: This is an onValidationChanged exception. Other subscribers will still be notified ignoring the failed callback`;
(0, chai_1.expect)(consoleWarnStub.calledWith('model-validation/model-validation-service:', msg1)).to.be.true;
await modelValidationService.validate('key2', {});
const msg2 = `An error occurred within the onValidationChanged callback for 'key2'. Error: This is an onValidationChanged exception. Other subscribers will still be notified ignoring the failed callback`;
(0, chai_1.expect)(consoleWarnStub.calledWith('model-validation/model-validation-service:', msg2)).to.be.true;
(0, chai_1.expect)(subscriptionB.onValidationChanged).to.be.calledTwice;
subscriptionA.close();
subscriptionB.close();
});
it('subscribe to the updates of the validation state of all models with one onValidationChanged throwing an error', async () => {
const subscriptionA = modelValidationService.subscribe();
subscriptionA.onValidationChanged = (_modelId, _model, _diagnostic) => {
throw new Error('This is an onValidationChanged exception');
};
const subscriptionB = modelValidationService.subscribe();
subscriptionB.onValidationChanged = sinon_1.default.spy();
await modelValidationService.validate('key1', {});
(0, chai_1.expect)(subscriptionB.onValidationChanged).to.be.calledOnce;
const msg = `An error occurred within the onValidationChanged callback for 'key1'. Error: This is an onValidationChanged exception. Other subscribers will still be notified ignoring the failed callback`;
(0, chai_1.expect)(consoleWarnStub.calledWith('model-validation/model-validation-service:', msg)).to.be.true;
subscriptionA.close();
subscriptionB.close();
});
it('get current validation state in subscription', async () => {
modelValidationService.addValidator(validatorError);
const subscriptionA = modelValidationService.subscribe();
let currentState;
subscriptionA.onValidationChanged = (modelId, _model, _diagnostic) => {
currentState = modelValidationService.getValidationState(modelId);
};
const diagnostic = await modelValidationService.validate('key1', {});
(0, chai_1.expect)(currentState).to.equal(diagnostic);
(0, chai_1.expect)(currentState?.severity).to.equal('error');
});
});
});
//# sourceMappingURL=model-validation-service.spec.js.map