Skip to content

Commit

Permalink
test(decorator-validation): add more tests
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Selman <[email protected]>
  • Loading branch information
dselman committed Oct 9, 2024
1 parent f954ee7 commit c35c95a
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 29 deletions.
21 changes: 16 additions & 5 deletions packages/concerto-core/lib/decoratormanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,19 +375,30 @@ class DecoratorManager {
this.migrateAndValidate(modelManager, decoratorCommandSet, options?.migrate, options?.validate, options?.validateCommands);

// we create synthetic imports for all decorator declarations
// along with any of their type reference arguments
const decoratorImports = decoratorCommandSet.commands.map(command => {
return {
return [{
$class: `${MetaModelNamespace}.ImportType`,
name: command.decorator.name,
namespace: command.decorator.namespace ? command.decorator.namespace : options?.defaultNamespace
};
}).filter(i => i.namespace);
}].concat(command.decorator.arguments ? command.decorator.arguments?.filter(a => a.type)
.map(a => {
return {
$class: `${MetaModelNamespace}.ImportType`,
name: a.type.name,
namespace: a.type.namespace ? a.type.namespace : options?.defaultNamespace
};
})
: []);
}).flat().filter(i => i.namespace);
const { namespaceCommandsMap, declarationCommandsMap, propertyCommandsMap, mapElementCommandsMap, typeCommandsMap } = this.getDecoratorMaps(decoratorCommandSet);
const ast = modelManager.getAst(true, true);
const decoratedAst = JSON.parse(JSON.stringify(ast));
decoratedAst.models.forEach((model) => {
// add the imports for all decorators, in case they get added below
model.imports = model.imports ? model.imports.concat(decoratorImports) : decoratorImports;
// remove the imports for types defined in this namespace
const neededImports = decoratorImports.filter(i => i.namespace !== model.namespace);
// add the imports for decorators, in case they get added below
model.imports = model.imports ? model.imports.concat(neededImports) : neededImports;
model.declarations.forEach((decl) => {
const declarationDecoratorCommandSets = [];
const { name: declarationName, $class: $classForDeclaration } = decl;
Expand Down
8 changes: 2 additions & 6 deletions packages/concerto-core/lib/introspect/decorator.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class Decorator {
this.handleError(validationOptions.invalidDecorator, err);
}
else {
if (!ModelUtil.isAssignableTo(mf, typeDecl.getFullyQualifiedName(), property)) {
if (!ModelUtil.isAssignableTo(typeDecl.getModelFile(), typeDecl.getFullyQualifiedName(), property)) {
const err = `Decorator ${this.getName()} references a type ${arg.name} which cannot be assigned to the declared type ${property.getFullyQualifiedTypeName()}`;
this.handleError(validationOptions.invalidDecorator, err);
}
Expand All @@ -183,14 +183,10 @@ class Decorator {
}
}
catch (err) {
console.log(err);

Check warning on line 186 in packages/concerto-core/lib/introspect/decorator.js

View workflow job for this annotation

GitHub Actions / Unit Tests (20.x, macos-latest)

Unexpected console statement

Check warning on line 186 in packages/concerto-core/lib/introspect/decorator.js

View workflow job for this annotation

GitHub Actions / Unit Tests (18.x, macos-latest)

Unexpected console statement

Check warning on line 186 in packages/concerto-core/lib/introspect/decorator.js

View workflow job for this annotation

GitHub Actions / Unit Tests (20.x, ubuntu-latest)

Unexpected console statement

Check warning on line 186 in packages/concerto-core/lib/introspect/decorator.js

View workflow job for this annotation

GitHub Actions / Unit Tests (18.x, ubuntu-latest)

Unexpected console statement

Check warning on line 186 in packages/concerto-core/lib/introspect/decorator.js

View workflow job for this annotation

GitHub Actions / Unit Tests (20.x, windows-latest)

Unexpected console statement

Check warning on line 186 in packages/concerto-core/lib/introspect/decorator.js

View workflow job for this annotation

GitHub Actions / Unit Tests (18.x, windows-latest)

Unexpected console statement
this.handleError(validationOptions.missingDecorator, err);
}
}
// check that all type ref arguments can be resolved
const typeRefs = this.arguments.filter(a => a?.type === 'Identifier');
typeRefs.forEach(typeRef => {
mf.resolveType(`Decorator ${this.getName()} on ${this.getParent().getName()}`, typeRef.name);
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace [email protected]

concept Person {
o String firstName
o String lastName
}
37 changes: 37 additions & 0 deletions packages/concerto-core/test/data/decoratorcommands/validated.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$class" : "[email protected]",
"name" : "validated",
"version": "1.0.0",
"commands" : [
{
"$class" : "[email protected]",
"type" : "UPSERT",
"target" : {
"$class" : "[email protected]",
"type" : "[email protected]"
},
"decorator" : {
"$class" : "[email protected]",
"name" : "Form",
"arguments" : [
{
"$class" : "[email protected]",
"value" : "inputType"
},
{
"$class" : "[email protected]",
"value" : "text"
},
{
"$class" : "[email protected]",
"type" : {
"$class" : "[email protected]",
"name" : "HR",
"namespace" : "[email protected]"
}
}
]
}
}
]
}
33 changes: 18 additions & 15 deletions packages/concerto-core/test/decoratormanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,40 +160,43 @@ describe('DecoratorManager', () => {
// create a model manager with decorator validation ON
const testModelManager = new ModelManager({strict:true, decoratorValidation: {missingDecorator: 'error', invalidDecorator: 'error'} });

// add a model that defines types that are *referenced* by decorators
// declared in the decorator command set web.json
testModelManager.addCTOModel(`namespace [email protected]
abstract concept Category {}
concept HR extends Category {}
`, 'categories.cto');

// add the model that declares the decorators we can use
// in the model and in decorator command sets
testModelManager.addCTOModel(`namespace [email protected]
import [email protected]
concept PII extends Decorator {}
concept Form extends Decorator {
o String key
o String value
o Concept category
}
concept New extends Decorator {}
concept UnversionedNamespace extends Decorator {}
concept Address extends Decorator {}
concept IsValid extends Decorator {}
`, 'decorators.cto');

// add the domain model
const modelText = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/test.cto'), 'utf-8');
testModelManager.addCTOModel(modelText, 'test.cto');
const modelText = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/validated.cto'), 'utf-8');
testModelManager.addCTOModel(modelText, 'validated.cto');

const cat = testModelManager.getType('[email protected]');
cat.should.not.be.null;

// load the decorator command set
const dcs = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/web.json'), 'utf-8');
const dcs = fs.readFileSync(path.join(__dirname,'/data/decoratorcommands/validated.json'), 'utf-8');

// decorator the models, using the default namespace [email protected] for decorator
// commands that do not supply an explicit namespaces for their decorators
const decoratedModelManager = DecoratorManager.decorateModels( testModelManager, JSON.parse(dcs),
{validate: true, validateCommands: true, migrate: true, defaultNamespace: '[email protected]'});

const ssnDecl = decoratedModelManager.getType('[email protected]');
ssnDecl.should.not.be.null;
ssnDecl.getDecorator('PII').should.not.be.null;

const decl = decoratedModelManager.getType('[email protected]');
decl.should.not.be.null;
decl.getDecorator('Editable').should.not.be.null;
const personDecl = decoratedModelManager.getType('[email protected]');
personDecl.should.not.be.null;
personDecl.getProperty('firstName').getDecorator('Form').should.not.be.null;
});

/*
Expand Down
8 changes: 6 additions & 2 deletions packages/concerto-core/test/introspect/decorators.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,15 @@ describe('Decorators', () => {
it('should fail to validate type refs that are not defined locally', () => {

(() => {
const modelManager = new ModelManager();
const modelManager = new ModelManager({
decoratorValidation: {
missingDecorator: 'error'
}
});
Util.addComposerModel(modelManager);
let modelDefinitions = fs.readFileSync('test/data/decorators/invalid-typeref.cto', 'utf8');
modelManager.addCTOModel(modelDefinitions);
}).should.throw(/Undeclared type "Missing"/);
}).should.throw(/IllegalModelException: Undeclared type/);
});

it('should fail to validate type refs that are not in imported namespace', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/concerto-core/test/validateDecorators.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ concept HR extends Category {}
concept Name {}
concept Hide extends Decorator {
o Category category
o Category category optional
}
concept Person {
Expand Down

0 comments on commit c35c95a

Please sign in to comment.