2016-04-20 11:52:06 +00:00
|
|
|
// 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
|
|
|
|
|
2016-08-22 19:55:22 +00:00
|
|
|
'use strict';
|
|
|
|
|
2018-12-07 16:13:48 +00:00
|
|
|
const Promise = require('bluebird');
|
|
|
|
const ValidationError = require('../..').ValidationError;
|
2016-04-20 11:52:06 +00:00
|
|
|
|
2018-12-07 16:13:48 +00:00
|
|
|
const contextTestHelpers = require('../helpers/context-test-helpers');
|
|
|
|
const ContextRecorder = contextTestHelpers.ContextRecorder;
|
|
|
|
const aCtxForModel = contextTestHelpers.aCtxForModel;
|
2016-04-20 11:52:06 +00:00
|
|
|
|
2018-12-07 16:13:48 +00:00
|
|
|
const uid = require('../helpers/uid-generator');
|
|
|
|
const HookMonitor = require('../helpers/hook-monitor');
|
2016-04-20 11:52:06 +00:00
|
|
|
|
|
|
|
module.exports = function(dataSource, should, connectorCapabilities) {
|
|
|
|
describe('EmbedsMany - destroy', function() {
|
2018-12-07 16:13:48 +00:00
|
|
|
let ctxRecorder, hookMonitor, expectedError;
|
2016-04-20 11:52:06 +00:00
|
|
|
beforeEach(function sharedSetup() {
|
|
|
|
ctxRecorder = new ContextRecorder('hook not called');
|
2016-08-19 17:46:59 +00:00
|
|
|
hookMonitor = new HookMonitor({includeModelName: true});
|
2016-04-20 11:52:06 +00:00
|
|
|
expectedError = new Error('test error');
|
|
|
|
});
|
|
|
|
|
2018-12-07 16:13:48 +00:00
|
|
|
let Owner, Embedded;
|
|
|
|
let migrated = false;
|
2016-04-20 11:52:06 +00:00
|
|
|
beforeEach(function setupDatabase() {
|
|
|
|
Embedded = dataSource.createModel('Embedded', {
|
|
|
|
// Set id.generated to false to honor client side values
|
2016-08-19 17:46:59 +00:00
|
|
|
id: {type: String, id: true, generated: false, default: uid.next},
|
|
|
|
name: {type: String, required: true},
|
|
|
|
extra: {type: String, required: false},
|
2016-04-20 11:52:06 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Owner = dataSource.createModel('Owner', {});
|
|
|
|
Owner.embedsMany(Embedded);
|
|
|
|
|
|
|
|
hookMonitor.install(Embedded);
|
|
|
|
hookMonitor.install(Owner);
|
|
|
|
|
|
|
|
if (migrated) {
|
|
|
|
return Owner.deleteAll();
|
|
|
|
} else {
|
|
|
|
return dataSource.automigrate(Owner.modelName)
|
|
|
|
.then(function() { migrated = true; });
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-12-07 16:13:48 +00:00
|
|
|
let ownerInstance, existingInstance, existingItem;
|
2016-04-20 11:52:06 +00:00
|
|
|
beforeEach(function setupData() {
|
|
|
|
return Owner.create({})
|
|
|
|
.then(function(inst) {
|
|
|
|
ownerInstance = inst;
|
|
|
|
})
|
|
|
|
.then(function() {
|
2018-12-07 16:13:48 +00:00
|
|
|
const item = new Embedded({name: 'created'});
|
2016-04-20 11:52:06 +00:00
|
|
|
return ownerInstance.embeddedList.create(item).then(function(it) {
|
|
|
|
existingItem = it;
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.then(function() {
|
|
|
|
hookMonitor.resetNames();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function callDestroy() {
|
|
|
|
// Unfortunately, updateById was not promisified yet
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
return ownerInstance.embeddedList.destroy(
|
|
|
|
existingItem.id,
|
|
|
|
function(err, result) {
|
|
|
|
if (err) reject(err);
|
|
|
|
else resolve(result);
|
2018-07-16 06:46:25 +00:00
|
|
|
}
|
|
|
|
);
|
2016-04-20 11:52:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|