Skip to content

Commit

Permalink
composer-common/lib/introspect unit tests (hyperledger-archives#1507)
Browse files Browse the repository at this point in the history
* Added assetdeclaration unit test

* composer-common/lib/introspect unit tests
  • Loading branch information
Liam Grace authored Jul 7, 2017
1 parent f993330 commit c5fdad2
Show file tree
Hide file tree
Showing 18 changed files with 408 additions and 8 deletions.
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

0 comments on commit c5fdad2

Please sign in to comment.