不要怂,就是干,撸起袖子干!

Commit 02fb3faa by Sushant Committed by Jan Aagaard Meier

Fix #5880, deletedAt compare against NOW (#5897)

* test to check if deletedAt allow future dates

* compare deletedAt with NOW() rather than IS NULL

* fixed tests which depends upon old deletedAt behaviour

* fixed findAndCountAll test which depend upon deletedAt old behaviour
1 parent 8e0fd546
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
- [CHANGED] Throw `bluebird.AggregateError` instead of array from `bulkCreate` when validation fails - [CHANGED] Throw `bluebird.AggregateError` instead of array from `bulkCreate` when validation fails
- [FIXED] `$notIn: []` is now converted to `NOT IN (NULL)` [#4859](https://github.com/sequelize/sequelize/issues/4859) - [FIXED] `$notIn: []` is now converted to `NOT IN (NULL)` [#4859](https://github.com/sequelize/sequelize/issues/4859)
- [FIXED] Add `raw` support to `instance.get()` [#5815](https://github.com/sequelize/sequelize/issues/5815) - [FIXED] Add `raw` support to `instance.get()` [#5815](https://github.com/sequelize/sequelize/issues/5815)
- [ADDED] Compare deletedAt against current timestamp when using paranoid [#5880](https://github.com/sequelize/sequelize/pull/5880)
## BC breaks: ## BC breaks:
- `hookValidate` removed in favor of `validate` with `hooks: true | false`. `validate` returns a promise which is rejected if validation fails - `hookValidate` removed in favor of `validate` with `hooks: true | false`. `validate` returns a promise which is rejected if validation fails
......
...@@ -139,6 +139,8 @@ var paranoidClause = function(model, options) { ...@@ -139,6 +139,8 @@ var paranoidClause = function(model, options) {
, deletedAtObject = {} , deletedAtObject = {}
, deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null; , deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
deletedAtDefaultValue = deletedAtDefaultValue || { $or: { $gte: model.sequelize.literal('CURRENT_TIMESTAMP'), $eq: null } };
deletedAtObject[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue; deletedAtObject[deletedAtAttribute.field || deletedAtCol] = deletedAtDefaultValue;
if (Utils._.isEmpty(options.where)) { if (Utils._.isEmpty(options.where)) {
......
...@@ -2,11 +2,21 @@ ...@@ -2,11 +2,21 @@
var chai = require('chai') var chai = require('chai')
, expect = chai.expect , expect = chai.expect
, sinon = require('sinon')
, Support = require(__dirname + '/../support') , Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + '/../../../lib/data-types') , DataTypes = require(__dirname + '/../../../lib/data-types')
, Promise = require('bluebird'); , Promise = require('bluebird');
describe(Support.getTestDialectTeaser('Include'), function() { describe(Support.getTestDialectTeaser('Include'), function() {
before(function () {
this.clock = sinon.useFakeTimers();
});
after(function () {
this.clock.restore();
});
describe('findAndCountAll', function() { describe('findAndCountAll', function() {
it('should be able to include a required model. Result rows should match count', function() { it('should be able to include a required model. Result rows should match count', function() {
var User = this.sequelize.define('User', { name: DataTypes.STRING(40) }, { paranoid: true }), var User = this.sequelize.define('User', { name: DataTypes.STRING(40) }, { paranoid: true }),
...@@ -19,6 +29,8 @@ describe(Support.getTestDialectTeaser('Include'), function() { ...@@ -19,6 +29,8 @@ describe(Support.getTestDialectTeaser('Include'), function() {
B = this.sequelize.define('B', { name: DataTypes.STRING(40) }, { paranoid: true }), B = this.sequelize.define('B', { name: DataTypes.STRING(40) }, { paranoid: true }),
C = this.sequelize.define('C', { name: DataTypes.STRING(40) }, { paranoid: true }); C = this.sequelize.define('C', { name: DataTypes.STRING(40) }, { paranoid: true });
var self = this;
// Associate them // Associate them
User.hasMany(SomeConnection, { foreignKey: 'u', constraints: false }); User.hasMany(SomeConnection, { foreignKey: 'u', constraints: false });
...@@ -84,13 +96,13 @@ describe(Support.getTestDialectTeaser('Include'), function() { ...@@ -84,13 +96,13 @@ describe(Support.getTestDialectTeaser('Include'), function() {
{ name: 'because we only want A' } { name: 'because we only want A' }
]) ])
).then(function () { ).then(function () {
// Delete some of conns to prove the concept // Delete some of conns to prove the concept
return SomeConnection.destroy({where: { return SomeConnection.destroy({where: {
m: 'A', m: 'A',
u: 1, u: 1,
fk: [1, 2] fk: [1, 2]
}}).then(function() { }}).then(function() {
self.clock.tick(1000);
// Last and most important queries ( we connected 4, but deleted 2, witch means we must get 2 only ) // Last and most important queries ( we connected 4, but deleted 2, witch means we must get 2 only )
return A.findAndCountAll({ return A.findAndCountAll({
include: [{ include: [{
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
var chai = require('chai') var chai = require('chai')
, expect = chai.expect , expect = chai.expect
, sinon = require('sinon')
, Support = require(__dirname + '/../support') , Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + '/../../../lib/data-types'); , DataTypes = require(__dirname + '/../../../lib/data-types');
...@@ -31,6 +32,14 @@ describe(Support.getTestDialectTeaser('Paranoid'), function() { ...@@ -31,6 +32,14 @@ describe(Support.getTestDialectTeaser('Paranoid'), function() {
return S.sync({ force: true }); return S.sync({ force: true });
}); });
before(function () {
this.clock = sinon.useFakeTimers();
});
after(function () {
this.clock.restore();
});
it('paranoid with timestamps: false should be ignored / not crash', function() { it('paranoid with timestamps: false should be ignored / not crash', function() {
var S = this.sequelize var S = this.sequelize
, Test = S.define('Test', { , Test = S.define('Test', {
...@@ -108,6 +117,9 @@ describe(Support.getTestDialectTeaser('Paranoid'), function() { ...@@ -108,6 +117,9 @@ describe(Support.getTestDialectTeaser('Paranoid'), function() {
}).then(function () { }).then(function () {
return this.y.destroy(); return this.y.destroy();
}).then(function () { }).then(function () {
//prevent CURRENT_TIMESTAMP to be same
this.clock.tick(1000);
return X.findAll({ return X.findAll({
include: [Y] include: [Y]
}).get(0); }).get(0);
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* jshint -W030 */ /* jshint -W030 */
var chai = require('chai') var chai = require('chai')
, sinon = require('sinon')
, Sequelize = require('../../../../index') , Sequelize = require('../../../../index')
, Promise = Sequelize.Promise , Promise = Sequelize.Promise
, expect = chai.expect , expect = chai.expect
...@@ -10,6 +11,15 @@ var chai = require('chai') ...@@ -10,6 +11,15 @@ var chai = require('chai')
, dialect = Support.getTestDialect(); , dialect = Support.getTestDialect();
describe(Support.getTestDialectTeaser('Model'), function() { describe(Support.getTestDialectTeaser('Model'), function() {
before(function () {
this.clock = sinon.useFakeTimers();
});
after(function () {
this.clock.restore();
});
describe('attributes', function() { describe('attributes', function() {
describe('field', function() { describe('field', function() {
beforeEach(function() { beforeEach(function() {
...@@ -523,15 +533,21 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -523,15 +533,21 @@ describe(Support.getTestDialectTeaser('Model'), function() {
paranoid: true paranoid: true
}); });
return User.sync({force: true}).then(function () { return User.sync({force: true})
return User.create().then(function (user) { .bind(this)
.then(function () {
return User.create();
})
.then(function (user) {
return user.destroy(); return user.destroy();
}).then(function () { })
return User.findAll().then(function (users) { .then(function () {
expect(users.length).to.equal(0); this.clock.tick(1000);
}); return User.findAll();
})
.then(function (users) {
expect(users.length).to.equal(0);
}); });
});
}); });
it('should work with paranoid Model.destroy()', function () { it('should work with paranoid Model.destroy()', function () {
......
...@@ -7,6 +7,7 @@ var chai = require('chai') ...@@ -7,6 +7,7 @@ var chai = require('chai')
, Sequelize = require('../../../index') , Sequelize = require('../../../index')
, Promise = Sequelize.Promise , Promise = Sequelize.Promise
, expect = chai.expect , expect = chai.expect
, moment = require('moment')
, Support = require(__dirname + '/../support') , Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + '/../../../lib/data-types') , DataTypes = require(__dirname + '/../../../lib/data-types')
, config = require(__dirname + '/../../config/config') , config = require(__dirname + '/../../config/config')
...@@ -987,5 +988,28 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -987,5 +988,28 @@ describe(Support.getTestDialectTeaser('Model'), function() {
}); });
it('should find records where deletedAt set to future', function() {
var User = this.sequelize.define('paranoiduser', {
username: Sequelize.STRING
}, { paranoid: true });
return User.sync({ force: true }).then(function() {
return User.bulkCreate([
{username: 'Bob'},
{username: 'Tobi', deletedAt: moment().add(30, 'minutes').format()},
{username: 'Max', deletedAt: moment().add(30, 'days').format()},
{username: 'Tony', deletedAt: moment().subtract(30, 'days').format()}
]);
}).then(function() {
return User.find({ where: {username: 'Tobi'} });
}).then(function(tobi) {
expect(tobi).not.to.be.null;
}).then(function() {
return User.findAll();
}).then(function(users) {
expect(users.length).to.be.eql(3);
});
});
}); });
}); });
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!