test: fix broken Role tests

Rework the test suite to always report errors and correctly signal
when async tests are done.

This should prevent spurious test failures on CI servers that are
difficult to troubleshoot, because the error is reported for different
test case.
This commit is contained in:
Miroslav Bajtoš 2016-08-03 15:55:29 +02:00
parent fea3b781a0
commit ed953a4c6f
1 changed files with 352 additions and 177 deletions

View File

@ -6,11 +6,6 @@
var assert = require('assert'); var assert = require('assert');
var sinon = require('sinon'); var sinon = require('sinon');
var loopback = require('../index'); var loopback = require('../index');
var Role = loopback.Role;
var RoleMapping = loopback.RoleMapping;
var User = loopback.User;
var Application = loopback.Application;
var ACL = loopback.ACL;
var async = require('async'); var async = require('async');
var expect = require('chai').expect; var expect = require('chai').expect;
var Promise = require('bluebird'); var Promise = require('bluebird');
@ -20,17 +15,29 @@ function checkResult(err, result) {
} }
describe('role model', function() { describe('role model', function() {
var ds; var app, Role, RoleMapping, User, Application, ACL;
beforeEach(function() { beforeEach(function() {
ds = loopback.createDataSource({connector: 'memory'}); // Use local app registry to ensure models are isolated to avoid
// Re-attach the models so that they can have isolated store to avoid
// pollutions from other tests // pollutions from other tests
ACL.attachTo(ds); app = loopback({ localRegistry: true, loadBuiltinModels: true });
User.attachTo(ds); app.dataSource('db', { connector: 'memory' });
Role.attachTo(ds);
RoleMapping.attachTo(ds); ACL = app.registry.getModel('ACL');
Application.attachTo(ds); app.model(ACL, { dataSource: 'db' });
User = app.registry.getModel('User');
app.model(User, { dataSource: 'db' });
Role = app.registry.getModel('Role');
app.model(Role, { dataSource: 'db' });
RoleMapping = app.registry.getModel('RoleMapping');
app.model(RoleMapping, { dataSource: 'db' });
Application = app.registry.getModel('Application');
app.model(Application, { dataSource: 'db' });
ACL.roleModel = Role; ACL.roleModel = Role;
ACL.roleMappingModel = RoleMapping; ACL.roleMappingModel = RoleMapping;
ACL.userModel = User; ACL.userModel = User;
@ -40,57 +47,93 @@ describe('role model', function() {
Role.applicationModel = Application; Role.applicationModel = Application;
}); });
it('should define role/role relations', function() { it('should define role/role relations', function(done) {
Role.create({name: 'user'}, function(err, userRole) { Role.create({ name: 'user' }, function(err, userRole) {
Role.create({name: 'admin'}, function(err, adminRole) { if (err) return done(err);
userRole.principals.create({principalType: RoleMapping.ROLE, principalId: adminRole.id}, function(err, mapping) { Role.create({ name: 'admin' }, function(err, adminRole) {
if (err) return done(err);
userRole.principals.create(
{ principalType: RoleMapping.ROLE, principalId: adminRole.id },
function(err, mapping) {
if (err) return done(err);
async.parallel([
function(next) {
Role.find(function(err, roles) { Role.find(function(err, roles) {
if (err) return next(err);
assert.equal(roles.length, 2); assert.equal(roles.length, 2);
next();
}); });
},
function(next) {
RoleMapping.find(function(err, mappings) { RoleMapping.find(function(err, mappings) {
if (err) return next(err);
assert.equal(mappings.length, 1); assert.equal(mappings.length, 1);
assert.equal(mappings[0].principalType, RoleMapping.ROLE); assert.equal(mappings[0].principalType, RoleMapping.ROLE);
assert.equal(mappings[0].principalId, adminRole.id); assert.equal(mappings[0].principalId, adminRole.id);
next();
}); });
},
function(next) {
userRole.principals(function(err, principals) { userRole.principals(function(err, principals) {
if (err) return next(err);
assert.equal(principals.length, 1); assert.equal(principals.length, 1);
next();
}); });
},
function(next) {
userRole.roles(function(err, roles) { userRole.roles(function(err, roles) {
if (err) return next(err);
assert.equal(roles.length, 1); assert.equal(roles.length, 1);
next();
});
},
], done);
}); });
}); });
}); });
}); });
}); it('should define role/user relations', function(done) {
it('should define role/user relations', function() {
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) { User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
if (err) return done(err);
Role.create({ name: 'userRole' }, function(err, role) { Role.create({ name: 'userRole' }, function(err, role) {
if (err) return done(err);
role.principals.create({ principalType: RoleMapping.USER, principalId: user.id }, role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
function(err, p) { function(err, p) {
if (err) return done(err);
async.parallel([
function(next) {
Role.find(function(err, roles) { Role.find(function(err, roles) {
assert(!err); if (err) return next(err);
assert.equal(roles.length, 1); assert.equal(roles.length, 1);
assert.equal(roles[0].name, 'userRole'); assert.equal(roles[0].name, 'userRole');
next();
}); });
},
function(next) {
role.principals(function(err, principals) { role.principals(function(err, principals) {
assert(!err); if (err) return next(err);
assert.equal(principals.length, 1); assert.equal(principals.length, 1);
assert.equal(principals[0].principalType, RoleMapping.USER); assert.equal(principals[0].principalType, RoleMapping.USER);
assert.equal(principals[0].principalId, user.id); assert.equal(principals[0].principalId, user.id);
next();
}); });
},
function(next) {
role.users(function(err, users) { role.users(function(err, users) {
assert(!err); if (err) return next(err);
assert.equal(users.length, 1); assert.equal(users.length, 1);
assert.equal(users[0].id, user.id); assert.equal(users[0].id, user.id);
next();
});
},
], done);
}); });
}); });
}); });
}); });
});
it('should not allow duplicate role name', function(done) { it('should not allow duplicate role name', function(done) {
Role.create({ name: 'userRole' }, function(err, role) { Role.create({ name: 'userRole' }, function(err, role) {
if (err) return done(err); if (err) return done(err);
@ -107,81 +150,146 @@ describe('role model', function() {
}); });
}); });
it('should automatically generate role id', function() { it('should automatically generate role id', function(done) {
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) { User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
if (err) return done(err);
Role.create({ name: 'userRole' }, function(err, role) { Role.create({ name: 'userRole' }, function(err, role) {
if (err) return done(err);
assert(role.id); assert(role.id);
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, function(err, p) { role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
function(err, p) {
if (err) return done(err);
assert(p.id); assert(p.id);
assert.equal(p.roleId, role.id); assert.equal(p.roleId, role.id);
async.parallel([
function(next) {
Role.find(function(err, roles) { Role.find(function(err, roles) {
assert(!err); if (err) return next(err);
assert.equal(roles.length, 1); assert.equal(roles.length, 1);
assert.equal(roles[0].name, 'userRole'); assert.equal(roles[0].name, 'userRole');
next();
}); });
},
function(next) {
role.principals(function(err, principals) { role.principals(function(err, principals) {
assert(!err); if (err) return next(err);
assert.equal(principals.length, 1); assert.equal(principals.length, 1);
assert.equal(principals[0].principalType, RoleMapping.USER); assert.equal(principals[0].principalType, RoleMapping.USER);
assert.equal(principals[0].principalId, user.id); assert.equal(principals[0].principalId, user.id);
next();
}); });
},
function(next) {
role.users(function(err, users) { role.users(function(err, users) {
assert(!err); if (err) return next(err);
assert.equal(users.length, 1); assert.equal(users.length, 1);
assert.equal(users[0].id, user.id); assert.equal(users[0].id, user.id);
}); });
next();
},
], done);
});
}); });
}); });
}); });
}); it('should support getRoles() and isInRole()', function(done) {
it('should support getRoles() and isInRole()', function() {
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) { User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
if (err) return done(err);
Role.create({ name: 'userRole' }, function(err, role) { Role.create({ name: 'userRole' }, function(err, role) {
if (err) return done(err);
role.principals.create({ principalType: RoleMapping.USER, principalId: user.id }, role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
function(err, p) { function(err, p) {
Role.isInRole('userRole', { principalType: RoleMapping.USER, principalId: user.id }, if (err) return done(err);
function(err, exists) { async.series([
assert(!err && exists === true); function(next) {
Role.isInRole(
'userRole',
{ principalType: RoleMapping.USER, principalId: user.id },
function(err, inRole) {
if (err) return next(err);
// NOTE(bajtos) Apparently isRole is not a boolean,
// but the matchin role object instead
assert(!!inRole);
next();
}); });
},
Role.isInRole('userRole', {principalType: RoleMapping.APP, principalId: user.id}, function(err, exists) { function(next) {
assert(!err && exists === false); Role.isInRole(
'userRole',
{ principalType: RoleMapping.APP, principalId: user.id },
function(err, inRole) {
if (err) return next(err);
assert(!inRole);
next();
}); });
},
Role.isInRole('userRole', {principalType: RoleMapping.USER, principalId: 100}, function(err, exists) { function(next) {
assert(!err && exists === false); Role.isInRole(
'userRole',
{ principalType: RoleMapping.USER, principalId: 100 },
function(err, inRole) {
if (err) return next(err);
assert(!inRole);
next();
}); });
},
Role.getRoles({principalType: RoleMapping.USER, principalId: user.id}, function(err, roles) { function(next) {
assert.equal(roles.length, 3); // everyone, authenticated, userRole Role.getRoles(
assert(roles.indexOf(role.id) >= 0); { principalType: RoleMapping.USER, principalId: user.id },
assert(roles.indexOf(Role.EVERYONE) >= 0); function(err, roles) {
assert(roles.indexOf(Role.AUTHENTICATED) >= 0); if (err) return next(err);
expect(roles).to.eql([
Role.AUTHENTICATED,
Role.EVERYONE,
role.id,
]);
next();
}); });
Role.getRoles({principalType: RoleMapping.APP, principalId: user.id}, function(err, roles) { },
assert.equal(roles.length, 2); function(next) {
assert(roles.indexOf(Role.EVERYONE) >= 0); Role.getRoles(
assert(roles.indexOf(Role.AUTHENTICATED) >= 0); { principalType: RoleMapping.APP, principalId: user.id },
function(err, roles) {
if (err) return next(err);
expect(roles).to.eql([
Role.AUTHENTICATED,
Role.EVERYONE,
]);
next();
}); });
Role.getRoles({principalType: RoleMapping.USER, principalId: 100}, function(err, roles) { },
assert.equal(roles.length, 2); function(next) {
assert(roles.indexOf(Role.EVERYONE) >= 0); Role.getRoles(
assert(roles.indexOf(Role.AUTHENTICATED) >= 0); { principalType: RoleMapping.USER, principalId: 100 },
function(err, roles) {
if (err) return next(err);
expect(roles).to.eql([
Role.AUTHENTICATED,
Role.EVERYONE,
]);
next();
}); });
Role.getRoles({principalType: RoleMapping.USER, principalId: null}, function(err, roles) { },
assert.equal(roles.length, 2); function(next) {
assert(roles.indexOf(Role.EVERYONE) >= 0); Role.getRoles(
assert(roles.indexOf(Role.UNAUTHENTICATED) >= 0); { principalType: RoleMapping.USER, principalId: null },
function(err, roles) {
if (err) return next(err);
expect(roles).to.eql([
Role.UNAUTHENTICATED,
Role.EVERYONE,
]);
next();
});
},
], done);
}); });
}); });
}); });
}); });
}); it('should support owner role resolver', function(done) {
it('should support owner role resolver', function() {
Role.registerResolver('returnPromise', function(role, context) { Role.registerResolver('returnPromise', function(role, context) {
return new Promise(function(resolve) { return new Promise(function(resolve) {
process.nextTick(function() { process.nextTick(function() {
@ -190,62 +298,121 @@ describe('role model', function() {
}); });
}); });
var Album = ds.createModel('Album', { var Album = app.registry.createModel('Album', {
name: String, name: String,
userId: Number userId: Number,
}, { }, {
relations: { relations: {
user: { user: {
type: 'belongsTo', type: 'belongsTo',
model: 'User', model: 'User',
foreignKey: 'userId' foreignKey: 'userId',
} },
} },
}); });
app.model(Album, { dataSource: 'db' });
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) { User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
Role.isInRole('returnPromise', { principalType: ACL.USER, principalId: user.id }, if (err) return done(err);
async.parallel([
function(next) {
Role.isInRole(
'returnPromise',
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) { function(err, yes) {
assert(!err && yes); if (err) return next(err);
assert(yes);
next();
}); });
},
Role.isInRole(Role.AUTHENTICATED, { principalType: ACL.USER, principalId: user.id }, function(next) {
Role.isInRole(
Role.AUTHENTICATED,
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) { function(err, yes) {
assert(!err && yes); if (err) next(err);
assert(yes);
next();
}); });
},
Role.isInRole(Role.AUTHENTICATED, { principalType: ACL.USER, principalId: null }, function(next) {
Role.isInRole(
Role.AUTHENTICATED,
{ principalType: ACL.USER, principalId: null },
function(err, yes) { function(err, yes) {
assert(!err && !yes); if (err) next(err);
assert(!yes);
next();
}); });
},
Role.isInRole(Role.UNAUTHENTICATED, {principalType: ACL.USER, principalId: user.id}, function(err, yes) { function(next) {
assert(!err && !yes); Role.isInRole(
Role.UNAUTHENTICATED,
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) {
if (err) return next(err);
assert(!yes);
next();
}); });
Role.isInRole(Role.UNAUTHENTICATED, {principalType: ACL.USER, principalId: null}, function(err, yes) { },
assert(!err && yes); function(next) {
Role.isInRole(
Role.UNAUTHENTICATED,
{ principalType: ACL.USER, principalId: null },
function(err, yes) {
if (err) return next(err);
assert(yes);
next();
}); });
},
Role.isInRole(Role.EVERYONE, {principalType: ACL.USER, principalId: user.id}, function(err, yes) { function(next) {
assert(!err && yes); Role.isInRole(
Role.EVERYONE,
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) {
if (err) return next(err);
assert(yes);
next();
}); });
},
Role.isInRole(Role.EVERYONE, {principalType: ACL.USER, principalId: null}, function(err, yes) { function(next) {
assert(!err && yes); Role.isInRole(
Role.EVERYONE,
{ principalType: ACL.USER, principalId: null },
function(err, yes) {
if (err) return next(err);
assert(yes);
next();
}); });
},
function(next) {
Album.create({ name: 'Album 1', userId: user.id }, function(err, album1) { Album.create({ name: 'Album 1', userId: user.id }, function(err, album1) {
var role = { principalType: ACL.USER, principalId: user.id, model: Album, id: album1.id }; if (err) return done(err);
var role = {
principalType: ACL.USER, principalId: user.id,
model: Album, id: album1.id,
};
Role.isInRole(Role.OWNER, role, function(err, yes) { Role.isInRole(Role.OWNER, role, function(err, yes) {
assert(!err && yes); if (err) return next(err);
}); assert(yes);
Album.create({name: 'Album 2'}, function(err, album2) {
Role.isInRole(Role.OWNER, {principalType: ACL.USER, principalId: user.id, model: Album, id: album2.id}, function(err, yes) { Album.create({ name: 'Album 2' }, function(err, album2) {
assert(!err && !yes); if (err) return next(err);
role = {
principalType: ACL.USER, principalId: user.id,
model: Album, id: album2.id,
};
Role.isInRole(Role.OWNER, role, function(err, yes) {
if (err) return next(err);
assert(!yes);
next();
}); });
}); });
}); });
}); });
},
], done);
});
}); });
describe('isMappedToRole', function() { describe('isMappedToRole', function() {
@ -255,7 +422,7 @@ describe('role model', function() {
User.create({ User.create({
username: 'john', username: 'john',
email: 'john@gmail.com', email: 'john@gmail.com',
password: 'jpass' password: 'jpass',
}, function(err, u) { }, function(err, u) {
if (err) return done(err); if (err) return done(err);
@ -263,18 +430,18 @@ describe('role model', function() {
User.create({ User.create({
username: 'mary', username: 'mary',
email: 'mary@gmail.com', email: 'mary@gmail.com',
password: 'mpass' password: 'mpass',
}, function(err, u) { }, function(err, u) {
if (err) return done(err); if (err) return done(err);
Application.create({ Application.create({
name: 'demo' name: 'demo',
}, function(err, a) { }, function(err, a) {
if (err) return done(err); if (err) return done(err);
app = a; app = a;
Role.create({ Role.create({
name: 'admin' name: 'admin',
}, function(err, r) { }, function(err, r) {
if (err) return done(err); if (err) return done(err);
@ -282,12 +449,12 @@ describe('role model', function() {
var principals = [ var principals = [
{ {
principalType: ACL.USER, principalType: ACL.USER,
principalId: user.id principalId: user.id,
}, },
{ {
principalType: ACL.APP, principalType: ACL.APP,
principalId: app.id principalId: app.id,
} },
]; ];
async.each(principals, function(p, done) { async.each(principals, function(p, done) {
role.principals.create(p, done); role.principals.create(p, done);
@ -398,7 +565,6 @@ describe('role model', function() {
done(); done();
}); });
}); });
}); });
describe('listByPrincipalType', function() { describe('listByPrincipalType', function() {
@ -425,12 +591,17 @@ describe('role model', function() {
mappings.forEach(function(principalType) { mappings.forEach(function(principalType) {
var Model = principalTypesToModels[principalType]; var Model = principalTypesToModels[principalType];
Model.create({name:'test', email:'x@y.com', password: 'foobar'}, function(err, model) { Model.create({ name: 'test', email: 'x@y.com', password: 'foobar' }, function(err, model) {
Role.create({name:'testRole'}, function(err, role) { if (err) return done(err);
role.principals.create({principalType: principalType, principalId: model.id}, function(err, p) { var uniqueRoleName = 'testRoleFor' + principalType;
Role.create({ name: uniqueRoleName }, function(err, role) {
if (err) return done(err);
role.principals.create({ principalType: principalType, principalId: model.id },
function(err, p) {
if (err) return done(err);
var pluralName = Model.pluralModelName.toLowerCase(); var pluralName = Model.pluralModelName.toLowerCase();
role[pluralName](function(err, models) { role[pluralName](function(err, models) {
assert(!err); if (err) return done(err);
assert.equal(models.length, 1); assert.equal(models.length, 1);
if (++runs === mappings.length) { if (++runs === mappings.length) {
@ -444,13 +615,17 @@ describe('role model', function() {
}); });
it('should apply query', function(done) { it('should apply query', function(done) {
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) { User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
Role.create({name: 'userRole'}, function(err, role) { if (err) return done(err);
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, function(err, p) { Role.create({ name: 'userRole' }, function(err, role) {
var query = {fields:['id', 'name']}; if (err) return done(err);
role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
function(err, p) {
if (err) return done(err);
var query = { fields: ['id', 'name'] };
sandbox.spy(User, 'find'); sandbox.spy(User, 'find');
role.users(query, function(err, users) { role.users(query, function(err, users) {
assert(!err); if (err) return done(err);
assert.equal(users.length, 1); assert.equal(users.length, 1);
assert.equal(users[0].id, user.id); assert.equal(users[0].id, user.id);
assert(User.find.calledWith(query)); assert(User.find.calledWith(query));