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 7546ee531d
commit 48205fb2bd
1 changed files with 319 additions and 154 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,51 +47,88 @@ 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) {
if (err) return done(err);
Role.create({ name: 'admin' }, function(err, adminRole) { Role.create({ name: 'admin' }, function(err, adminRole) {
userRole.principals.create({ principalType: RoleMapping.ROLE, principalId: adminRole.id }, if (err) return done(err);
function(err, mapping) { userRole.principals.create(
Role.find(function(err, roles) { { principalType: RoleMapping.ROLE, principalId: adminRole.id },
assert.equal(roles.length, 2); function(err, mapping) {
if (err) return done(err);
async.parallel([
function(next) {
Role.find(function(err, roles) {
if (err) return next(err);
assert.equal(roles.length, 2);
next();
});
},
function(next) {
RoleMapping.find(function(err, mappings) {
if (err) return next(err);
assert.equal(mappings.length, 1);
assert.equal(mappings[0].principalType, RoleMapping.ROLE);
assert.equal(mappings[0].principalId, adminRole.id);
next();
});
},
function(next) {
userRole.principals(function(err, principals) {
if (err) return next(err);
assert.equal(principals.length, 1);
next();
});
},
function(next) {
userRole.roles(function(err, roles) {
if (err) return next(err);
assert.equal(roles.length, 1);
next();
});
},
], done);
}); });
RoleMapping.find(function(err, mappings) {
assert.equal(mappings.length, 1);
assert.equal(mappings[0].principalType, RoleMapping.ROLE);
assert.equal(mappings[0].principalId, adminRole.id);
});
userRole.principals(function(err, principals) {
assert.equal(principals.length, 1);
});
userRole.roles(function(err, roles) {
assert.equal(roles.length, 1);
});
});
}); });
}); });
}); });
it('should define role/user relations', function() { it('should define role/user relations', 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);
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.find(function(err, roles) { if (err) return done(err);
assert(!err); async.parallel([
assert.equal(roles.length, 1); function(next) {
assert.equal(roles[0].name, 'userRole'); Role.find(function(err, roles) {
}); if (err) return next(err);
role.principals(function(err, principals) { assert.equal(roles.length, 1);
assert(!err); assert.equal(roles[0].name, 'userRole');
assert.equal(principals.length, 1); next();
assert.equal(principals[0].principalType, RoleMapping.USER); });
assert.equal(principals[0].principalId, user.id); },
}); function(next) {
role.users(function(err, users) { role.principals(function(err, principals) {
assert(!err); if (err) return next(err);
assert.equal(users.length, 1); assert.equal(principals.length, 1);
assert.equal(users[0].id, user.id); assert.equal(principals[0].principalType, RoleMapping.USER);
}); assert.equal(principals[0].principalId, user.id);
next();
});
},
function(next) {
role.users(function(err, users) {
if (err) return next(err);
assert.equal(users.length, 1);
assert.equal(users[0].id, user.id);
next();
});
},
], done);
}); });
}); });
}); });
@ -106,86 +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 }, role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
function(err, p) { 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);
Role.find(function(err, roles) { async.parallel([
assert(!err); function(next) {
assert.equal(roles.length, 1); Role.find(function(err, roles) {
assert.equal(roles[0].name, 'userRole'); if (err) return next(err);
}); assert.equal(roles.length, 1);
role.principals(function(err, principals) { assert.equal(roles[0].name, 'userRole');
assert(!err); next();
assert.equal(principals.length, 1); });
assert.equal(principals[0].principalType, RoleMapping.USER); },
assert.equal(principals[0].principalId, user.id); function(next) {
}); role.principals(function(err, principals) {
role.users(function(err, users) { if (err) return next(err);
assert(!err); assert.equal(principals.length, 1);
assert.equal(users.length, 1); assert.equal(principals[0].principalType, RoleMapping.USER);
assert.equal(users[0].id, user.id); assert.equal(principals[0].principalId, user.id);
}); next();
});
},
function(next) {
role.users(function(err, users) {
if (err) return next(err);
assert.equal(users.length, 1);
assert.equal(users[0].id, user.id);
});
next();
},
], done);
}); });
}); });
}); });
}); });
it('should support getRoles() and isInRole()', function() { it('should support getRoles() and isInRole()', 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);
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',
Role.isInRole('userRole', { principalType: RoleMapping.APP, principalId: user.id }, { principalType: RoleMapping.USER, principalId: user.id },
function(err, exists) { function(err, inRole) {
assert(!err && exists === false); if (err) return next(err);
}); // NOTE(bajtos) Apparently isRole is not a boolean,
// but the matchin role object instead
Role.isInRole('userRole', { principalType: RoleMapping.USER, principalId: 100 }, assert(!!inRole);
function(err, exists) { next();
assert(!err && exists === false); });
}); },
function(next) {
Role.getRoles({ principalType: RoleMapping.USER, principalId: user.id }, Role.isInRole(
function(err, roles) { 'userRole',
assert.equal(roles.length, 3); // everyone, authenticated, userRole { principalType: RoleMapping.APP, principalId: user.id },
assert(roles.indexOf(role.id) >= 0); function(err, inRole) {
assert(roles.indexOf(Role.EVERYONE) >= 0); if (err) return next(err);
assert(roles.indexOf(Role.AUTHENTICATED) >= 0); assert(!inRole);
}); 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.isInRole(
assert(roles.indexOf(Role.AUTHENTICATED) >= 0); 'userRole',
}); { principalType: RoleMapping.USER, principalId: 100 },
Role.getRoles({ principalType: RoleMapping.USER, principalId: 100 }, function(err, inRole) {
function(err, roles) { if (err) return next(err);
assert.equal(roles.length, 2); assert(!inRole);
assert(roles.indexOf(Role.EVERYONE) >= 0); next();
assert(roles.indexOf(Role.AUTHENTICATED) >= 0); });
}); },
Role.getRoles({ principalType: RoleMapping.USER, principalId: null }, function(next) {
function(err, roles) { Role.getRoles(
assert.equal(roles.length, 2); { principalType: RoleMapping.USER, principalId: user.id },
assert(roles.indexOf(Role.EVERYONE) >= 0); function(err, roles) {
assert(roles.indexOf(Role.UNAUTHENTICATED) >= 0); if (err) return next(err);
}); expect(roles).to.eql([
Role.AUTHENTICATED,
Role.EVERYONE,
role.id,
]);
next();
});
},
function(next) {
Role.getRoles(
{ principalType: RoleMapping.APP, principalId: user.id },
function(err, roles) {
if (err) return next(err);
expect(roles).to.eql([
Role.AUTHENTICATED,
Role.EVERYONE,
]);
next();
});
},
function(next) {
Role.getRoles(
{ principalType: RoleMapping.USER, principalId: 100 },
function(err, roles) {
if (err) return next(err);
expect(roles).to.eql([
Role.AUTHENTICATED,
Role.EVERYONE,
]);
next();
});
},
function(next) {
Role.getRoles(
{ 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() { it('should support owner role resolver', function(done) {
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() {
@ -194,7 +298,7 @@ describe('role model', function() {
}); });
}); });
var Album = ds.createModel('Album', { var Album = app.registry.createModel('Album', {
name: String, name: String,
userId: Number, userId: Number,
}, { }, {
@ -206,54 +310,108 @@ describe('role model', function() {
}, },
}, },
}); });
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);
function(err, yes) { async.parallel([
assert(!err && yes); function(next) {
}); Role.isInRole(
'returnPromise',
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) {
if (err) return next(err);
assert(yes);
next();
});
},
function(next) {
Role.isInRole(
Role.AUTHENTICATED,
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) {
if (err) next(err);
assert(yes);
next();
});
},
function(next) {
Role.isInRole(
Role.AUTHENTICATED,
{ principalType: ACL.USER, principalId: null },
function(err, yes) {
if (err) next(err);
assert(!yes);
next();
});
},
function(next) {
Role.isInRole(
Role.UNAUTHENTICATED,
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) {
if (err) return next(err);
assert(!yes);
next();
});
},
function(next) {
Role.isInRole(
Role.UNAUTHENTICATED,
{ principalType: ACL.USER, principalId: null },
function(err, yes) {
if (err) return next(err);
assert(yes);
next();
});
},
function(next) {
Role.isInRole(
Role.EVERYONE,
{ principalType: ACL.USER, principalId: user.id },
function(err, yes) {
if (err) return next(err);
assert(yes);
next();
});
},
function(next) {
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) {
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) {
if (err) return next(err);
assert(yes);
Role.isInRole(Role.AUTHENTICATED, { principalType: ACL.USER, principalId: user.id }, Album.create({ name: 'Album 2' }, function(err, album2) {
function(err, yes) { if (err) return next(err);
assert(!err && yes); role = {
}); principalType: ACL.USER, principalId: user.id,
model: Album, id: album2.id,
Role.isInRole(Role.AUTHENTICATED, { principalType: ACL.USER, principalId: null }, };
function(err, yes) { Role.isInRole(Role.OWNER, role, function(err, yes) {
assert(!err && !yes); if (err) return next(err);
}); assert(!yes);
next();
Role.isInRole(Role.UNAUTHENTICATED, { principalType: ACL.USER, principalId: user.id }, });
function(err, yes) { });
assert(!err && !yes); });
});
Role.isInRole(Role.UNAUTHENTICATED, { principalType: ACL.USER, principalId: null },
function(err, yes) {
assert(!err && yes);
});
Role.isInRole(Role.EVERYONE, { principalType: ACL.USER, principalId: user.id },
function(err, yes) {
assert(!err && yes);
});
Role.isInRole(Role.EVERYONE, { principalType: ACL.USER, principalId: null },
function(err, yes) {
assert(!err && yes);
});
Album.create({ name: 'Album 1', userId: user.id }, function(err, album1) {
var role = { principalType: ACL.USER, principalId: user.id, model: Album, id: album1.id };
Role.isInRole(Role.OWNER, role, function(err, yes) {
assert(!err && yes);
});
Album.create({ name: 'Album 2' }, function(err, album2) {
role = { principalType: ACL.USER, principalId: user.id, model: Album, id: album2.id };
Role.isInRole(Role.OWNER, role, function(err, yes) {
assert(!err && !yes);
}); });
}); },
}); ], done);
}); });
}); });
@ -434,12 +592,16 @@ 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);
var uniqueRoleName = 'testRoleFor' + principalType;
Role.create({ name: uniqueRoleName }, function(err, role) {
if (err) return done(err);
role.principals.create({ principalType: principalType, principalId: model.id }, role.principals.create({ principalType: principalType, principalId: model.id },
function(err, p) { 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) {
@ -454,13 +616,16 @@ 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) {
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);
var query = { fields: ['id', 'name'] }; 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));