starting unit tests
This commit is contained in:
parent
844147517e
commit
a657e8aa46
|
@ -399,26 +399,25 @@ app.enableAuth = function(options) {
|
|||
}
|
||||
};
|
||||
|
||||
this._verifyAuthModelRelations();
|
||||
|
||||
this.isAuthEnabled = true;
|
||||
return this._verifyAuthModelRelations(app._warnOnBadAuthModelRelations)
|
||||
.then(() => {
|
||||
this.isAuthEnabled = true;
|
||||
});
|
||||
};
|
||||
|
||||
app._verifyAuthModelRelations = function() {
|
||||
app._verifyAuthModelRelations = function(warnFn) {
|
||||
let self = this;
|
||||
|
||||
// Allow unit-tests (but also LoopBack users) to disable the warnings
|
||||
const warnOnBadSetup = self.get('_disableAuthModelRelationsWarnings') !== true;
|
||||
const warnOnBadSetup = this.get('_warnOnBadAuthModelsSetup') !== false;
|
||||
// Prevent the app from being killed although the configuration is inconsistent
|
||||
const dismissRejection = self.get('_dismissBadUserConfigRejection') !== true;
|
||||
const abortOnBadUserSetup = this.get('_abortOnBadUserSetup') !== false;
|
||||
|
||||
const AccessToken = self.registry.findModel('AccessToken');
|
||||
const User = self.registry.findModel('User');
|
||||
const models = self.models();
|
||||
const AccessToken = this.registry.findModel('AccessToken');
|
||||
const User = this.registry.findModel('User');
|
||||
const models = this.models();
|
||||
|
||||
verifyUserModelsSetup().then(()=> {
|
||||
verifyRelationsSetup();
|
||||
});
|
||||
return verifyUserModelsSetup().then(verifyRelationsSetup);
|
||||
|
||||
function verifyUserModelsSetup() {
|
||||
let userModels = models.filter(model => {
|
||||
|
@ -428,25 +427,22 @@ app._verifyAuthModelRelations = function() {
|
|||
|
||||
// we use Promise.reflect() here to let all the tests go without throwing,
|
||||
// this way we can get all the logs for all models we are checking
|
||||
return Promise.map(userModels, (model) => {
|
||||
return scheduleVerification(model, hasMultipleUserModelsConfig).reflect();
|
||||
})
|
||||
.then((inspections) => {
|
||||
return Promise.map(userModels,
|
||||
model => scheduleVerification(model, hasMultipleUserModelsConfig).reflect()
|
||||
)
|
||||
.then(inspections => {
|
||||
// proceed to next checkups if no error
|
||||
let hasErrors = inspections.filter(inspection => {
|
||||
return inspection.isRejected();
|
||||
});
|
||||
let hasErrors = inspections.filter(inspection => inspection.isRejected());
|
||||
if (!hasErrors.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// else log and eventually kill the app
|
||||
console.warn('Application setup is inconsistent: some models are set to use ' +
|
||||
'the Multiple user models configuration while some other models are set to ' +
|
||||
'use the Single user model configuration. The model config is listed below.');
|
||||
warnFn('BAD_USER_MODELS_SETUP');
|
||||
|
||||
// detailed logs
|
||||
inspections.forEach(inspection => {
|
||||
console.log(inspection);
|
||||
let modelSetup = (inspection.value() || inspection.reason()).modelSetup;
|
||||
console.warn(
|
||||
'Model %j of type %j is set to use the %j user model config',
|
||||
|
@ -456,11 +452,14 @@ app._verifyAuthModelRelations = function() {
|
|||
);
|
||||
});
|
||||
|
||||
delete self.hasMultipleUserModelsConfig;
|
||||
delete this.hasMultipleUserModelsConfig;
|
||||
|
||||
if (!dismissRejection) {
|
||||
throw new Error('Application setup is inconsistent, please see the log for more ' +
|
||||
'information');
|
||||
if (abortOnBadUserSetup) {
|
||||
const msg = 'Application setup is inconsistent, please see the log ' +
|
||||
'for more information';
|
||||
const error = new Error(msg);
|
||||
error.code = 'BAD_USER_MODELS_SETUP';
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -501,7 +500,8 @@ app._verifyAuthModelRelations = function() {
|
|||
if (model === User || model.prototype instanceof User) {
|
||||
isInstanceOfUser = true;
|
||||
const hasManyTokens = model.relations && model.relations.accessTokens;
|
||||
hasMultipleUserModelsConfig = !!hasManyTokens.polymorphic;
|
||||
// TODO: handle when hasManyTokens is undefined?
|
||||
hasMultipleUserModelsConfig = !!(hasManyTokens.polymorphic);
|
||||
} else if (model === AccessToken || model.prototype instanceof AccessToken) {
|
||||
const belongsToUser = model.relations && model.relations.user;
|
||||
// the test on belongsToUser is required as we allow AccessToken model not
|
||||
|
@ -512,7 +512,7 @@ app._verifyAuthModelRelations = function() {
|
|||
|
||||
let modelSetup = {
|
||||
model: model.modelName,
|
||||
instanceOf: isInstanceOfUser ? 'User' : 'AccesToken',
|
||||
instanceOf: isInstanceOfUser ? 'User' : 'AccessToken',
|
||||
hasMultipleUserModelsConfig,
|
||||
};
|
||||
|
||||
|
@ -531,30 +531,18 @@ app._verifyAuthModelRelations = function() {
|
|||
|
||||
function verifyAccessTokenRelations(Model) {
|
||||
if (!warnOnBadSetup) return;
|
||||
|
||||
const belongsToUser = Model.relations && Model.relations.user;
|
||||
if (belongsToUser) return;
|
||||
|
||||
const modelFrom = Model.modelName;
|
||||
const relationsConfig = Model.settings.relations || {};
|
||||
const userName = (relationsConfig.user || {}).model;
|
||||
if (userName) {
|
||||
console.warn(
|
||||
'The model %j configures "belongsTo User-like models" relation ' +
|
||||
'with target model %j. However, the model %j is not attached to ' +
|
||||
'the application and therefore cannot be used by this relation. ' +
|
||||
'This typically happens when the application has a custom ' +
|
||||
'custom User subclass, but does not fix AccessToken relations ' +
|
||||
'to use this new model.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName, userName, userName);
|
||||
const modelTo = (relationsConfig.user || {}).model;
|
||||
if (modelTo) {
|
||||
warnFn('CUSTOM_USER_MODEL_NOT_AVAILABLE', {modelFrom, modelTo});
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'The model %j does not have "belongsTo User-like model" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName);
|
||||
warnFn('MISSING_RELATION_TO_USER', {modelFrom});
|
||||
}
|
||||
|
||||
function verifyUserRelations(Model) {
|
||||
|
@ -563,10 +551,49 @@ app._verifyAuthModelRelations = function() {
|
|||
const hasManyTokens = Model.relations && Model.relations.accessTokens;
|
||||
if (hasManyTokens) return;
|
||||
|
||||
const modelFrom = Model.modelName;
|
||||
const relationsConfig = Model.settings.relations || {};
|
||||
const accessTokenName = (relationsConfig.accessTokens || {}).model;
|
||||
if (accessTokenName) {
|
||||
console.warn(
|
||||
const modelTo = (relationsConfig.accessTokens || {}).model;
|
||||
if (modelTo) {
|
||||
warnFn('CUSTOM_ACCESS_TOKEN_MODEL_NOT_AVAILABLE', {modelFrom, modelTo});
|
||||
return;
|
||||
}
|
||||
|
||||
warnFn('MISSING_RELATION_TO_ACCESS_TOKEN', {modelFrom});
|
||||
}
|
||||
};
|
||||
|
||||
// function warnOnBadAuthModelRelations(code, args) {
|
||||
app._warnOnBadAuthModelRelations = function(code, args) {
|
||||
switch (code) {
|
||||
case 'BAD_USER_MODELS_SETUP':
|
||||
g.warn('Application setup is inconsistent: some models are set to use ' +
|
||||
'the Multiple user models configuration while some other models are set to ' +
|
||||
'use the Single user model configuration. The model config is listed below.');
|
||||
break;
|
||||
|
||||
case 'CUSTOM_USER_MODEL_NOT_AVAILABLE':
|
||||
g.warn(
|
||||
'The model %j configures "belongsTo User-like models" relation ' +
|
||||
'with target model %j. However, the model %j is not attached to ' +
|
||||
'the application and therefore cannot be used by this relation. ' +
|
||||
'This typically happens when the application has a custom ' +
|
||||
'custom User subclass, but does not fix AccessToken relations ' +
|
||||
'to use this new model.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
args.modelFrom, args.modelTo, args.modelTo);
|
||||
break;
|
||||
|
||||
case 'MISSING_RELATION_TO_USER':
|
||||
g.warn(
|
||||
'The model %j does not have "belongsTo User-like model" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
args.modelFrom);
|
||||
break;
|
||||
|
||||
case 'CUSTOM_ACCESS_TOKEN_MODEL_NOT_AVAILABLE':
|
||||
g.warn(
|
||||
'The model %j configures "hasMany AccessToken-like models" relation ' +
|
||||
'with target model %j. However, the model %j is not attached to ' +
|
||||
'the application and therefore cannot be used by this relation. ' +
|
||||
|
@ -574,15 +601,15 @@ app._verifyAuthModelRelations = function() {
|
|||
'AccessToken subclass, but does not fix User relations to use this ' +
|
||||
'new model.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName, accessTokenName, accessTokenName);
|
||||
return;
|
||||
}
|
||||
args.modelFrom, args.modelTo, args.modelTo);
|
||||
break;
|
||||
|
||||
console.warn(
|
||||
'The model %j does not have "hasMany AccessToken-like models" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
Model.modelName);
|
||||
case 'MISSING_RELATION_TO_ACCESS_TOKEN':
|
||||
g.warn(
|
||||
'The model %j does not have "hasMany AccessToken-like models" relation ' +
|
||||
'configured.\n' +
|
||||
'Learn more at http://ibm.biz/setup-loopback-auth',
|
||||
args.modelFrom);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ var expect = require('./helpers/expect');
|
|||
var it = require('./util/it');
|
||||
var request = require('supertest');
|
||||
|
||||
var Promise = require('bluebird');
|
||||
|
||||
describe('app', function() {
|
||||
var app;
|
||||
beforeEach(function() {
|
||||
|
@ -859,10 +861,11 @@ describe('app', function() {
|
|||
});
|
||||
|
||||
describe.onServer('enableAuth', function() {
|
||||
it('should set app.isAuthEnabled to true', function() {
|
||||
it('sets app.isAuthEnabled to true', function() {
|
||||
expect(app.isAuthEnabled).to.not.equal(true);
|
||||
app.enableAuth();
|
||||
expect(app.isAuthEnabled).to.equal(true);
|
||||
return app.enableAuth().then(() => {
|
||||
expect(app.isAuthEnabled).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('auto-configures required models to provided dataSource', function() {
|
||||
|
@ -871,14 +874,14 @@ describe('app', function() {
|
|||
require('../lib/builtin-models')(app.registry);
|
||||
var db = app.dataSource('db', {connector: 'memory'});
|
||||
|
||||
app.enableAuth({dataSource: 'db'});
|
||||
return app.enableAuth({dataSource: 'db'}).then(() => {
|
||||
expect(Object.keys(app.models)).to.include.members(AUTH_MODELS);
|
||||
|
||||
expect(Object.keys(app.models)).to.include.members(AUTH_MODELS);
|
||||
|
||||
AUTH_MODELS.forEach(function(m) {
|
||||
var Model = app.models[m];
|
||||
expect(Model.dataSource, m + '.dataSource').to.equal(db);
|
||||
expect(Model.shared, m + '.shared').to.equal(m === 'User');
|
||||
AUTH_MODELS.forEach(function(m) {
|
||||
var Model = app.models[m];
|
||||
expect(Model.dataSource, m + '.dataSource').to.equal(db);
|
||||
expect(Model.shared, m + '.shared').to.equal(m === 'User');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -892,9 +895,71 @@ describe('app', function() {
|
|||
const AccessToken = app.registry.getModel('AccessToken');
|
||||
AccessToken.settings.relations.user.model = 'Customer';
|
||||
|
||||
app.enableAuth({dataSource: 'db'});
|
||||
return app.enableAuth({dataSource: 'db'}).then(() => {
|
||||
expect(Object.keys(app.models)).to.not.include('User');
|
||||
});
|
||||
});
|
||||
|
||||
expect(Object.keys(app.models)).to.not.include('User');
|
||||
describe('auth models config health check', function() {
|
||||
var app, warnings;
|
||||
beforeEach(function() {
|
||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
|
||||
warnings = [];
|
||||
function warnFn(code, args) {
|
||||
warnings.push(Object.assign(args || {}, {code}));
|
||||
}
|
||||
app._warnOnBadAuthModelRelations = warnFn;
|
||||
});
|
||||
|
||||
it('sets app.hasMultipleUserModelsConfig to false if the app ' +
|
||||
'has a single User model correctly configured', function() {
|
||||
return app.enableAuth({dataSource: 'db'}).then(() => {
|
||||
expect(app.hasMultipleUserModelsConfig).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('sets app.hasMultipleUserModelsConfig to true if the app ' +
|
||||
'has multiple User models correctly configured', function() {
|
||||
var Merchant = createModel(app, 'Merchant', {base: 'User'});
|
||||
var Customer = createModel(app, 'Customer', {base: 'User'});
|
||||
var Token = createModel(app, 'Token', {base: 'AccessToken'});
|
||||
|
||||
// Update AccessToken and Users to bind them through polymorphic relations
|
||||
Token.belongsTo('user', {idName: 'id', polymorphic: {idType: 'string',
|
||||
foreignKey: 'userId', discriminator: 'principalType'}});
|
||||
Merchant.hasMany('accessTokens', {model: Token, polymorphic: {foreignKey: 'userId',
|
||||
discriminator: 'principalType'}});
|
||||
Customer.hasMany('accessTokens', {model: Token, polymorphic: {foreignKey: 'userId',
|
||||
discriminator: 'principalType'}});
|
||||
return app.enableAuth({dataSource: 'db'}).then(() => {
|
||||
expect(app.hasMultipleUserModelsConfig).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('warns if a custom user model is referenced in the ' +
|
||||
'AccessToken "user" relation, but is not available', function() {
|
||||
// Set AccessToken's belongsTo relation "user" to use a custom user model
|
||||
// This model is deliberately not available
|
||||
const AccessToken = app.registry.getModel('AccessToken');
|
||||
AccessToken.settings.relations.user.model = 'Customer';
|
||||
|
||||
return app.enableAuth({dataSource: 'db'}).then(() => {
|
||||
expect(warnings).to.eql([{
|
||||
code: 'CUSTOM_USER_MODEL_NOT_AVAILABLE',
|
||||
modelFrom: 'AccessToken',
|
||||
modelTo: 'Customer',
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
// helpers
|
||||
function createModel(app, name, options) {
|
||||
var model = app.registry.createModel(Object.assign({name: name}, options));
|
||||
app.model(model, {dataSource: 'db'});
|
||||
return model;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -2442,7 +2442,7 @@ describe('User', function() {
|
|||
it('handles subclassed user with no accessToken relation', () => {
|
||||
// setup a new LoopBack app, we don't want to use shared models
|
||||
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
||||
app.set('_disableAuthModelRelationsWarnings', true);
|
||||
app.set('_warnOnBadAuthModelsSetup', false);
|
||||
app.set('remoting', {errorHandler: {debug: true, log: false}});
|
||||
app.dataSource('db', {connector: 'memory'});
|
||||
const User = app.registry.createModel({
|
||||
|
|
Loading…
Reference in New Issue