Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,8 @@ class RelationshipDeclaration extends Property {
throw new IllegalModelException('Relationship ' + this.getName() + ' points to a missing type ' + this.getFullyQualifiedTypeName(), classDecl.getModelFile(), this.ast.location);
}


if ((namespace === ModelUtil.getSystemNamespace()) && classDecl.isEvent()) {
// Transaction relationship in event, continue
} else if((namespace === ModelUtil.getSystemNamespace()) && classDeclaration.isSystemRelationshipTarget() === false) {
throw new IllegalModelException('Relationship ' + this.getName() + ' must be to an asset, participant or transaction, but is to ' + this.getFullyQualifiedTypeName(), classDecl.getModelFile(), this.ast.location);
} else if(classDeclaration.isRelationshipTarget() === false) {
throw new IllegalModelException('Relationship ' + this.getName() + ' must be to an asset or participant, but is to ' + this.getFullyQualifiedTypeName(), classDecl.getModelFile(), this.ast.location);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace com.testing

asset Asset identified by id {
o String id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace com.testing
concept Test {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace com.testing

transaction Transaction {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ import com.testing.parent.Super

participant Sub extends Super { }

participant Sub2 extends Super { }
participant Sub2 extends Super { }

concept Test {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace com.testing

event Event identified by eventId{
o String eventId
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace com.testing

participant Participant identified by participantId {
o String participantId
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace com.testing

participant P identified by pId {
o String pId
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace com.testing

asset A identified by AId {
o String AId
--> Testing test
}
11 changes: 11 additions & 0 deletions packages/composer-common/test/introspect/assetdeclaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

'use strict';

const IllegalModelException = require('../../lib/introspect/illegalmodelexception');
const AssetDeclaration = require('../../lib/introspect/assetdeclaration');
const ClassDeclaration = require('../../lib/introspect/classdeclaration');
const ModelFile = require('../../lib/introspect/modelfile');
Expand Down Expand Up @@ -129,6 +130,16 @@ 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('#getSuperType', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ describe('BaseModelException', function () {
exc.stack.should.be.a('string');
});

it('should use messahe over fulMessage', () => {
let exc = new BaseModelException('message', {start: 1, end: 2});
exc.message.should.equal('message');
});

it('should handle a lack of support for stack traces', function () {
let captureStackTrace = Error.captureStackTrace;
Error.captureStackTrace = null;
Expand Down
48 changes: 48 additions & 0 deletions packages/composer-common/test/introspect/classdeclaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

'use strict';

const IllegalModelException = require('../../lib/introspect/illegalmodelexception');
const ClassDeclaration = require('../../lib/introspect/classdeclaration');
const AssetDeclaration = require('../../lib/introspect/assetdeclaration');
const EnumDeclaration = require('../../lib/introspect/enumdeclaration');
Expand Down Expand Up @@ -139,6 +140,17 @@ describe('ClassDeclaration', () => {
}).should.throw(/Duplicate class/);
});

it('should throw when not abstract, not enum and not concept without an identifier', () => {
let asset = loadLastDeclaration('test/data/parser/classdeclaration.noidentifier.cto', TransactionDeclaration);
asset.superType = null;
try {
asset.validate();
} catch (err) {
err.should.be.an.instanceOf(IllegalModelException);
should.exist(err.message);
err.message.should.match(/Class Transaction is not declared as abstract. It must define an identifying field./);
}
});
});

describe('#accept', () => {
Expand Down Expand Up @@ -309,4 +321,40 @@ describe('ClassDeclaration', () => {
});
});

describe('#isEvent', () => {
const modelFileNames = [
'test/data/parser/classdeclaration.participantwithparents.parent.cto',
'test/data/parser/classdeclaration.participantwithparents.child.cto'
];
let modelManager;

beforeEach(() => {
modelManager = new ModelManager();
const modelFiles = loadModelFiles(modelFileNames, modelManager);
modelManager.addModelFiles(modelFiles);
});
it('should return false', () => {
const testClass = modelManager.getType('com.testing.child.Sub');
testClass.isEvent().should.be.false;

});
});

describe('#isRelationshipTarget', () => {
const modelFileNames = [
'test/data/parser/classdeclaration.isrelationshiptarget.cto',
];
let modelManager;

beforeEach(() => {
modelManager = new ModelManager();
const modelFiles = loadModelFiles(modelFileNames, modelManager);
modelManager.addModelFiles(modelFiles);
});
it('should return false', () => {
const testClass = modelManager.getType('com.testing.Test');
testClass.isRelationshipTarget().should.be.false;

});
});
});
43 changes: 43 additions & 0 deletions packages/composer-common/test/introspect/eventdeclaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,44 @@ const sinon = require('sinon');

describe('EventDeclaration', () => {

let mockModelManager;
let mockSystemEvent;

/**
* Load an arbitrary number of model files.
* @param {String[]} modelFileNames array of model file names.
* @param {ModelManager} modelManager the model manager to which the created model files will be registered.
* @return {ModelFile[]} array of loaded model files, matching the supplied arguments.
*/
const loadModelFiles = (modelFileNames, modelManager) => {
const modelFiles = [];
for (let modelFileName of modelFileNames) {
const modelDefinitions = fs.readFileSync(modelFileName, 'utf8');
const modelFile = new ModelFile(modelManager, modelDefinitions);
modelFiles.push(modelFile);
}
modelManager.addModelFiles(modelFiles, modelFileNames);
return modelFiles;
};

const loadModelFile = (modelFileName) => {
return loadModelFiles([modelFileName], mockModelManager)[0];
};

const loadLastDeclaration = (modelFileName, type) => {
const modelFile = loadModelFile(modelFileName);
const declarations = modelFile.getDeclarations(type);
return declarations[declarations.length - 1];
};

let sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();
mockModelManager = sinon.createStubInstance(ModelManager);
mockSystemEvent = sinon.createStubInstance(EventDeclaration);
mockSystemEvent.getFullyQualifiedName.returns('org.hyperledger.composer.system.Event');
mockModelManager.getSystemTypes.returns([mockSystemEvent]);
});

afterEach(() => {
Expand All @@ -50,6 +84,15 @@ describe('EventDeclaration', () => {
});
});

describe('#validate', () => {
it('should throw if event is not a system type but named event', () => {
let event = loadLastDeclaration('test/data/parser/eventdeclaration.systypename.cto', EventDeclaration);
event.superType = null;
(() => {
event.validate();
}).should.throw(/Event is a reserved type name./);
});
});


describe('#parse', () => {
Expand Down
35 changes: 35 additions & 0 deletions packages/composer-common/test/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,17 @@ describe('ModelFile', () => {
mf.getType('String').should.equal('String');
});

it('should return false if imported, non primative\'s modelFile doesn\'t exist', () => {
const ast = {
namespace: 'org.acme',
body: [ ]
};
sandbox.stub(parser, 'parse').returns(ast);
let mf = new ModelFile(mockModelManager, 'fake');
mf.isImportedType = () => { return true; };
mf.resolveImport = () => { return 'org.acme'; };
should.not.exist(mf.getType('TNTAsset'));
});
});

describe('#getAssetDeclaration', () => {
Expand Down Expand Up @@ -542,4 +553,28 @@ describe('ModelFile', () => {

});

describe('#getFullyQualifiedTypeName', () => {
it('should return null if not prmative, imported or local type', () => {
const ast = {
namespace: 'org.acme',
body: [ ]
};
sandbox.stub(parser, 'parse').returns(ast);
let mf = new ModelFile(mockModelManager, 'fake');
mf.isImportedType = () => { return false; };
mf.isLocalType = () => { return false; };
should.not.exist(mf.getFullyQualifiedTypeName('TNTAsset'));
});

it('should return the type name if its a primative type', () => {
const ast = {
namespace: 'org.acme',
body: [ ]
};
sandbox.stub(parser, 'parse').returns(ast);
let modelFile = new ModelFile(mockModelManager, 'something');

modelFile.getFullyQualifiedTypeName('String').should.equal('String');
});
});
});
12 changes: 12 additions & 0 deletions packages/composer-common/test/introspect/numbervalidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,17 @@ describe('NumberValidator', () => {
v.validate('id', 101);
}).should.throw(/org.acme.myField: Value is outside upper bound 101/);
});

it('should do nothing if no value is given', () => {
let v = new NumberValidator(mockField, VALID_UPPER_AND_LOWER_BOUND_AST);
v.validate();
});
});

describe('#toString', () => {
it('should return the correct string', () => {
let v = new NumberValidator(mockField, VALID_UPPER_AND_LOWER_BOUND_AST);
v.toString().should.equal(`NumberValidator lower: ${VALID_UPPER_AND_LOWER_BOUND_AST.lower} upper: ${VALID_UPPER_AND_LOWER_BOUND_AST.upper}`);
});
});
});
97 changes: 97 additions & 0 deletions packages/composer-common/test/introspect/participantdeclaration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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.
*/

