Skip to content

Commit

Permalink
fix(core): detect ambiguous imports / declarations
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Roberts <code@rbrts.uk>
  • Loading branch information
mttrbrts committed Feb 5, 2024
1 parent 3b62a2c commit b19d943
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 57 deletions.
7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions packages/concerto-core/src/introspect/declaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,24 @@ class Declaration extends Decorated {
this.fqn = ModelUtil.getFullyQualifiedName(this.modelFile.getNamespace(), this.name);
}

/**
* Semantic validation of the structure of this decorated. Subclasses should
* override this method to impose additional semantic constraints on the
* contents/relations of fields.
*
* @param {...*} args the validation arguments
* @throws {IllegalModelException}
* @protected
*/
validate(...args) {
super.validate(...args);

// #648 - check for clashes against imported types
if (this.getModelFile().isImportedType(this.getName())){
throw new IllegalModelException(`Type '${this.getName()}' clashes with an imported type with the same name.`, this.modelFile, this.ast.location);
}
}

/**
* Returns the ModelFile that defines this class.
*
Expand Down
12 changes: 0 additions & 12 deletions packages/concerto-core/test/composer/composermodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,6 @@ const COMPOSER_MODEL =
*/
namespace system@1.0.0
event Event {
}
participant Participant {
}
asset Asset {
}
transaction Transaction {
}
/**
* Abstract system participant that all participants extend.
* Has no properties, and is soley used as a basis to model other assets.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

namespace com.testing@1.0.0

asset Asset identified by id {
asset MyAsset identified by id {
o String id
o String newValue
}

asset Asset identified by id {
asset MyAsset identified by id {
o String id
o String newValue
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

namespace com.testing@1.0.0

concept Concept{
concept MyConcept {
o String id
o String newValue
}

concept Concept{
concept MyConcept {
o String id
o String newValue
}
10 changes: 0 additions & 10 deletions packages/concerto-core/test/introspect/assetdeclaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,6 @@ describe('AssetDeclaration', () => {
asset.validate();
}).should.throw(/more than one field named/);
});

it('should throw an an IllegalModelException if its not a System Type and is called Asset', () => {
let asset = loadLastAssetDeclaration('test/data/parser/assetdeclaration.systypename.cto');
try {
asset.validate();
} catch (err) {
err.should.be.an.instanceOf(IllegalModelException);
err.message.should.match(/Asset is a reserved type name./);
}
});
});

describe('#getProperty', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/concerto-core/test/introspect/classdeclaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ describe('ClassDeclaration', () => {
describe('#_resolveSuperType', () => {

it('should return Asset if no super type', () => {
let classDecl = modelManager.getType('system@1.0.0.Asset');
let classDecl = modelManager.getType('concerto@1.0.0.Asset');
classDecl._resolveSuperType().should.not.be.null;
});

Expand Down Expand Up @@ -394,7 +394,7 @@ describe('ClassDeclaration', () => {
describe('#getSuperTypeDeclaration', () => {

it('should return Concept if no super type', () => {
let classDecl = modelManager.getType('system@1.0.0.Asset');
let classDecl = modelManager.getType('concerto@1.0.0.Asset');
classDecl.getSuperTypeDeclaration().should.not.be.null;
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/concerto-core/test/introspect/introspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('Introspector', () => {
let classDecl = introspector.getClassDeclarations();
const scalarDecl = classDecl.filter(declaration => declaration.isScalarDeclaration?.());
const mapDecl = classDecl.filter(declaration => declaration.isMapDeclaration?.());
classDecl.length.should.equal(44);
classDecl.length.should.equal(40);
scalarDecl.length.should.equal(0);
mapDecl.length.should.equal(0);
});
Expand Down
40 changes: 40 additions & 0 deletions packages/concerto-core/test/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,46 @@ describe('ModelFile', () => {
}).should.throw('Importing types from different versions ("1.0.0", "2.0.0") of the same namespace "org.freddos@2.0.0" is not permitted.');
});

it('should throw when declaring an ambiguous type', () => {
const myModelManager = new ModelManager();

const model1 = `namespace A@1.0.0
concept B {
o String name
}`;

const model2 = `namespace B@1.0.0
import A@1.0.0.{B}
concept B {
}`;


let modelFile1 = ParserUtil.newModelFile(myModelManager, model1);
myModelManager.addModelFile(modelFile1);

let modelFile2 = ParserUtil.newModelFile(myModelManager, model2);

(() => {
myModelManager.addModelFile(modelFile2);
}).should.throw('Type \'B\' clashes with an imported type with the same name.');
});

it('should recognise a user-space type with the same name as a Prototype', () => {
const model1 = `
namespace A@1.0.0
concept Transaction {}
`;

let modelFile1 = ParserUtil.newModelFile(modelManager, model1);

(() => {
modelManager.addModelFile(modelFile1);
}).should.throw('Type \'Transaction\' clashes with an imported type with the same name.');
});

});

describe('#getDefinitions', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('ParticipantDeclaration', () => {
describe('#getParticipantDeclarations', () => {
it('should get participants', () => {
let participants = modelManager.getParticipantDeclarations();
participants.should.have.lengthOf(3);
participants.should.have.lengthOf(2);
participants[0].declarationKind().should.equal('ParticipantDeclaration');
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/concerto-core/test/model/concept-identified.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('Concept Identifiers', function () {
--> Product product
}
transaction Request {}
event Event {}
event MyEvent {}
`, 'test.cto');
classDecl = modelManager.getType('org.accordproject@1.0.0.Order');
});
Expand Down Expand Up @@ -98,7 +98,7 @@ describe('Concept Identifiers', function () {
const txn = factory.newResource('org.accordproject@1.0.0', 'Request');
dayjs(txn.getTimestamp()).isValid().should.be.true;

const event = factory.newResource('org.accordproject@1.0.0', 'Event');
const event = factory.newResource('org.accordproject@1.0.0', 'MyEvent');
dayjs(event.getTimestamp()).isValid().should.be.true;
});

Expand Down
10 changes: 5 additions & 5 deletions packages/concerto-core/test/modelmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -882,11 +882,11 @@ concept Bar {
});

describe('#getDeclarations', () => {
const numberModelBaseAssets = 13;
const numberModelBaseAssets = 12;
const numberModelBaseEnums = 2;
const numberModelBaseParticipants = 5;
const numberModelBaseEvents = 1;
const numberModelBaseTransactions = 21;
const numberModelBaseParticipants = 4;
const numberModelBaseEvents = 0;
const numberModelBaseTransactions = 20;
const numberModelBaseConcepts = 2;

describe('#getAssetDeclarations', () => {
Expand Down Expand Up @@ -1122,7 +1122,7 @@ concept Bar {
modelManager.addModelFiles([composerModel, modelBase, farm2fork, concertoModel]);

modelManager.getModelFiles().length.should.equal(5);
modelManager.getModelFiles().map(mf => mf.getAllDeclarations()).flat().length.should.equal(69);
modelManager.getModelFiles().map(mf => mf.getAllDeclarations()).flat().length.should.equal(65);

const filtered = modelManager.filter(declaration => declaration.getFullyQualifiedName() === 'org.accordproject.test@1.0.0.Product');

Expand Down

0 comments on commit b19d943

Please sign in to comment.