Skip to content

Commit

Permalink
composer-common/lib/serializer unit tests (hyperledger-archives#1561)
Browse files Browse the repository at this point in the history
  • Loading branch information
Liam Grace authored Jul 14, 2017
1 parent 4d4913c commit a25cb7c
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 17 deletions.
16 changes: 0 additions & 16 deletions packages/composer-common/lib/serializer/resourcevalidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ class ResourceValidator {
* @private
*/
visit(thing, parameters) {
const obj = parameters.stack.peek();
this.log('visit', thing.toString() + ' with value (' + obj + ')');

if (thing instanceof EnumDeclaration) {
return this.visitEnumDeclaration(thing, parameters);
} else if (thing instanceof ClassDeclaration) {
Expand Down Expand Up @@ -402,19 +399,6 @@ class ResourceValidator {
}
}

/**
* @param {String} callSite - the location
* @param {String} message - the message to log.
*/
log(callSite, message) {
const log = false;
if(log) {
if(!message) {
message = '';
}
}
}

/**
* Throw a new error for a model violation.
* @param {string} id - the identifier of this instance.
Expand Down
10 changes: 10 additions & 0 deletions packages/composer-common/test/serializer/instancegenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,14 @@ describe('InstanceGenerator', () => {

});

describe('#findConcreteSubclass', () => {
it('should return the same declaration if it is abstract', () => {
const declaration = {
isAbstract: () => {
return false;
}
};
visitor.findConcreteSubclass(declaration).should.deep.equal(declaration);
});
});
});
11 changes: 11 additions & 0 deletions packages/composer-common/test/serializer/jsongenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,17 @@ describe('JSONGenerator', () => {
options.writer.getBuffer().should.equal('"next":[{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_1","next":[{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_2","next":[{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_3","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_1""resource:org.acme.SimpleAssetCircleArray#DOGE_2"]}"resource:org.acme.SimpleAssetCircleArray#DOGE_1"]},{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_3","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_1",{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_2","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_3""resource:org.acme.SimpleAssetCircleArray#DOGE_1"]}]}]},{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_2","next":[{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_3","next":[{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_1","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_2""resource:org.acme.SimpleAssetCircleArray#DOGE_3"]}"resource:org.acme.SimpleAssetCircleArray#DOGE_2"]},{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_1","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_2",{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_3","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_1""resource:org.acme.SimpleAssetCircleArray#DOGE_2"]}]}]},{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_3","next":[{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_1","next":[{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_2","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_3""resource:org.acme.SimpleAssetCircleArray#DOGE_1"]}"resource:org.acme.SimpleAssetCircleArray#DOGE_3"]},{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_2","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_3",{"$class":"org.acme.SimpleAssetCircleArray","assetId":"DOGE_1","next":["resource:org.acme.SimpleAssetCircleArray#DOGE_2""resource:org.acme.SimpleAssetCircleArray#DOGE_3"]}]}]}]');
});

it('should throw if stack contains something other than a Resource or Concept', () => {
jsonGenerator = new JSONGenerator(false, true);
let options = {
writer: new JSONWriter(),
stack: new TypedStack({})
};
options.stack.push('string');
(() => {
jsonGenerator.visitClassDeclaration(relationshipDeclaration4, options);
}).should.throw(/Expected a Resource or a Concept/);
});
});

describe('#getRelationshipText', () => {
Expand Down
144 changes: 143 additions & 1 deletion packages/composer-common/test/serializer/resourcevalidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ const Factory = require('../../lib/factory');
const TypedStack = require('../../lib/serializer/typedstack');
const TypeNotFoundException = require('../../lib/typenotfoundexception');
const ResourceValidator = require('../../lib/serializer/resourcevalidator');
const Identifiable = require('../../lib/model/identifiable');
const Field = require('../../lib/introspect/field');
const Resource = require('../../lib/model/resource');
const ModelUtil = require('../../lib/modelutil');
const ClassDeclaration = require('../../lib/introspect/classdeclaration');

const sinon = require('sinon');
const chai = require('chai');
chai.should();
chai.use(require('chai-things'));
Expand All @@ -30,6 +36,8 @@ describe('ResourceValidator', function () {
let resourceValidator;
let factory;

let sandbox;

const enumModelString = `namespace org.acme.enumerations
enum AnimalType {
o SHEEP_GOAT
Expand All @@ -54,6 +62,9 @@ describe('ResourceValidator', function () {
participant Employee identified by employeeId extends Person {
o String employeeId
}
concept Data {
o String name
}
`;

const levelTwoModel = `namespace org.acme.l2
Expand Down Expand Up @@ -91,6 +102,7 @@ describe('ResourceValidator', function () {
}`;

before(function () {
sandbox = sinon.sandbox.create();
resourceValidator = new ResourceValidator();
modelManager = new ModelManager();
factory = new Factory(modelManager);
Expand All @@ -105,6 +117,33 @@ describe('ResourceValidator', function () {

afterEach(function () {
modelManager.clearModelFiles();
sandbox.restore();
});

describe('#visit', () => {
it('should do nothing if unknown object given', () => {
const parameters = {
stack: new TypedStack({})
};

const thing = {
toString: () => {
return 'testing';
}
};
sandbox.stub(resourceValidator, 'visitEnumDeclaration');
sandbox.stub(resourceValidator, 'visitClassDeclaration');
sandbox.stub(resourceValidator, 'visitRelationshipDeclaration');
sandbox.stub(resourceValidator, 'visitField');

resourceValidator.visit(thing, parameters);

sinon.assert.notCalled(resourceValidator.visitEnumDeclaration);
sinon.assert.notCalled(resourceValidator.visitClassDeclaration);
sinon.assert.notCalled(resourceValidator.visitRelationshipDeclaration);
sinon.assert.notCalled(resourceValidator.visitField);

});
});

describe('#visitRelationshipDeclaration', function() {
Expand Down Expand Up @@ -267,7 +306,15 @@ describe('ResourceValidator', function () {
const vehicleDeclaration = modelManager.getType('org.acme.l3.Car');
const field = vehicleDeclaration.getProperty('vehicleTypes');
const parameters = { stack : typedStack, 'modelManager' : modelManager, rootResourceIdentifier : 'TEST' };
field.accept(resourceValidator,parameters );
field.accept(resourceValidator,parameters);
});

it('should throw if dataType is undefined', () => {
let mockField = sinon.createStubInstance(Field);
mockField.getName.returns('propName');
(() => {
resourceValidator.visitField(mockField, {stack: {pop: () => {return undefined;}}});
}).should.throw(/Model violation in instance undefined/);
});
});

Expand Down Expand Up @@ -373,5 +420,100 @@ describe('ResourceValidator', function () {
}).should.throw(/has an empty identifier/);
});

it('should report undeclared field if not identifiable', () => {
const data = factory.newConcept('org.acme.l1', 'Data');
data.name = 'name';
data.numberOfWipers = 2;
const typedStack = new TypedStack(data);
const conceptDeclaration = modelManager.getType('org.acme.l1.Data');
const parameters = { stack : typedStack, 'modelManager' : modelManager, rootResourceIdentifier : 'ABC' };

(() => {
resourceValidator.visitClassDeclaration(conceptDeclaration,parameters);
}).should.throw(/property named numberOfWipers which is not declared/);
});
});

describe('#reportFieldTypeViolation', () => {
let mockIdentifiable;
let mockField;
beforeEach(() => {
mockIdentifiable = sinon.createStubInstance(Identifiable);
mockField = sinon.createStubInstance(Field);
});

it('should get fully qualified type and name if Identifiable', () => {
mockIdentifiable.getFullyQualifiedType.returns('doge');
mockIdentifiable.getFullyQualifiedIdentifier.returns('com.doge');
(() => {
ResourceValidator.reportFieldTypeViolation('id', 'property', mockIdentifiable, mockField);
}).should.throw(/property has value com.doge \(doge\)/);
});

it('should not fail if strigify fails', () => {
// Crazy object to force JSON.strigify to throw
let obj = {};
obj.a = obj;

(() => {
ResourceValidator.reportFieldTypeViolation('id', 'property', obj, mockField);
}).should.throw(/id field property has value/);
});
});

describe('#checkItem', () => {
it('should throw if dataType is undefined', () => {
let mockField = sinon.createStubInstance(Field);
mockField.getName.returns('propName');
(() => {
resourceValidator.checkItem(undefined, mockField, {rootResourceIdentifier: 'identifier'});
}).should.throw(/Model violation in instance identifier field propName/);
});

it('should throw if class declaration is not found', () => {
let mockField = sinon.createStubInstance(Field);
mockField.isPrimitive.returns(false);
let mockIdentifiable = sinon.createStubInstance(Identifiable);
mockField.getName.returns('propName');

let stub = sinon.stub();
stub.onFirstCall().returns('classDeclaration');
stub.onSecondCall().throws('error');

let parameters = {rootResourceIdentifier: 'identifier', modelManager: {getType: stub}};

(() => {
resourceValidator.checkItem(mockIdentifiable, mockField, parameters);
}).should.throw(/Model violation in instance identifier field propName/);
});
});

describe('#checkRelationship', () => {
let mockResource;
let mockClassDeclaration;
let parameters;

beforeEach(() => {
mockResource = sinon.createStubInstance(Resource);
mockClassDeclaration = sinon.createStubInstance(ClassDeclaration);
mockClassDeclaration.isConcept.returns(false);
parameters = {rootResourceIdentifier: 'identifier', modelManager: {getType: () => {return mockClassDeclaration;}}};
sandbox.stub(ModelUtil, 'isAssignableTo').returns(true);
});


it('should not throw if Resource given and convertResourcesToRelationships is set', () => {
resourceValidator.options.convertResourcesToRelationships = true;
(() => {
resourceValidator.checkRelationship(parameters, {}, mockResource);
}).should.not.throw();
});

it('should not throw if Resource given and permitResourcesForRelationships is set', () => {
resourceValidator.options.permitResourcesForRelationships = true;
(() => {
resourceValidator.checkRelationship(parameters, {}, mockResource);
}).should.not.throw();
});
});
});
11 changes: 11 additions & 0 deletions packages/composer-common/test/serializer/typedstack.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,15 @@ describe('TypedStack', function () {
assert.throws( function() {ts.pop(Number);}, /.+Found: ROOT/, 'did not throw with expected message');
});
});

describe('#peek', () => {
it('should throw an error if value given is null', () => {
const ts = new TypedStack('ROOT');
(() => {
// Set the top of the stack to null
ts.stack = [null];
ts.peek(null);
}).should.throw(/Pop returned invalid data/);
});
});
});

0 comments on commit a25cb7c

Please sign in to comment.