'use strict';

const ParticipantDeclaration = require('../../lib/introspect/participantdeclaration');
const ClassDeclaration = require('../../lib/introspect/classdeclaration');
const ModelFile = require('../../lib/introspect/modelfile');
const ModelManager = require('../../lib/modelmanager');
const fs = require('fs');

require('chai').should();
const sinon = require('sinon');

describe('ParticipantDeclaration', () => {

let mockModelManager;
let mockClassDeclaration;
let mockSystemParticipant;
let sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();
mockModelManager = sinon.createStubInstance(ModelManager);
mockSystemParticipant = sinon.createStubInstance(ParticipantDeclaration);
mockSystemParticipant.getFullyQualifiedName.returns('org.hyperledger.composer.system.Participant');
mockModelManager.getSystemTypes.returns([mockSystemParticipant]);
mockClassDeclaration = sinon.createStubInstance(ClassDeclaration);
mockModelManager.getType.returns(mockClassDeclaration);
mockClassDeclaration.getProperties.returns([]);
});

afterEach(() => {
sandbox.restore();
});

let loadParticipantDeclaration = (modelFileName) => {
let modelDefinitions = fs.readFileSync(modelFileName, 'utf8');
let modelFile = new ModelFile(mockModelManager, modelDefinitions);
let assets = modelFile.getParticipantDeclarations();
assets.should.have.lengthOf(1);

return assets[0];
};

describe('#constructor', () => {

it('should throw if modelFile not specified', () => {
(() => {
new ParticipantDeclaration(null, {});
}).should.throw(/required/);
});

it('should throw if ast not specified', () => {
let mockModelFile = sinon.createStubInstance(ModelFile);
(() => {
new ParticipantDeclaration(mockModelFile, null);
}).should.throw(/required/);
});

});

describe('#isRelationshipTarget', () => {
it('should return true', () => {
let p = loadParticipantDeclaration('test/data/parser/participantdeclaration.valid.cto');
p.isRelationshipTarget().should.be.true;
});
});

describe('#getSystemType', () => {
it('should return Participant', () => {
let p = loadParticipantDeclaration('test/data/parser/participantdeclaration.valid.cto');
p.getSystemType().should.equal('Participant');
});
});

describe('#validate', () => {
it('should throw error if system type and name Participant', () => {
let p = loadParticipantDeclaration('test/data/parser/participantdeclaration.systypename.cto');
(() => {
p.validate();
}).should.throw(/Participant is a reserved type name./);
});
});

});
Loading