diff --git a/test/helpers/hook-monitor.js b/test/helpers/hook-monitor.js new file mode 100644 index 00000000..384d076b --- /dev/null +++ b/test/helpers/hook-monitor.js @@ -0,0 +1,30 @@ +// Copyright IBM Corp. 2016. All Rights Reserved. +// Node module: loopback-datasource-juggler +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +module.exports = HookMonitor; + +function HookMonitor(opts) { + if (!(this instanceof HookMonitor)) { + return new HookMonitor(); + } + + this.options = opts || {}; + this.names = []; +}; + +HookMonitor.prototype.install = function(ObservedModel, hookNames) { + var monitor = this; + this.names = []; + ObservedModel._notify = ObservedModel.notifyObserversOf; + ObservedModel.notifyObserversOf = function(operation, context, callback) { + if (!Array.isArray(hookNames) || hookNames.indexOf(operation) !== -1) { + var item = monitor.options.includeModelName ? + ObservedModel.modelName + ':' + operation : + operation; + monitor.names.push(item); + } + this._notify.apply(this, arguments); + }; +}; diff --git a/test/persistence-hooks.suite.js b/test/persistence-hooks.suite.js index a9caefae..f5b3e0d7 100644 --- a/test/persistence-hooks.suite.js +++ b/test/persistence-hooks.suite.js @@ -13,6 +13,8 @@ var aCtxForModel = contextTestHelpers.aCtxForModel; var uid = require('./helpers/uid-generator'); var getLastGeneratedUid = uid.last; +var HookMonitor = require('./helpers/hook-monitor'); + module.exports = function(dataSource, should, connectorCapabilities) { if (!connectorCapabilities) connectorCapabilities = {}; if (connectorCapabilities.replaceOrCreateReportsNewInstance === undefined) { @@ -20,17 +22,16 @@ module.exports = function(dataSource, should, connectorCapabilities) { ' replaceOrCreateReportsNewInstance'); } describe('Persistence hooks', function() { - var ctxRecorder, expectedError, observersCalled; + var ctxRecorder, hookMonitor, expectedError; var TestModel, existingInstance; var migrated = false; - var triggered; var undefinedValue = undefined; beforeEach(function setupDatabase(done) { ctxRecorder = new ContextRecorder('hook not called'); + hookMonitor = new HookMonitor({ includeModelName: false }); expectedError = new Error('test error'); - observersCalled = []; TestModel = dataSource.createModel('TestModel', { // Set id.generated to false to honor client side values @@ -77,7 +78,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { function(err, list) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'loaded', ]); @@ -189,7 +190,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { function(err, record, created) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'before save', 'persist', 'loaded', @@ -555,7 +556,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { { name: 'new-record' }, function(err, record, created) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'before save', 'persist', @@ -576,14 +577,14 @@ module.exports = function(dataSource, should, connectorCapabilities) { if (err) return done(err); if (dataSource.connector.findOrCreate) { - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'before save', 'persist', 'loaded', ]); } else { - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'loaded', ]); @@ -934,7 +935,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { existingInstance.save( function(err, record, created) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'before save', 'persist', 'loaded', @@ -1146,7 +1147,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { { name: 'changed' }, function(err, record, created) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'before save', 'persist', 'loaded', @@ -1402,7 +1403,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { { name: 'replaced' }, function(err, record, created) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'before save', 'persist', 'loaded', @@ -1645,7 +1646,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { { id: 'not-found', name: 'not found' }, function(err, record, created) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'before save', 'persist', @@ -1664,7 +1665,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { function(err, record, created) { if (err) return done(err); if (dataSource.connector.updateOrCreate) { - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'before save', 'persist', @@ -1672,7 +1673,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { 'after save', ]); } else { - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'loaded', 'before save', @@ -1769,8 +1770,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { }); it('triggers hooks only once', function(done) { - TestModel.observe('access', pushNameAndNext('access')); - TestModel.observe('before save', pushNameAndNext('before save')); + monitorHookExecution(['access', 'before save']); TestModel.observe('access', function(ctx, next) { ctx.query = { where: { id: { neq: existingInstance.id }}}; @@ -1781,7 +1781,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { { id: 'ignored', name: 'new name' }, function(err, instance) { if (err) return done(err); - observersCalled.should.eql(['access', 'before save']); + hookMonitor.names.should.eql(['access', 'before save']); done(); }); }); @@ -2101,7 +2101,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { { id: 'not-found', name: 'not found' }, function(err, record, created) { if (err) return done(err); - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'before save', 'persist', @@ -2120,7 +2120,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { function(err, record, created) { if (err) return done(err); if (dataSource.connector.replaceOrCreate) { - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'before save', 'persist', @@ -2137,7 +2137,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { // loopback-datasource-juggler/issues#836. // 2) It, also, gets triggered in "replaceAttributes()" // in this chain replaceORCreate()->replaceAttributes() - triggered.should.eql([ + hookMonitor.names.should.eql([ 'access', 'loaded', 'before save', @@ -2234,8 +2234,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { }); it('triggers hooks only once', function(done) { - TestModel.observe('access', pushNameAndNext('access')); - TestModel.observe('before save', pushNameAndNext('before save')); + monitorHookExecution(['access', 'before save']); TestModel.observe('access', function(ctx, next) { ctx.query = { where: { id: { neq: existingInstance.id }}}; @@ -2246,7 +2245,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { { id: 'ignored', name: 'new name' }, function(err, instance) { if (err) return done(err); - observersCalled.should.eql(['access', 'before save']); + hookMonitor.names.should.eql(['access', 'before save']); done(); }); }); @@ -2808,8 +2807,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { }); it('triggers hooks only once', function(done) { - TestModel.observe('access', pushNameAndNext('access')); - TestModel.observe('after delete', pushNameAndNext('after delete')); + monitorHookExecution(); TestModel.observe('access', function(ctx, next) { ctx.query = { where: { id: { neq: existingInstance.id }}}; next(); @@ -2817,7 +2815,7 @@ module.exports = function(dataSource, should, connectorCapabilities) { existingInstance.delete(function(err) { if (err) return done(err); - observersCalled.should.eql(['access', 'after delete']); + hookMonitor.names.should.eql(['access', 'before delete', 'after delete']); done(); }); }); @@ -2979,13 +2977,6 @@ module.exports = function(dataSource, should, connectorCapabilities) { }); - function pushNameAndNext(name) { - return function(context, next) { - observersCalled.push(name); - next(); - }; - } - function nextWithError(err) { return function(context, next) { next(err); @@ -3016,13 +3007,8 @@ module.exports = function(dataSource, should, connectorCapabilities) { TestModel.findOne({ where: { id: id }}, { notify: false }, cb); } - function monitorHookExecution() { - triggered = []; - TestModel._notify = TestModel.notifyObserversOf; - TestModel.notifyObserversOf = function(operation, context, callback) { - triggered.push(operation); - this._notify.apply(this, arguments); - }; + function monitorHookExecution(hookNames) { + hookMonitor.install(TestModel, hookNames); } });