Merge pull request #904 from strongloop/feature/hooks-for-embeds-one
Implement operation hooks for EmbedsOne methods
This commit is contained in:
commit
7247b6637e
|
@ -2107,16 +2107,16 @@ EmbedsOne.prototype.create = function(targetModelData, options, cb) {
|
|||
|
||||
var inst = this.callScopeMethod('build', targetModelData);
|
||||
|
||||
var updateEmbedded = function() {
|
||||
var updateEmbedded = function(callback) {
|
||||
if (modelInstance.isNewRecord()) {
|
||||
modelInstance.setAttribute(propertyName, inst);
|
||||
modelInstance.save(options, function(err) {
|
||||
cb(err, err ? null : inst);
|
||||
callback(err, err ? null : inst);
|
||||
});
|
||||
} else {
|
||||
modelInstance.updateAttribute(propertyName,
|
||||
inst, options, function(err) {
|
||||
cb(err, err ? null : inst);
|
||||
callback(err, err ? null : inst);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -2124,17 +2124,37 @@ EmbedsOne.prototype.create = function(targetModelData, options, cb) {
|
|||
if (this.definition.options.persistent) {
|
||||
inst.save(options, function(err) { // will validate
|
||||
if (err) return cb(err, inst);
|
||||
updateEmbedded();
|
||||
updateEmbedded(cb);
|
||||
});
|
||||
} else {
|
||||
var err = inst.isValid() ? null : new ValidationError(inst);
|
||||
if (err) {
|
||||
process.nextTick(function() {
|
||||
cb(err);
|
||||
});
|
||||
} else {
|
||||
updateEmbedded();
|
||||
}
|
||||
var context = {
|
||||
Model: modelTo,
|
||||
instance: inst,
|
||||
options: options || {},
|
||||
hookState: {},
|
||||
};
|
||||
modelTo.notifyObserversOf('before save', context, function(err) {
|
||||
if (err) {
|
||||
return process.nextTick(function() {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
var err = inst.isValid() ? null : new ValidationError(inst);
|
||||
if (err) {
|
||||
process.nextTick(function() {
|
||||
cb(err);
|
||||
});
|
||||
} else {
|
||||
updateEmbedded(function(err, inst) {
|
||||
if (err) return cb(err);
|
||||
context.instance = inst;
|
||||
modelTo.notifyObserversOf('after save', context, function(err) {
|
||||
cb(err, err ? null : inst);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return cb.promise;
|
||||
};
|
||||
|
@ -2174,6 +2194,7 @@ EmbedsOne.prototype.update = function(targetModelData, options, cb) {
|
|||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
var modelTo = this.definition.modelTo;
|
||||
var modelInstance = this.modelInstance;
|
||||
var propertyName = this.definition.keyFrom;
|
||||
|
@ -2183,13 +2204,39 @@ EmbedsOne.prototype.update = function(targetModelData, options, cb) {
|
|||
|
||||
var embeddedInstance = modelInstance[propertyName];
|
||||
if (embeddedInstance instanceof modelTo) {
|
||||
embeddedInstance.setAttributes(data);
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
if (typeof cb === 'function') {
|
||||
modelInstance.save(options, function(err, inst) {
|
||||
cb(err, inst ? inst[propertyName] : embeddedInstance);
|
||||
var hookState = {};
|
||||
var context = {
|
||||
Model: modelTo,
|
||||
currentInstance: embeddedInstance,
|
||||
data: data,
|
||||
options: options || {},
|
||||
hookState: hookState,
|
||||
};
|
||||
modelTo.notifyObserversOf('before save', context, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
embeddedInstance.setAttributes(context.data);
|
||||
|
||||
// TODO support async validations
|
||||
if (!embeddedInstance.isValid()) {
|
||||
return cb(new ValidationError(embeddedInstance));
|
||||
}
|
||||
|
||||
modelInstance.save(function(err, inst) {
|
||||
if (err) return cb(err);
|
||||
|
||||
context = {
|
||||
Model: modelTo,
|
||||
instance: inst ? inst[propertyName] : embeddedInstance,
|
||||
options: options || {},
|
||||
hookState: hookState,
|
||||
};
|
||||
modelTo.notifyObserversOf('after save', context, function(err) {
|
||||
cb(err, context.instance);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (!embeddedInstance && cb) {
|
||||
return this.callScopeMethod('create', data, cb);
|
||||
} else if (!embeddedInstance) {
|
||||
|
@ -2204,13 +2251,33 @@ EmbedsOne.prototype.destroy = function(options, cb) {
|
|||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
var modelTo = this.definition.modelTo;
|
||||
var modelInstance = this.modelInstance;
|
||||
var propertyName = this.definition.keyFrom;
|
||||
var embeddedInstance = modelInstance[propertyName];
|
||||
|
||||
if (!embeddedInstance) {
|
||||
cb();
|
||||
return cb.promise;
|
||||
}
|
||||
|
||||
modelInstance.unsetAttribute(propertyName, true);
|
||||
cb = cb || utils.createPromiseCallback();
|
||||
modelInstance.save(function(err, result) {
|
||||
cb && cb(err, result);
|
||||
|
||||
var context = {
|
||||
Model: modelTo,
|
||||
instance: embeddedInstance,
|
||||
options: options || {},
|
||||
hookState: {},
|
||||
};
|
||||
modelTo.notifyObserversOf('before delete', context, function(err) {
|
||||
if (err) return cb(err);
|
||||
modelInstance.save(function(err, result) {
|
||||
if (err) return cb(err);
|
||||
modelTo.notifyObserversOf('after delete', context, cb);
|
||||
});
|
||||
});
|
||||
|
||||
return cb.promise;
|
||||
};
|
||||
|
||||
|
|
|
@ -28,3 +28,7 @@ HookMonitor.prototype.install = function(ObservedModel, hookNames) {
|
|||
this._notify.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
HookMonitor.prototype.resetNames = function() {
|
||||
this.names = [];
|
||||
};
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright IBM Corp. 2015,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
|
||||
|
||||
var ValidationError = require('../..').ValidationError;
|
||||
|
||||
var contextTestHelpers = require('../helpers/context-test-helpers');
|
||||
var ContextRecorder = contextTestHelpers.ContextRecorder;
|
||||
var aCtxForModel = contextTestHelpers.aCtxForModel;
|
||||
|
||||
var uid = require('../helpers/uid-generator');
|
||||
var HookMonitor = require('../helpers/hook-monitor');
|
||||
|
||||
module.exports = function(dataSource, should, connectorCapabilities) {
|
||||
describe('EmbedsOne - create', function() {
|
||||
var ctxRecorder, hookMonitor, expectedError;
|
||||
|
||||
beforeEach(function setupHelpers() {
|
||||
ctxRecorder = new ContextRecorder('hook not called');
|
||||
hookMonitor = new HookMonitor({ includeModelName: true });
|
||||
expectedError = new Error('test error');
|
||||
});
|
||||
|
||||
var Owner, Embedded, ownerInstance;
|
||||
var migrated = false;
|
||||
|
||||
beforeEach(function setupDatabase() {
|
||||
Embedded = dataSource.createModel('Embedded', {
|
||||
// Set id.generated to false to honor client side values
|
||||
id: { type: String, id: true, generated: false, default: uid.next },
|
||||
name: { type: String, required: true },
|
||||
extra: { type: String, required: false },
|
||||
});
|
||||
|
||||
Owner = dataSource.createModel('Owner', {});
|
||||
Owner.embedsOne(Embedded);
|
||||
|
||||
hookMonitor.install(Embedded);
|
||||
hookMonitor.install(Owner);
|
||||
|
||||
if (migrated) {
|
||||
return Owner.deleteAll();
|
||||
} else {
|
||||
return dataSource.automigrate(Owner.modelName)
|
||||
.then(function() { migrated = true; });
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(function setupData() {
|
||||
return Owner.create({}).then(function(inst) {
|
||||
ownerInstance = inst;
|
||||
hookMonitor.resetNames();
|
||||
});
|
||||
});
|
||||
|
||||
function callCreate() {
|
||||
var item = new Embedded({ name: 'created' });
|
||||
return ownerInstance.embeddedItem.create(item);
|
||||
}
|
||||
|
||||
it('triggers hooks in the correct order', function() {
|
||||
return callCreate().then(function(result) {
|
||||
hookMonitor.names.should.eql([
|
||||
'Embedded:before save',
|
||||
//TODO 'Embedded:persist',
|
||||
'Owner:before save',
|
||||
'Owner:persist',
|
||||
'Owner:loaded',
|
||||
'Owner:after save',
|
||||
//TODO 'Embedded:loaded',
|
||||
'Embedded:after save',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('trigers `before save` hook on embedded model', function() {
|
||||
Embedded.observe('before save', ctxRecorder.recordAndNext());
|
||||
return callCreate().then(function(instance) {
|
||||
ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
|
||||
instance: {
|
||||
id: instance.id,
|
||||
name: 'created',
|
||||
extra: undefined,
|
||||
},
|
||||
// TODO isNewInstance: true,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
it('trigers `before save` hook on owner model');
|
||||
|
||||
it('applies updates from `before save` hook', function() {
|
||||
Embedded.observe('before save', function(ctx, next) {
|
||||
ctx.instance.should.be.instanceOf(Embedded);
|
||||
ctx.instance.extra = 'hook data';
|
||||
next();
|
||||
});
|
||||
return callCreate().then(function(instance) {
|
||||
instance.should.have.property('extra', 'hook data');
|
||||
});
|
||||
});
|
||||
|
||||
it('validates model after `before save` hook', function() {
|
||||
Embedded.observe('before save', invalidateEmbeddedModel);
|
||||
return callCreate().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.be.instanceOf(ValidationError);
|
||||
(err.details.codes || {}).should.eql({ name: ['presence'] });
|
||||
});
|
||||
});
|
||||
|
||||
it('aborts when `before save` hook fails', function() {
|
||||
Embedded.observe('before save', nextWithError(expectedError));
|
||||
return callCreate().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.eql(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
it('triggers `persist` hook on embedded model');
|
||||
it('triggers `persist` hook on owner model');
|
||||
it('applies updates from `persist` hook');
|
||||
it('aborts when `persist` hook fails');
|
||||
|
||||
// TODO
|
||||
it('triggers `loaded` hook on embedded model');
|
||||
it('triggers `loaded` hook on owner model');
|
||||
it('applies updates from `loaded` hook');
|
||||
it('aborts when `loaded` hook fails');
|
||||
|
||||
it('triggers `after save` hook on embedded model', function() {
|
||||
Embedded.observe('after save', ctxRecorder.recordAndNext());
|
||||
return callCreate().then(function(instance) {
|
||||
ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
|
||||
instance: {
|
||||
id: instance.id,
|
||||
name: 'created',
|
||||
extra: undefined,
|
||||
},
|
||||
// TODO isNewInstance: true,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
it('triggers `after save` hook on owner model');
|
||||
|
||||
it('applies updates from `after save` hook', function() {
|
||||
Embedded.observe('after save', function(ctx, next) {
|
||||
ctx.instance.should.be.instanceOf(Embedded);
|
||||
ctx.instance.extra = 'hook data';
|
||||
next();
|
||||
});
|
||||
return callCreate().then(function(instance) {
|
||||
instance.should.have.property('extra', 'hook data');
|
||||
});
|
||||
});
|
||||
|
||||
it('aborts when `after save` hook fails', function() {
|
||||
Embedded.observe('after save', nextWithError(expectedError));
|
||||
return callCreate().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.eql(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
function invalidateEmbeddedModel(context, next) {
|
||||
if (context.instance) {
|
||||
context.instance.name = '';
|
||||
} else {
|
||||
context.data.name = '';
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
function nextWithError(err) {
|
||||
return function(context, next) {
|
||||
next(err);
|
||||
};
|
||||
}
|
||||
|
||||
function throwShouldHaveFailed() {
|
||||
throw new Error('operation should have failed');
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright IBM Corp. 2015,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
|
||||
|
||||
var ValidationError = require('../..').ValidationError;
|
||||
|
||||
var contextTestHelpers = require('../helpers/context-test-helpers');
|
||||
var ContextRecorder = contextTestHelpers.ContextRecorder;
|
||||
var aCtxForModel = contextTestHelpers.aCtxForModel;
|
||||
|
||||
var uid = require('../helpers/uid-generator');
|
||||
var HookMonitor = require('../helpers/hook-monitor');
|
||||
|
||||
module.exports = function(dataSource, should, connectorCapabilities) {
|
||||
describe('EmbedsOne - destroy', function() {
|
||||
var ctxRecorder, hookMonitor, expectedError;
|
||||
beforeEach(function sharedSetup() {
|
||||
ctxRecorder = new ContextRecorder('hook not called');
|
||||
hookMonitor = new HookMonitor({ includeModelName: true });
|
||||
expectedError = new Error('test error');
|
||||
});
|
||||
|
||||
var Owner, Embedded;
|
||||
var migrated = false;
|
||||
beforeEach(function setupDatabase() {
|
||||
Embedded = dataSource.createModel('Embedded', {
|
||||
// Set id.generated to false to honor client side values
|
||||
id: { type: String, id: true, generated: false, default: uid.next },
|
||||
name: { type: String, required: true },
|
||||
extra: { type: String, required: false },
|
||||
});
|
||||
|
||||
Owner = dataSource.createModel('Owner', {});
|
||||
Owner.embedsOne(Embedded);
|
||||
|
||||
hookMonitor.install(Embedded);
|
||||
hookMonitor.install(Owner);
|
||||
|
||||
if (migrated) {
|
||||
return Owner.deleteAll();
|
||||
} else {
|
||||
return dataSource.automigrate(Owner.modelName)
|
||||
.then(function() { migrated = true; });
|
||||
}
|
||||
});
|
||||
|
||||
var ownerInstance, existingInstance;
|
||||
beforeEach(function setupData() {
|
||||
return Owner.create({})
|
||||
.then(function(inst) {
|
||||
ownerInstance = inst;
|
||||
})
|
||||
.then(function() {
|
||||
var item = new Embedded({ name: 'created' });
|
||||
return ownerInstance.embeddedItem.create(item).then(function(it) {
|
||||
existingItem = it;
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
hookMonitor.resetNames();
|
||||
});
|
||||
});
|
||||
|
||||
function callDestroy() {
|
||||
return ownerInstance.embeddedItem.destroy();
|
||||
}
|
||||
|
||||
it('triggers hooks in the correct order', function() {
|
||||
return callDestroy().then(function(result) {
|
||||
hookMonitor.names.should.eql([
|
||||
'Embedded:before delete',
|
||||
'Owner:before save',
|
||||
'Owner:persist',
|
||||
'Owner:loaded',
|
||||
'Owner:after save',
|
||||
'Embedded:after delete',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('trigers `before delete` hook', function() {
|
||||
Embedded.observe('before delete', ctxRecorder.recordAndNext());
|
||||
return callDestroy().then(function() {
|
||||
ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
|
||||
instance: {
|
||||
id: existingItem.id,
|
||||
name: 'created',
|
||||
extra: undefined,
|
||||
},
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
// In order to allow "before delete" hook to make changes,
|
||||
// we need to enhance the context to include information
|
||||
// about the model instance being deleted.
|
||||
// "ctx.where: { id: embedded.id }" may not be enough,
|
||||
// as it does not identify the parent (owner) model
|
||||
it('applies updates from `before delete` hook');
|
||||
|
||||
it('aborts when `before delete` hook fails', function() {
|
||||
Embedded.observe('before delete', nextWithError(expectedError));
|
||||
return callDestroy().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.eql(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
it('trigers `after delete` hook', function() {
|
||||
Embedded.observe('after delete', ctxRecorder.recordAndNext());
|
||||
return callDestroy().then(function() {
|
||||
ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
|
||||
instance: {
|
||||
id: existingItem.id,
|
||||
name: 'created',
|
||||
extra: undefined,
|
||||
},
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('aborts when `after delete` hook fails', function() {
|
||||
Embedded.observe('after delete', nextWithError(expectedError));
|
||||
return callDestroy().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.eql(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
function nextWithError(err) {
|
||||
return function(context, next) {
|
||||
next(err);
|
||||
};
|
||||
}
|
||||
|
||||
function throwShouldHaveFailed() {
|
||||
throw new Error('operation should have failed');
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright IBM Corp. 2015,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
|
||||
|
||||
var ValidationError = require('../..').ValidationError;
|
||||
|
||||
var contextTestHelpers = require('../helpers/context-test-helpers');
|
||||
var ContextRecorder = contextTestHelpers.ContextRecorder;
|
||||
var aCtxForModel = contextTestHelpers.aCtxForModel;
|
||||
|
||||
var uid = require('../helpers/uid-generator');
|
||||
var HookMonitor = require('../helpers/hook-monitor');
|
||||
|
||||
module.exports = function(dataSource, should, connectorCapabilities) {
|
||||
describe('EmbedsOne - update', function() {
|
||||
var ctxRecorder, hookMonitor, expectedError;
|
||||
beforeEach(function setupHelpers() {
|
||||
ctxRecorder = new ContextRecorder('hook not called');
|
||||
hookMonitor = new HookMonitor({ includeModelName: true });
|
||||
expectedError = new Error('test error');
|
||||
});
|
||||
|
||||
var Owner, Embedded;
|
||||
var migrated = false;
|
||||
beforeEach(function setupDatabase() {
|
||||
Embedded = dataSource.createModel('Embedded', {
|
||||
// Set id.generated to false to honor client side values
|
||||
id: { type: String, id: true, generated: false, default: uid.next },
|
||||
name: { type: String, required: true },
|
||||
extra: { type: String, required: false },
|
||||
});
|
||||
|
||||
Owner = dataSource.createModel('Owner', {});
|
||||
Owner.embedsOne(Embedded);
|
||||
|
||||
hookMonitor.install(Embedded);
|
||||
hookMonitor.install(Owner);
|
||||
|
||||
if (migrated) {
|
||||
return Owner.deleteAll();
|
||||
} else {
|
||||
return dataSource.automigrate(Owner.modelName)
|
||||
.then(function() { migrated = true; });
|
||||
}
|
||||
});
|
||||
|
||||
var ownerInstance, existingItem;
|
||||
beforeEach(function setupData() {
|
||||
return Owner.create({})
|
||||
.then(function(inst) {
|
||||
ownerInstance = inst;
|
||||
})
|
||||
.then(function() {
|
||||
var item = new Embedded({ name: 'created' });
|
||||
return ownerInstance.embeddedItem.create(item).then(function(it) {
|
||||
existingItem = it;
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
hookMonitor.resetNames();
|
||||
});
|
||||
});
|
||||
|
||||
function callUpdate() {
|
||||
return ownerInstance.embeddedItem.update({ name: 'updated' });
|
||||
}
|
||||
|
||||
it('triggers hooks in the correct order', function() {
|
||||
return callUpdate().then(function(result) {
|
||||
hookMonitor.names.should.eql([
|
||||
'Embedded:before save',
|
||||
//TODO 'Embedded:persist',
|
||||
'Owner:before save',
|
||||
'Owner:persist',
|
||||
'Owner:loaded',
|
||||
'Owner:after save',
|
||||
//TODO 'Embedded:loaded',
|
||||
'Embedded:after save',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('trigers `before save` hook on embedded model', function() {
|
||||
Embedded.observe('before save', ctxRecorder.recordAndNext());
|
||||
return callUpdate().then(function(instance) {
|
||||
ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
|
||||
currentInstance: {
|
||||
id: instance.id,
|
||||
name: 'created',
|
||||
extra: undefined,
|
||||
},
|
||||
data: {
|
||||
name: 'updated',
|
||||
},
|
||||
// TODO isNewInstance: true,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
it('trigers `before save` hook on owner model');
|
||||
|
||||
it('applies updates from `before save` hook', function() {
|
||||
Embedded.observe('before save', function(ctx, next) {
|
||||
ctx.data.extra = 'hook data';
|
||||
next();
|
||||
});
|
||||
return callUpdate().then(function(instance) {
|
||||
instance.should.have.property('extra', 'hook data');
|
||||
});
|
||||
});
|
||||
|
||||
it('validates model after `before save` hook', function() {
|
||||
Embedded.observe('before save', invalidateEmbeddedModel);
|
||||
return callUpdate().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.be.instanceOf(ValidationError);
|
||||
(err.details.codes || {}).should.eql({ name: ['presence'] });
|
||||
});
|
||||
});
|
||||
|
||||
it('aborts when `before save` hook fails', function() {
|
||||
Embedded.observe('before save', nextWithError(expectedError));
|
||||
return callUpdate().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.eql(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
it('triggers `persist` hook on embedded model');
|
||||
it('triggers `persist` hook on owner model');
|
||||
it('applies updates from `persist` hook');
|
||||
it('aborts when `persist` hook fails');
|
||||
|
||||
// TODO
|
||||
it('triggers `loaded` hook on embedded model');
|
||||
it('triggers `loaded` hook on owner model');
|
||||
it('applies updates from `loaded` hook');
|
||||
it('aborts when `loaded` hook fails');
|
||||
|
||||
it('triggers `after save` hook on embedded model', function() {
|
||||
Embedded.observe('after save', ctxRecorder.recordAndNext());
|
||||
return callUpdate().then(function(instance) {
|
||||
ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
|
||||
instance: {
|
||||
id: instance.id,
|
||||
name: 'updated',
|
||||
extra: undefined,
|
||||
},
|
||||
// TODO isNewInstance: true,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// TODO
|
||||
it('triggers `after save` hook on owner model');
|
||||
|
||||
it('applies updates from `after save` hook', function() {
|
||||
Embedded.observe('after save', function(ctx, next) {
|
||||
ctx.instance.should.be.instanceOf(Embedded);
|
||||
ctx.instance.extra = 'hook data';
|
||||
next();
|
||||
});
|
||||
return callUpdate().then(function(instance) {
|
||||
instance.should.have.property('extra', 'hook data');
|
||||
});
|
||||
});
|
||||
|
||||
it('aborts when `after save` hook fails', function() {
|
||||
Embedded.observe('after save', nextWithError(expectedError));
|
||||
return callUpdate().then(throwShouldHaveFailed, function(err) {
|
||||
err.should.eql(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
function invalidateEmbeddedModel(context, next) {
|
||||
if (context.instance) {
|
||||
context.instance.name = '';
|
||||
} else {
|
||||
context.data.name = '';
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
function nextWithError(err) {
|
||||
return function(context, next) {
|
||||
next(err);
|
||||
};
|
||||
}
|
||||
|
||||
function throwShouldHaveFailed() {
|
||||
throw new Error('operation should have failed');
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
var debug = require('debug')('test');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(dataSource, should, connectorCapabilities) {
|
||||
var operations = fs.readdirSync(__dirname);
|
||||
operations = operations.filter(function(it) {
|
||||
return it !== path.basename(__filename) &&
|
||||
!!require.extensions[path.extname(it).toLowerCase()];
|
||||
});
|
||||
for (var ix in operations) {
|
||||
var name = operations[ix];
|
||||
var fullPath = require.resolve('./' + name);
|
||||
debug('Loading test suite %s (%s)', name, fullPath);
|
||||
require(fullPath).apply(this, arguments);
|
||||
}
|
||||
};
|
|
@ -2976,6 +2976,8 @@ module.exports = function(dataSource, should, connectorCapabilities) {
|
|||
function monitorHookExecution(hookNames) {
|
||||
hookMonitor.install(TestModel, hookNames);
|
||||
}
|
||||
|
||||
require('./operation-hooks.suite')(dataSource, should, connectorCapabilities);
|
||||
});
|
||||
|
||||
function get(propertyName) {
|
||||
|
|
Loading…
Reference in New Issue