diff --git a/lib/dao.js b/lib/dao.js index 55846e7e..b3e98408 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -148,7 +148,10 @@ function noCallback(err, result) { * - instance (null or Model) */ DataAccessObject.create = function (data, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } var Model = this; var self = this; @@ -167,9 +170,9 @@ DataAccessObject.create = function (data, options, cb) { } } - cb = cb || noCallback; data = data || {}; options = options || {}; + cb = cb || (Array.isArray(data) ? noCallback : utils.createPromiseCallback()); assert(typeof data === 'object', 'The data argument must be an object or array'); assert(typeof options === 'object', 'The options argument must be an object'); @@ -276,12 +279,27 @@ DataAccessObject.create = function (data, options, cb) { }, obj, cb); } + // Does this make any sense? How would chaining be used here? -partap + // for chaining - return obj; + return cb.promise || obj; }; function stillConnecting(dataSource, obj, args) { - return dataSource.ready(obj, args); + if (typeof args[args.length-1] === 'function') { + return dataSource.ready(obj, args); + } + + // promise variant + var promiseArgs = Array.prototype.slice.call(args); + promiseArgs.callee = args.callee + var cb = utils.createPromiseCallback(); + promiseArgs.push(cb); + if (dataSource.ready(obj, promiseArgs)) { + return cb.promise; + } else { + return false; + } } /** @@ -298,8 +316,9 @@ function stillConnecting(dataSource, obj, args) { // 'upsert' will be used as the name for strong-remoting to keep it backward // compatible for angular SDK DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) { - return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; } if (options === undefined && cb === undefined) { @@ -316,7 +335,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); data = data || {}; options = options || {}; @@ -404,6 +423,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data }); } } + return cb.promise; }; /** @@ -418,10 +438,19 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data * @param {Function} cb Callback called with (err, instance, created) */ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } - assert(arguments.length >= 2, 'At least two arguments are required'); - if (options === undefined && cb === undefined) { + assert(arguments.length >= 1, 'At least one argument is required'); + if (data === undefined && options === undefined && cb === undefined) { + assert(typeof query === 'object', 'Single argument must be data object'); + // findOrCreate(data); + // query will be built from data, and method will return Promise + data = query; + query = {where: data}; + } else if (options === undefined && cb === undefined) { if (typeof data === 'function') { // findOrCreate(data, cb); // query will be built from data @@ -437,7 +466,7 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); query = query || {where: {}}; data = data || {}; options = options || {}; @@ -466,11 +495,19 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) if (created) { Model.notifyObserversOf('after save', { Model: Model, instance: obj }, function(err) { - cb(err, obj, created); + if (cb.promise) { + cb(err, [obj, created]); + } else { + cb(err, obj, created); + } if (!err) Model.emit('changed', obj); }); } else { - cb(err, obj, created); + if (cb.promise) { + cb(err, [obj, created]); + } else { + cb(err, obj, created); + } } }); } @@ -481,9 +518,10 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) try { this._normalize(query); } catch (err) { - return process.nextTick(function () { + process.nextTick(function () { cb(err); }); + return cb.promise; } this.applyScope(query); @@ -521,12 +559,23 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) } else { Model.findOne(query, options, function (err, record) { if (err) return cb(err); - if (record) return cb(null, record, false); + if (record) { + if (cb.promise) { + return cb(null, [record, false]); + } else { + return cb(null, record, false); + } + } Model.create(data, options, function (err, record) { - cb(err, record, record != null); + if (cb.promise) { + cb(err, [record, record != null]); + } else { + cb(err, record, record != null); + } }); }); } + return cb.promise; }; /** @@ -537,7 +586,10 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) * @param {Function} cb Callback function called with (err, exists: Bool) */ DataAccessObject.exists = function exists(id, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } assert(arguments.length >= 1, 'The id argument is required'); if (cb === undefined) { @@ -548,7 +600,7 @@ DataAccessObject.exists = function exists(id, options, cb) { } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); options = options || {}; assert(typeof options === 'object', 'The options argument must be an object'); @@ -559,10 +611,11 @@ DataAccessObject.exists = function exists(id, options, cb) { cb(err, err ? false : count === 1); }); } else { - return process.nextTick(function() { + process.nextTick(function() { cb(new Error('Model::exists requires the id argument')); }); } + return cb.promise; }; /** @@ -580,7 +633,10 @@ DataAccessObject.exists = function exists(id, options, cb) { * @param {Function} cb Callback called with (err, instance) */ DataAccessObject.findById = function find(id, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } assert(arguments.length >= 1, 'The id argument is required'); if (cb === undefined) { @@ -590,18 +646,20 @@ DataAccessObject.findById = function find(id, options, cb) { options = {}; } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); options = options || {}; assert(typeof options === 'object', 'The options argument must be an object'); assert(typeof cb === 'function', 'The cb argument must be a function'); if (id == null || id === '') { - return process.nextTick(function() { + process.nextTick(function() { cb(new Error('Model::findById requires the id argument')); }); + } else { + this.findOne(byIdQuery(this, id), options, cb); } - this.findOne(byIdQuery(this, id), options, cb); + return cb.promise; }; /** @@ -626,7 +684,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) { } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); options = options || {}; query = query || {}; @@ -638,7 +696,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) { var pk = idName(this); if (ids.length === 0) { process.nextTick(function() { cb(null, []); }); - return; + return cb.promise;; } var filter = { where: {} }; @@ -647,6 +705,7 @@ DataAccessObject.findByIds = function(ids, query, options, cb) { this.find(filter, options, function(err, results) { cb(err, err ? results : utils.sortObjectsByIds(pk, ids, results)); }); + return cb.promise; }; function convertNullToNotFoundError(ctx, cb) { @@ -662,7 +721,7 @@ function convertNullToNotFoundError(ctx, cb) { // alias function for backwards compat. DataAccessObject.all = function () { - DataAccessObject.find.apply(this, arguments); + return DataAccessObject.find.apply(this, arguments); }; var operators = { @@ -994,7 +1053,10 @@ DataAccessObject._coerce = function (where) { */ DataAccessObject.find = function find(query, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } if (options === undefined && cb === undefined) { if (typeof query === 'function') { @@ -1010,7 +1072,7 @@ DataAccessObject.find = function find(query, options, cb) { } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); query = query || {}; options = options || {}; @@ -1023,9 +1085,10 @@ DataAccessObject.find = function find(query, options, cb) { try { this._normalize(query); } catch (err) { - return process.nextTick(function () { + process.nextTick(function () { cb(err); }); + return cb.promise; } this.applyScope(query); @@ -1073,7 +1136,7 @@ DataAccessObject.find = function find(query, options, cb) { }); // already handled - return; + return cb.promise; } } @@ -1135,6 +1198,7 @@ DataAccessObject.find = function find(query, options, cb) { self.getDataSource().connector.all(self.modelName, query, allCb); }); } + return cb.promise; }; /** @@ -1146,7 +1210,10 @@ DataAccessObject.find = function find(query, options, cb) { * @param {Function} cb Callback function called with (err, instance) */ DataAccessObject.findOne = function findOne(query, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } if (options === undefined && cb === undefined) { if (typeof query === 'function') { @@ -1160,7 +1227,7 @@ DataAccessObject.findOne = function findOne(query, options, cb) { } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); query = query || {}; options = options || {}; @@ -1173,6 +1240,7 @@ DataAccessObject.findOne = function findOne(query, options, cb) { if (err || !collection || !collection.length > 0) return cb(err, null); cb(err, collection[0]); }); + return cb.promise; }; /** @@ -1190,7 +1258,10 @@ DataAccessObject.findOne = function findOne(query, options, cb) { * @param {Function} [cb] Callback called with (err) */ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyAll = function destroyAll(where, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } var Model = this; @@ -1206,7 +1277,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); where = where || {}; options = options || {}; @@ -1267,6 +1338,7 @@ DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyA }); } } + return cb.promise; }; function whereIsEmpty(where) { @@ -1285,7 +1357,10 @@ function whereIsEmpty(where) { // 'deleteById' will be used as the name for strong-remoting to keep it backward // compatible for angular SDK DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.deleteById = function deleteById(id, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } assert(arguments.length >= 1, 'The id argument is required'); if (cb === undefined) { @@ -1297,15 +1372,16 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de } options = options || {}; - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); assert(typeof options === 'object', 'The options argument must be an object'); assert(typeof cb === 'function', 'The cb argument must be a function'); if (id == null || id === '') { - return process.nextTick(function() { + process.nextTick(function() { cb(new Error('Model::deleteById requires the id argument')); }); + return cb.promise; } var Model = this; @@ -1316,6 +1392,7 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de } if(!err) Model.emit('deleted', id); }); + return cb.promise; }; /** @@ -1333,7 +1410,10 @@ DataAccessObject.removeById = DataAccessObject.destroyById = DataAccessObject.de * @param {Function} cb Callback, called with (err, count) */ DataAccessObject.count = function (where, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } if (options === undefined && cb === undefined) { if (typeof where === 'function') { @@ -1349,7 +1429,7 @@ DataAccessObject.count = function (where, options, cb) { } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); where = where || {}; options = options || {}; @@ -1365,9 +1445,10 @@ DataAccessObject.count = function (where, options, cb) { where = removeUndefined(where); where = this._coerce(where); } catch (err) { - return process.nextTick(function () { + process.nextTick(function () { cb(err); }); + return cb.promise; } var Model = this; @@ -1376,6 +1457,7 @@ DataAccessObject.count = function (where, options, cb) { where = ctx.query.where; Model.getDataSource().connector.count(Model.modelName, cb, where); }); + return cb.promise; }; /** @@ -1387,7 +1469,10 @@ DataAccessObject.count = function (where, options, cb) { * @param {Function} cb Callback function with err and object arguments */ DataAccessObject.prototype.save = function (options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } var Model = this.constructor; if (typeof options === 'function') { @@ -1395,7 +1480,7 @@ DataAccessObject.prototype.save = function (options, cb) { options = {}; } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); options = options || {}; assert(typeof options === 'object', 'The options argument should be an object'); @@ -1466,6 +1551,7 @@ DataAccessObject.prototype.save = function (options, cb) { }, data, cb); } }); + return cb.promise; }; /** @@ -1486,27 +1572,39 @@ DataAccessObject.prototype.save = function (options, cb) { */ DataAccessObject.update = DataAccessObject.updateAll = function (where, data, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } - assert(arguments.length >= 2, 'At least two arguments are required'); - if (options === undefined && cb === undefined) { + assert(arguments.length >= 1, 'At least one argument is required'); + + if (data === undefined && options === undefined && cb === undefined && arguments.length === 1) { + data = where; + where = {}; + } else if (options === undefined && cb === undefined) { + // One of: + // updateAll(data, cb) + // updateAll(where, data) -> Promise if (typeof data === 'function') { - // updateAll(data, cb); cb = data; data = where; where = {}; } + } else if (cb === undefined) { + // One of: + // updateAll(where, data, options) -> Promise + // updateAll(where, data, cb) if (typeof options === 'function') { - // updateAll(query, data, cb) cb = options; options = {}; } } - cb = cb || noCallback; data = data || {}; options = options || {}; + cb = cb || utils.createPromiseCallback(); assert(typeof where === 'object', 'The where argument must be an object'); assert(typeof data === 'object', 'The data argument must be an object'); @@ -1564,6 +1662,7 @@ DataAccessObject.updateAll = function (where, data, options, cb) { }); }); } + return cb.promise; }; DataAccessObject.prototype.isNewRecord = function () { @@ -1589,14 +1688,17 @@ DataAccessObject.prototype._adapter = function () { DataAccessObject.prototype.remove = DataAccessObject.prototype.delete = DataAccessObject.prototype.destroy = function (options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } if (cb === undefined && typeof options === 'function') { cb = options; options = {}; } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); options = options || {}; assert(typeof options === 'object', 'The options argument should be an object'); @@ -1650,6 +1752,7 @@ DataAccessObject.prototype.remove = }); }, null, cb); } + return cb.promise; }; /** @@ -1674,7 +1777,7 @@ DataAccessObject.prototype.setAttribute = function setAttribute(name, value) { DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, cb) { var data = {}; data[name] = value; - this.updateAttributes(data, cb); + return this.updateAttributes(data, cb); }; /** @@ -1718,7 +1821,10 @@ DataAccessObject.prototype.unsetAttribute = function unsetAttribute(name, nullif * @param {Function} cb Callback function called with (err, instance) */ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, options, cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; + } if (options === undefined && cb === undefined) { if (typeof data === 'function') { @@ -1734,7 +1840,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op } } - cb = cb || noCallback; + cb = cb || utils.createPromiseCallback(); options = options || {}; assert((typeof data === 'object') && (data !== null), @@ -1803,6 +1909,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op }, data, cb); }, data); }); +return cb.promise; }; /** @@ -1812,11 +1919,12 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op * @private */ DataAccessObject.prototype.reload = function reload(cb) { - if (stillConnecting(this.getDataSource(), this, arguments)) { - return; + var connectionPromise = stillConnecting(this.getDataSource(), this, arguments); + if (connectionPromise) { + return connectionPromise; } - this.constructor.findById(getIdValue(this.constructor, this), cb); + return this.constructor.findById(getIdValue(this.constructor, this), cb); }; diff --git a/test/manipulation.test.js b/test/manipulation.test.js index 1864dbfc..77311069 100644 --- a/test/manipulation.test.js +++ b/test/manipulation.test.js @@ -59,6 +59,21 @@ describe('manipulation', function () { }); }); + it('should create instance (promise variant)', function (done) { + Person.create({name: 'Anatoliy'}) + .then (function (p) { + p.name.should.equal('Anatoliy'); + should.exist(p); + return Person.findById(p.id) + .then (function (person) { + person.id.should.eql(p.id); + person.name.should.equal('Anatoliy'); + done(); + }); + }) + .catch(done); + }); + it('should instantiate an object', function (done) { var p = new Person({name: 'Anatoliy'}); p.name.should.equal('Anatoliy'); @@ -71,6 +86,20 @@ describe('manipulation', function () { }); }); + it('should instantiate an object (promise variant)', function (done) { + var p = new Person({name: 'Anatoliy'}); + p.name.should.equal('Anatoliy'); + p.isNewRecord().should.be.true; + p.save() + .then (function(inst) { + inst.isNewRecord().should.be.false; + inst.should.equal(p); + done(); + }) + .catch(done); + + }); + it('should return instance of object', function (done) { var person = Person.create(function (err, p) { p.id.should.eql(person.id); @@ -93,6 +122,19 @@ describe('manipulation', function () { }); }); + it('should not allow user-defined value for the id of object - create (promise variant)', function (done) { + Person.create({id: 123456}) + .then (function (p) { + done(new Error('Person.create should have failed.')); + }, function (err) { + err.should.be.instanceof(ValidationError); + err.statusCode.should.equal(422); + err.details.messages.id.should.eql(['can\'t be set']); + done(); + }) + .catch(done); + }); + it('should not allow user-defined value for the id of object - save', function (done) { var p = new Person({id: 123456}); p.isNewRecord().should.be.true; @@ -106,6 +148,21 @@ describe('manipulation', function () { }); }); + it('should not allow user-defined value for the id of object - save (promise variant)', function (done) { + var p = new Person({id: 123456}); + p.isNewRecord().should.be.true; + p.save() + .then (function(inst) { + done(new Error('save should have failed.')); + }, function (err) { + err.should.be.instanceof(ValidationError); + err.statusCode.should.equal(422); + err.details.messages.id.should.eql(['can\'t be set']); + done(); + }) + .catch(done); + }); + it('should work when called without callback', function (done) { Person.afterCreate = function (next) { this.should.be.an.instanceOf(Person); @@ -131,6 +188,20 @@ describe('manipulation', function () { }); }); + it('should create instance with blank data (promise variant)', function (done) { + Person.create() + .then (function (p) { + should.exist(p); + should.not.exists(p.name); + return Person.findById(p.id) + .then (function (person) { + person.id.should.eql(p.id); + should.not.exists(person.name); + done(); + }); + }).catch(done); + }); + it('should work when called with no data and callback', function (done) { Person.afterCreate = function (next) { this.should.be.an.instanceOf(Person); @@ -219,6 +290,7 @@ describe('manipulation', function () { }); }); }); + }); describe('save', function () { @@ -232,6 +304,16 @@ describe('manipulation', function () { }); }); + it('should save new object (promise variant)', function (done) { + var p = new Person; + p.save() + .then(function () { + should.exist(p.id); + done(); + }) + .catch(done); + }); + it('should save existing object', function (done) { Person.findOne(function (err, p) { should.not.exist(err); @@ -247,6 +329,22 @@ describe('manipulation', function () { }); }); + it('should save existing object (promise variant)', function (done) { + Person.findOne() + .then(function (p) { + p.name = 'Fritz'; + return p.save() + .then(function () { + return Person.findOne() + .then(function (p) { + p.name.should.equal('Fritz'); + done(); + }); + }); + }) + .catch(done); + }); + it('should save invalid object (skipping validation)', function (done) { Person.findOne(function (err, p) { should.not.exist(err); @@ -265,6 +363,29 @@ describe('manipulation', function () { }); }); + it('should save invalid object (skipping validation - promise variant)', function (done) { + Person.findOne() + .then(function (p) { + p.isValid = function (done) { + process.nextTick(done); + return false; + }; + p.name = 'Nana'; + return p.save() + .then(function (d) { + done(new Error('save should have failed.')); + }, function (err) { + should.exist(err); + p.save({validate: false}) + .then(function (d) { + should.exist(d); + done(); + }); + }); + }) + .catch(done); + }); + it('should save throw error on validation', function () { Person.findOne(function (err, p) { should.not.exist(err); @@ -312,7 +433,7 @@ describe('manipulation', function () { person.updateAttribute('name', 'Paul Graham', function (err, p) { should.not.exist(err); Person.all(function (e, ps) { - should.not.exist(err); + should.not.exist(e); ps.should.have.lengthOf(1); ps.pop().name.should.equal('Paul Graham'); done(); @@ -320,12 +441,24 @@ describe('manipulation', function () { }); }); + it('should update one attribute (promise variant)', function (done) { + person.updateAttribute('name', 'Teddy Graham') + .then(function (p) { + return Person.all() + .then(function (ps) { + ps.should.have.lengthOf(1); + ps.pop().name.should.equal('Teddy Graham'); + done(); + }); + }).catch(done); + }); + it('should ignore undefined values on updateAttributes', function(done) { person.updateAttributes({'name': 'John', age: undefined}, function(err, p) { should.not.exist(err); Person.findById(p.id, function(e, p) { - should.not.exist(err); + should.not.exist(e); p.name.should.equal('John'); p.age.should.equal(15); done(); @@ -338,7 +471,7 @@ describe('manipulation', function () { function(err, p) { should.not.exist(err); Person.findById(p.id, function(e, p) { - should.not.exist(err); + should.not.exist(e); p.name.should.equal('John'); p.age.should.equal(15); done(); @@ -346,6 +479,19 @@ describe('manipulation', function () { }); }); + it('should allows model instance on updateAttributes (promise variant)', function(done) { + person.updateAttributes(new Person({'name': 'Jane', age: undefined})) + .then(function(p) { + return Person.findById(p.id) + .then(function(p) { + p.name.should.equal('Jane'); + p.age.should.equal(15); + done(); + }); + }) + .catch(done); + }); + }); describe('updateOrCreate', function() { @@ -404,6 +550,71 @@ describe('manipulation', function () { }); }); + describe('findOrCreate', function() { + it('should create a record with if new', function(done) { + Person.findOrCreate({ name: 'Zed', gender: 'male' }, + function(err, p, created) { + if (err) return done(err); + should.exist(p); + p.should.be.instanceOf(Person); + p.name.should.equal('Zed'); + p.gender.should.equal('male'); + created.should.equal(true); + done(); + }); + }); + + it('should find a record if exists', function(done) { + Person.findOrCreate( + {where: {name: 'Zed'}}, + {name: 'Zed', gender: 'male'}, + function(err, p, created) { + if (err) return done(err); + should.exist(p); + p.should.be.instanceOf(Person); + p.name.should.equal('Zed'); + p.gender.should.equal('male'); + created.should.equal(false); + done(); + }); + }); + + it('should create a record with if new (promise variant)', function(done) { + Person.findOrCreate({ name: 'Jed', gender: 'male' }) + .then(function(res) { + should.exist(res); + res.should.be.instanceOf(Array); + res.should.have.lengthOf(2); + var p = res[0]; + var created = res[1]; + p.should.be.instanceOf(Person); + p.name.should.equal('Jed'); + p.gender.should.equal('male'); + created.should.equal(true); + done(); + }) + .catch(done); + }); + + it('should find a record if exists (promise variant)', function(done) { + Person.findOrCreate( + {where: {name: 'Jed'}}, + {name: 'Jed', gender: 'male'}) + .then(function(res) { + res.should.be.instanceOf(Array); + res.should.have.lengthOf(2); + var p = res[0]; + var created = res[1]; + p.should.be.instanceOf(Person); + p.name.should.equal('Jed'); + p.gender.should.equal('male'); + created.should.equal(false); + done(); + }) + .catch(done); + }); + }); + describe('destroy', function () { it('should destroy record', function (done) { @@ -418,6 +629,21 @@ describe('manipulation', function () { }); }); + it('should destroy record (promise variant)', function (done) { + Person.create() + .then(function (p) { + return p.destroy() + .then(function () { + return Person.exists(p.id) + .then(function (ex) { + ex.should.not.be.ok; + done(); + }); + }); + }) + .catch(done); + }); + it('should destroy all records', function (done) { Person.destroyAll(function (err) { should.not.exist(err); @@ -431,6 +657,26 @@ describe('manipulation', function () { }); }); + it('should destroy all records (promise variant)', function (done) { + Person.create() + .then(function() { + return Person.destroyAll() + .then(function () { + return Person.all() + .then(function (ps) { + ps.should.have.lengthOf(0); + return Person.count() + .then(function (count) { + count.should.eql(0); + done(); + }); + }); + }); + }) + .catch(done); + + }); + // TODO: implement destroy with filtered set it('should destroy filtered set of records'); });