From ead1fb29657839c852d1d2cc04778eb2d8b688e9 Mon Sep 17 00:00:00 2001 From: Tetsuo Seto Date: Thu, 6 Apr 2017 13:04:16 -0700 Subject: [PATCH] Add test support for Cassandra connector --- test/basic-querying.test.js | 140 +++--- test/datatype.test.js | 9 +- test/helpers/uid-generator.js | 5 + test/manipulation.test.js | 418 +++++++++++----- test/relations.test.js | 886 +++++++++++++++++++++------------- 5 files changed, 939 insertions(+), 519 deletions(-) diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index d9859eb2..7358ee30 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -7,8 +7,11 @@ 'use strict'; /* global getSchema:false, connectorCapabilities:false */ -var should = require('./init.js'); var async = require('async'); +var bdd = require('./helpers/bdd-if'); +var should = require('./init.js'); +var uid = require('./helpers/uid-generator'); + var db, User; describe('basic-querying', function() { @@ -54,11 +57,13 @@ describe('basic-querying', function() { describe('findById', function() { before(function(done) { + db = getSchema(); User.destroyAll(done); }); it('should query by id: not found', function(done) { - User.findById(1, function(err, u) { + var unknownId = uid.fromConnector(db) || 1; + User.findById(unknownId, function(err, u) { should.not.exist(u); should.not.exist(err); done(); @@ -82,6 +87,7 @@ describe('basic-querying', function() { describe('findByIds', function() { var createdUsers; before(function(done) { + db = getSchema(); var people = [ {name: 'a', vip: true}, {name: 'b'}, @@ -168,7 +174,8 @@ describe('basic-querying', function() { }); }); - it('should query collection with skip & limit', function(done) { + bdd.itIf(connectorCapabilities.supportPagination !== false, 'should query collection with skip & ' + + 'limit', function(done) { User.find({skip: 1, limit: 4, order: 'seq'}, function(err, users) { should.exists(users); should.not.exists(err); @@ -178,7 +185,8 @@ describe('basic-querying', function() { }); }); - it('should query collection with offset & limit', function(done) { + bdd.itIf(connectorCapabilities.supportPagination !== false, 'should query collection with offset & ' + + 'limit', function(done) { User.find({offset: 2, limit: 3, order: 'seq'}, function(err, users) { should.exists(users); should.not.exists(err); @@ -197,7 +205,8 @@ describe('basic-querying', function() { }); }); - it('should query collection sorted by numeric field', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, 'should query collection sorted by numeric ' + + 'field', function(done) { User.find({order: 'order'}, function(err, users) { should.exists(users); should.not.exists(err); @@ -208,7 +217,8 @@ describe('basic-querying', function() { }); }); - it('should query collection desc sorted by numeric field', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, 'should query collection desc sorted by ' + + 'numeric field', function(done) { User.find({order: 'order DESC'}, function(err, users) { should.exists(users); should.not.exists(err); @@ -219,7 +229,8 @@ describe('basic-querying', function() { }); }); - it('should query collection sorted by string field', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, 'should query collection sorted by string ' + + 'field', function(done) { User.find({order: 'name'}, function(err, users) { should.exists(users); should.not.exists(err); @@ -230,7 +241,8 @@ describe('basic-querying', function() { }); }); - it('should query collection desc sorted by string field', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, 'should query collection desc sorted by ' + + 'string field', function(done) { User.find({order: 'name DESC'}, function(err, users) { should.exists(users); should.not.exists(err); @@ -241,11 +253,10 @@ describe('basic-querying', function() { }); }); - it('should query sorted desc by order integer field even though there' + - 'is an async model loaded hook', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, 'should query sorted desc by order integer field' + + ' even though there is an async model loaded hook', function(done) { User.find({order: 'order DESC'}, function(err, users) { if (err) return done(err); - should.exists(users); var order = users.map(function(u) { return u.order; }); order.should.eql([6, 5, 4, 3, 2, 1]); @@ -275,7 +286,8 @@ describe('basic-querying', function() { }); }); - it('should support "or" that is satisfied', function(done) { + bdd.itIf(connectorCapabilities.supportOrOperator !== false, 'should support "or" that is ' + + 'satisfied', function(done) { User.find({where: {or: [ {name: 'John Lennon'}, {role: 'lead'}, @@ -286,7 +298,8 @@ describe('basic-querying', function() { }); }); - it('should support "or" operator that is not satisfied', function(done) { + bdd.itIf(connectorCapabilities.supportOrOperator !== false, 'should support "or" operator that is ' + + 'not satisfied', function(done) { User.find({where: {or: [ {name: 'XYZ'}, {role: 'Hello1'}, @@ -298,7 +311,7 @@ describe('basic-querying', function() { }); it('should support date "gte" that is satisfied', function(done) { - User.find({order: 'seq', where: {birthday: {'gte': new Date('1980-12-08')}, + User.find({where: {birthday: {'gte': new Date('1980-12-08')}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -308,7 +321,7 @@ describe('basic-querying', function() { }); it('should support date "gt" that is not satisfied', function(done) { - User.find({order: 'seq', where: {birthday: {'gt': new Date('1980-12-08')}, + User.find({where: {birthday: {'gt': new Date('1980-12-08')}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 0); @@ -317,7 +330,7 @@ describe('basic-querying', function() { }); it('should support date "gt" that is satisfied', function(done) { - User.find({order: 'seq', where: {birthday: {'gt': new Date('1980-12-07')}, + User.find({where: {birthday: {'gt': new Date('1980-12-07')}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -327,7 +340,7 @@ describe('basic-querying', function() { }); it('should support date "lt" that is satisfied', function(done) { - User.find({order: 'seq', where: {birthday: {'lt': new Date('1980-12-07')}, + User.find({where: {birthday: {'lt': new Date('1980-12-07')}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -337,17 +350,18 @@ describe('basic-querying', function() { }); it('should support number "gte" that is satisfied', function(done) { - User.find({order: 'seq', where: {order: {'gte': 3}, - }}, function(err, users) { + User.find({where: {order: {'gte': 3}}}, function(err, users) { should.not.exist(err); users.should.have.property('length', 4); - users[0].name.should.equal('George Harrison'); + users.map(u => u.name).should.containDeep([ + 'George Harrison', 'Ringo Starr', 'Pete Best', 'Stuart Sutcliffe', + ]); done(); }); }); it('should support number "gt" that is not satisfied', function(done) { - User.find({order: 'seq', where: {order: {'gt': 6}, + User.find({where: {order: {'gt': 6}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 0); @@ -356,7 +370,7 @@ describe('basic-querying', function() { }); it('should support number "gt" that is satisfied', function(done) { - User.find({order: 'seq', where: {order: {'gt': 5}, + User.find({where: {order: {'gt': 5}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -366,7 +380,7 @@ describe('basic-querying', function() { }); it('should support number "lt" that is satisfied', function(done) { - User.find({order: 'seq', where: {order: {'lt': 2}, + User.find({where: {order: {'lt': 2}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 1); @@ -375,27 +389,27 @@ describe('basic-querying', function() { }); }); - it('should support number "gt" that is satisfied by null value', function(done) { - User.find({order: 'seq', where: {order: {'gt': null}, - }}, function(err, users) { + bdd.itIf(connectorCapabilities.ignoreUndefinedConditionValue !== false, 'should support number "gt" ' + + 'that is satisfied by null value', function(done) { + User.find({order: 'seq', where: {order: {'gt': null}}}, function(err, users) { should.not.exist(err); users.should.have.property('length', 0); done(); }); }); - it('should support number "lt" that is not satisfied by null value', function(done) { - User.find({order: 'seq', where: {order: {'lt': null}, - }}, function(err, users) { + bdd.itIf(connectorCapabilities.ignoreUndefinedConditionValue !== false, 'should support number "lt" ' + + 'that is not satisfied by null value', function(done) { + User.find({where: {order: {'lt': null}}}, function(err, users) { should.not.exist(err); users.should.have.property('length', 0); done(); }); }); - it('should support string "gte" that is satisfied by null value', function(done) { - User.find({order: 'seq', where: {name: {'gte': null}, - }}, function(err, users) { + bdd.itIf(connectorCapabilities.ignoreUndefinedConditionValue !== false, 'should support string "gte" ' + + 'that is satisfied by null value', function(done) { + User.find({order: 'seq', where: {name: {'gte': null}}}, function(err, users) { should.not.exist(err); users.should.have.property('length', 0); done(); @@ -403,17 +417,18 @@ describe('basic-querying', function() { }); it('should support string "gte" that is satisfied', function(done) { - User.find({order: 'seq', where: {name: {'gte': 'Paul McCartney'}, - }}, function(err, users) { + User.find({where: {name: {'gte': 'Paul McCartney'}}}, function(err, users) { should.not.exist(err); users.should.have.property('length', 4); - users[0].name.should.equal('Paul McCartney'); + for (var ix = 0; ix < users.length; ix++) { + users[ix].name.should.be.greaterThanOrEqual('Paul McCartney'); + } done(); }); }); it('should support string "gt" that is not satisfied', function(done) { - User.find({order: 'seq', where: {name: {'gt': 'xyz'}, + User.find({where: {name: {'gt': 'xyz'}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 0); @@ -422,37 +437,44 @@ describe('basic-querying', function() { }); it('should support string "gt" that is satisfied', function(done) { - User.find({order: 'seq', where: {name: {'gt': 'Paul McCartney'}, + User.find({where: {name: {'gt': 'Paul McCartney'}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 3); - users[0].name.should.equal('Ringo Starr'); + for (var ix = 0; ix < users.length; ix++) { + users[ix].name.should.be.greaterThan('Paul McCartney'); + } done(); }); }); it('should support string "lt" that is satisfied', function(done) { - User.find({order: 'seq', where: {name: {'lt': 'Paul McCartney'}, + User.find({where: {name: {'lt': 'Paul McCartney'}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 2); - users[0].name.should.equal('John Lennon'); + for (var ix = 0; ix < users.length; ix++) { + users[ix].name.should.be.lessThan('Paul McCartney'); + } done(); }); }); it('should support boolean "gte" that is satisfied', function(done) { - User.find({order: 'seq', where: {vip: {'gte': true}, + User.find({where: {vip: {'gte': true}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 3); - users[0].name.should.equal('John Lennon'); + for (var ix = 0; ix < users.length; ix++) { + users[ix].name.should.be.oneOf(['John Lennon', 'Stuart Sutcliffe', 'Paul McCartney']); + users[ix].vip.should.be.true(); + }; done(); }); }); it('should support boolean "gt" that is not satisfied', function(done) { - User.find({order: 'seq', where: {vip: {'gt': true}, + User.find({where: {vip: {'gt': true}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 0); @@ -461,35 +483,41 @@ describe('basic-querying', function() { }); it('should support boolean "gt" that is satisfied', function(done) { - User.find({order: 'seq', where: {vip: {'gt': false}, + User.find({where: {vip: {'gt': false}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 3); - users[0].name.should.equal('John Lennon'); + for (var ix = 0; ix < users.length; ix++) { + users[ix].name.should.be.oneOf(['John Lennon', 'Stuart Sutcliffe', 'Paul McCartney']); + users[ix].vip.should.be.true(users[ix].name + ' should be VIP'); + } done(); }); }); it('should support boolean "lt" that is satisfied', function(done) { - User.find({order: 'seq', where: {vip: {'lt': true}, + User.find({where: {vip: {'lt': true}, }}, function(err, users) { should.not.exist(err); users.should.have.property('length', 2); - users[0].name.should.equal('George Harrison'); + for (var ix = 0; ix < users.length; ix++) { + users[ix].name.should.be.oneOf(['Ringo Starr', 'George Harrison']); + users[ix].vip.should.be.false(users[ix].name + ' should not be VIP'); + } done(); }); }); - it('supports non-empty inq', function() { + bdd.itIf(connectorCapabilities.supportInq, 'supports non-empty inq', function() { // note there is no record with seq=100 return User.find({where: {seq: {inq: [0, 1, 100]}}}) .then(result => { const seqsFound = result.map(r => r.seq); - should(seqsFound).be.oneOf([0, 1], [1, 0]); + should(seqsFound).eql([0, 1]); }); }); - it('supports empty inq', function() { + bdd.itIf(connectorCapabilities.supportInq, 'supports empty inq', function() { return User.find({where: {seq: {inq: []}}}) .then(result => { const seqsFound = result.map(r => r.seq); @@ -723,7 +751,7 @@ describe('basic-querying', function() { }); }); - it('should find first record', function(done) { + bdd.itIf(connectorCapabilities.adhocSort, 'should find first record', function(done) { User.findOne({order: 'order'}, function(e, u) { should.not.exist(e); should.exist(u); @@ -733,7 +761,7 @@ describe('basic-querying', function() { }); }); - it('should find last record', function(done) { + bdd.itIf(connectorCapabilities.adhocSort, 'should find last record', function(done) { User.findOne({order: 'order DESC'}, function(e, u) { should.not.exist(e); should.exist(u); @@ -743,7 +771,7 @@ describe('basic-querying', function() { }); }); - it('should find last record in filtered set', function(done) { + bdd.itIf(connectorCapabilities.adhocSort, 'should find last record in filtered set', function(done) { User.findOne({ where: {role: 'lead'}, order: 'order DESC', @@ -782,8 +810,9 @@ describe('basic-querying', function() { }); it('should check whether record not exist', function(done) { + var unknownId = uid.fromConnector(db) || 42; User.destroyAll(function() { - User.exists(42, function(err, exists) { + User.exists(unknownId, function(err, exists) { should.not.exist(err); exists.should.not.be.ok; done(); @@ -815,7 +844,7 @@ describe.skip('queries', function() { var Todo; before(function prepDb(done) { - var db = getSchema(); + db = getSchema(); Todo = db.define('Todo', { id: false, content: {type: 'string'}, @@ -825,6 +854,7 @@ describe.skip('queries', function() { db.automigrate(['Todo'], done); }); beforeEach(function resetFixtures(done) { + db = getSchema(); Todo.destroyAll(function() { Todo.create([ {content: 'Buy eggs'}, diff --git a/test/datatype.test.js b/test/datatype.test.js index ae8f422d..634a16c0 100644 --- a/test/datatype.test.js +++ b/test/datatype.test.js @@ -15,8 +15,7 @@ describe('datatypes', function() { before(function(done) { db = getSchema(); var Nested = db.define('Nested', {}); - - Model = db.define('Model', { + var modelTableSchema = { str: String, date: Date, num: Number, @@ -24,7 +23,8 @@ describe('datatypes', function() { list: {type: [String]}, arr: Array, nested: Nested, - }); + }; + Model = db.define('Model', modelTableSchema); db.automigrate(['Model'], done); }); @@ -136,10 +136,9 @@ describe('datatypes', function() { function testUpdate(done) { Model.findById(id, function(err, m) { should.not.exist(err); - // update using updateAttributes m.updateAttributes({ - id: m.id, num: '10', + id: m.id, num: 10, }, function(err, m) { should.not.exist(err); m.num.should.be.type('number'); diff --git a/test/helpers/uid-generator.js b/test/helpers/uid-generator.js index 393bcaa1..56615f97 100644 --- a/test/helpers/uid-generator.js +++ b/test/helpers/uid-generator.js @@ -19,3 +19,8 @@ exports.last = function() { exports.reset = function() { lastId = 0; }; + +exports.fromConnector = function(db) { + return (db && db.connector && db.connector.generateUniqueId) ? + db.connector.generateUniqueId() : null; +}; diff --git a/test/manipulation.test.js b/test/manipulation.test.js index 36671ad3..6c4b1d02 100644 --- a/test/manipulation.test.js +++ b/test/manipulation.test.js @@ -6,13 +6,14 @@ // This test written in mocha+should.js 'use strict'; -/* global getSchema:false */ +/* global getSchema:false, connectorCapabilities:false */ var async = require('async'); +var bdd = require('./helpers/bdd-if'); var should = require('./init.js'); +var uid = require('./helpers/uid-generator'); var db, Person; var ValidationError = require('..').ValidationError; -var bdd = require('./helpers/bdd-if.js'); var UUID_REGEXP = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; @@ -82,9 +83,9 @@ describe('manipulation', function() { it('should create instance', function(done) { Person.create({name: 'Anatoliy'}, function(err, p) { - p.name.should.equal('Anatoliy'); if (err) return done(err); should.exist(p); + p.name.should.equal('Anatoliy'); Person.findById(p.id, function(err, person) { if (err) return done(err); person.id.should.eql(p.id); @@ -313,7 +314,7 @@ describe('manipulation', function() { Person.findById(created.id, function(err, found) { if (err) return done(err); var result = found.toObject(); - result.should.have.properties({ + result.should.containEql({ id: created.id, name: 'a-name', }); @@ -324,7 +325,8 @@ describe('manipulation', function() { }); }); - it('should refuse to create object with duplicate id', function(done) { + bdd.itIf(connectorCapabilities.refuseDuplicateInsert !== false, 'should refuse to create ' + + 'object with duplicate id', function(done) { // NOTE(bajtos) We cannot reuse Person model here, // `settings.forceId` aborts the CREATE request at the validation step. var Product = db.define('ProductTest', {name: String}, {forceId: false}); @@ -333,7 +335,7 @@ describe('manipulation', function() { Product.create({name: 'a-name'}, function(err, p) { if (err) return done(err); - Product.create({id: p.id, name: 'duplicate'}, function(err) { + Product.create({id: p.id, name: 'duplicate'}, function(err, result) { if (!err) { return done(new Error('Create should have rejected duplicate id.')); } @@ -348,6 +350,7 @@ describe('manipulation', function() { describe('save', function() { it('should save new object', function(done) { var p = new Person; + should.not.exist(p.id); p.save(function(err) { if (err) return done(err); should.exist(p.id); @@ -357,6 +360,7 @@ describe('manipulation', function() { it('should save new object (promise variant)', function(done) { var p = new Person; + should.not.exist(p.id); p.save() .then(function() { should.exist(p.id); @@ -459,12 +463,15 @@ describe('manipulation', function() { // "User.hasPassword() should match a password after it is changed" StubUser.create({password: 'foo'}, function(err, created) { if (err) return done(err); + created.password.should.equal('foo-FOO'); created.password = 'bar'; created.save(function(err, saved) { if (err) return done(err); + created.id.should.eql(saved.id); saved.password.should.equal('bar-BAR'); StubUser.findById(created.id, function(err, found) { if (err) return done(err); + created.id.should.eql(found.id); found.password.should.equal('bar-BAR'); done(); }); @@ -477,7 +484,8 @@ describe('manipulation', function() { var person; before(function(done) { - Person.destroyAll(function() { + Person.destroyAll(function(err) { + if (err) return done(err); Person.create({name: 'Mary', age: 15}, function(err, p) { if (err) return done(err); person = p; @@ -486,11 +494,6 @@ describe('manipulation', function() { }); }); - it('has an alias "patchAttributes"', function(done) { - person.updateAttributes.should.equal(person.patchAttributes); - done(); - }); - it('should have updated password hashed with updateAttribute', function(done) { StubUser.create({password: 'foo'}, function(err, created) { @@ -553,6 +556,7 @@ describe('manipulation', function() { function(err, p) { // if uknownVar was defined, it would return validationError if (err) return done(err); + person.id.should.eql(p.id); Person.findById(p.id, function(e, p) { if (e) return done(e); p.name.should.equal('John'); @@ -692,6 +696,11 @@ describe('manipulation', function() { }); }); + it('has an alias "patchAttributes"', function(done) { + person.updateAttributes.should.equal(person.patchAttributes); + done(); + }); + it('should allow model instance on updateAttributes', function(done) { person.updateAttributes(new Person({'name': 'John', age: undefined}), function(err, p) { @@ -775,7 +784,7 @@ describe('manipulation', function() { should.exist(data); should.exist(data.id); - data.id.should.equal(todo.id); + data.id.should.eql(todo.id); should.exist(data.content); data.content.should.equal('b'); @@ -827,22 +836,20 @@ describe('manipulation', function() { {name: 'a-name', gender: undefined}, function(err, instance) { if (err) return done(err); - instance.toObject().should.have.properties({ - id: instance.id, - name: 'a-name', - gender: undefined, - }); + var result = instance.toObject(); + result.id.should.eql(instance.id); + should.equal(result.name, 'a-name'); + should.equal(result.gender, undefined); Person.updateOrCreate( {id: instance.id, name: 'updated name'}, function(err, updated) { if (err) return done(err); var result = updated.toObject(); - result.should.have.properties({ - id: instance.id, - name: 'updated name', - }); + result.id.should.eql(instance.id); + should.equal(result.name, 'updated name'); should.equal(result.gender, null); + done(); }); }); @@ -880,8 +887,9 @@ describe('manipulation', function() { }); it('should allow save() of the created instance', function(done) { + var unknownId = uid.fromConnector(db) || 999; Person.updateOrCreate( - {id: 999 /* a new id */, name: 'a-name'}, + {id: unknownId, name: 'a-name'}, function(err, inst) { if (err) return done(err); inst.save(done); @@ -893,30 +901,31 @@ describe('manipulation', function() { describe.skip('replaceById - not implemented', function() {}); } else { describe('replaceOrCreate', function() { - var Post; - var ds = getSchema(); + var Post, unknownId; before(function(done) { - Post = ds.define('Post', { + db = getSchema(); + unknownId = uid.fromConnector(db) || 123; + Post = db.define('Post', { title: {type: String, length: 255, index: true}, content: {type: String}, comments: [String], }, {forceId: false}); - ds.automigrate('Post', done); + db.automigrate('Post', done); }); it('works without options on create (promise variant)', function(done) { - var post = {id: 123, title: 'a', content: 'AAA'}; + var post = {id: unknownId, title: 'a', content: 'AAA'}; Post.replaceOrCreate(post) .then(function(p) { should.exist(p); p.should.be.instanceOf(Post); - p.id.should.be.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal(post.title); p.content.should.equal(post.content); return Post.findById(p.id) .then(function(p) { - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.id.should.not.have.property('_id'); p.title.should.equal(p.title); p.content.should.equal(p.content); @@ -927,18 +936,18 @@ describe('manipulation', function() { }); it('works with options on create (promise variant)', function(done) { - var post = {id: 123, title: 'a', content: 'AAA'}; + var post = {id: unknownId, title: 'a', content: 'AAA'}; Post.replaceOrCreate(post, {validate: false}) .then(function(p) { should.exist(p); p.should.be.instanceOf(Post); - p.id.should.be.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal(post.title); p.content.should.equal(post.content); return Post.findById(p.id) .then(function(p) { - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.id.should.not.have.property('_id'); p.title.should.equal(p.title); p.content.should.equal(p.content); @@ -960,11 +969,12 @@ describe('manipulation', function() { .then(function(p) { should.exist(p); p.should.be.instanceOf(Post); - p.id.should.equal(created.id); + p.id.should.eql(created.id); p.should.not.have.property('_id'); p.title.should.equal('b'); p.should.have.property('content', undefined); p.should.have.property('comments', undefined); + return Post.findById(created.id) .then(function(p) { p.should.not.have.property('_id'); @@ -990,11 +1000,12 @@ describe('manipulation', function() { .then(function(p) { should.exist(p); p.should.be.instanceOf(Post); - p.id.should.equal(created.id); + p.id.should.eql(created.id); p.should.not.have.property('_id'); p.title.should.equal('b'); p.should.have.property('content', undefined); p.should.have.property('comments', undefined); + return Post.findById(created.id) .then(function(p) { p.should.not.have.property('_id'); @@ -1018,11 +1029,12 @@ describe('manipulation', function() { post.title = 'b'; Post.replaceOrCreate(post, function(err, p) { if (err) return done(err); - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal('b'); p.should.have.property('content', undefined); p.should.have.property('comments', undefined); + Post.findById(post.id, function(err, p) { if (err) return done(err); p.id.should.eql(post.id); @@ -1047,11 +1059,12 @@ describe('manipulation', function() { post.title = 'b'; Post.replaceOrCreate(post, function(err, p) { if (err) return done(err); - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal('b'); p.should.have.property('content', undefined); p.should.have.property('comments', undefined); + Post.findById(post.id, function(err, p) { if (err) return done(err); p.id.should.eql(post.id); @@ -1066,16 +1079,17 @@ describe('manipulation', function() { }); it('works without options on create (callback variant)', function(done) { - var post = {id: 123, title: 'a', content: 'AAA'}; + var post = {id: unknownId, title: 'a', content: 'AAA'}; Post.replaceOrCreate(post, function(err, p) { if (err) return done(err); - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal(post.title); p.content.should.equal(post.content); + Post.findById(p.id, function(err, p) { if (err) return done(err); - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal(post.title); p.content.should.equal(post.content); @@ -1085,16 +1099,17 @@ describe('manipulation', function() { }); it('works with options on create (callback variant)', function(done) { - var post = {id: 123, title: 'a', content: 'AAA'}; + var post = {id: unknownId, title: 'a', content: 'AAA'}; Post.replaceOrCreate(post, {validate: false}, function(err, p) { if (err) return done(err); - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal(post.title); p.content.should.equal(post.content); + Post.findById(p.id, function(err, p) { if (err) return done(err); - p.id.should.equal(post.id); + p.id.should.eql(post.id); p.should.not.have.property('_id'); p.title.should.equal(post.title); p.content.should.equal(post.content); @@ -1106,19 +1121,23 @@ describe('manipulation', function() { } var hasReplaceById = !!getSchema().connector.replaceById; - bdd.describeIf(hasReplaceById, 'replaceOrCreate when forceId is true', function() { - var Post; + + bdd.describeIf(hasReplaceById && connectorCapabilities.supportForceId !== false, 'replaceOrCreate ' + + 'when forceId is true', function() { + var Post, unknownId; before(function(done) { - var ds = getSchema(); - Post = ds.define('Post', { + db = getSchema(); + unknownId = uid.fromConnector(db) || 123; + Post = db.define('Post', { title: {type: String, length: 255}, content: {type: String}, }, {forceId: true}); - ds.automigrate('Post', done); + db.automigrate('Post', done); }); it('fails when id does not exist in db', function(done) { - var post = {id: 123, title: 'a', content: 'AAA'}; + var post = {id: unknownId, title: 'a', content: 'AAA'}; + Post.replaceOrCreate(post, function(err, p) { err.statusCode.should.equal(404); done(); @@ -1137,17 +1156,17 @@ describe('manipulation', function() { it('works on update if the request includes an existing id in db', function(done) { Post.create({title: 'a', content: 'AAA'}, - function(err, post) { - if (err) return done(err); - post = post.toObject(); - delete post.content; - post.title = 'b'; - Post.replaceOrCreate(post, function(err, p) { - if (err) return done(err); - p.id.should.equal(post.id); - done(); - }); - }); + function(err, post) { + if (err) return done(err); + post = post.toObject(); + delete post.content; + post.title = 'b'; + Post.replaceOrCreate(post, function(err, p) { + if (err) return done(err); + p.id.should.eql(post.id); + done(); + }); + }); }); }); @@ -1182,8 +1201,7 @@ describe('manipulation', function() { function(done) { StubUser.create({password: 'foo'}, function(err, created) { if (err) return done(err); - created.replaceAttributes({password: 'test'}, - function(err, created) { + created.replaceAttributes({password: 'test'}, function(err, created) { if (err) return done(err); created.password.should.equal('test-TEST'); StubUser.findById(created.id, function(err, found) { @@ -1201,8 +1219,8 @@ describe('manipulation', function() { if (err) return done(err); changePostIdInHook('before save'); p.replaceAttributes({title: 'b'}, function(err, data) { - data.id.should.eql(postInstance.id); if (err) return done(err); + data.id.should.eql(postInstance.id); Post.find(function(err, p) { if (err) return done(err); p[0].id.should.eql(postInstance.id); @@ -1303,11 +1321,13 @@ describe('manipulation', function() { }); it('should fail when changing id', function(done) { + var unknownId = uid.fromConnector(db) || 999; Post.findById(postInstance.id, function(err, p) { if (err) return done(err); - p.replaceAttributes({title: 'b', id: 999}, function(err, p) { + p.replaceAttributes({title: 'b', id: unknownId}, function(err, p) { should.exist(err); - var expectedErrMsg = 'id property (id) cannot be updated from ' + postInstance.id + ' to 999'; + var expectedErrMsg = 'id property (id) cannot be updated from ' + + postInstance.id + ' to ' + unknownId; err.message.should.equal(expectedErrMsg); done(); }); @@ -1351,16 +1371,18 @@ describe('manipulation', function() { bdd.describeIf(hasReplaceById, 'replaceById', function() { var Post; before(function(done) { - var ds = getSchema(); - Post = ds.define('Post', { + db = getSchema(); + Post = db.define('Post', { title: {type: String, length: 255}, content: {type: String}, }, {forceId: true}); - ds.automigrate('Post', done); + db.automigrate('Post', done); }); - it('fails when id does not exist in db using replaceById', function(done) { - var post = {id: 123, title: 'a', content: 'AAA'}; + bdd.itIf(connectorCapabilities.supportForceId !== false, 'fails when id does not exist in db ' + + 'using replaceById', function(done) { + var unknownId = uid.fromConnector(db) || 123; + var post = {id: unknownId, title: 'a', content: 'AAA'}; Post.replaceById(post.id, post, function(err, p) { err.statusCode.should.equal(404); done(); @@ -1501,7 +1523,8 @@ describe('manipulation', function() { it('should destroy filtered set of records'); }); - describe('deleteAll/destroyAll', function() { + bdd.describeIf(connectorCapabilities.reportDeletedCount !== false && + connectorCapabilities.deleteWithOtherThanId !== false, 'deleteAll/destroyAll', function() { beforeEach(function clearOldData(done) { Person.deleteAll(done); }); @@ -1511,7 +1534,10 @@ describe('manipulation', function() { name: 'John', }, { name: 'Jane', - }], done); + }], function(err, data) { + should.not.exist(err); + done(); + }); }); it('should be defined as function', function() { @@ -1563,6 +1589,81 @@ describe('manipulation', function() { }); }); + bdd.describeIf(connectorCapabilities.reportDeletedCount === false && + connectorCapabilities.deleteWithOtherThanId === false, 'deleteAll/destroyAll case 2', function() { + var idJohn, idJane; + beforeEach(function clearOldData(done) { + Person.deleteAll(done); + }); + + beforeEach(function createTestData(done) { + Person.create([{ + name: 'John', + }, { + name: 'Jane', + }], function(err, data) { + should.not.exist(err); + data.forEach(function(person) { + if (person.name === 'John') idJohn = person.id; + if (person.name === 'Jane') idJane = person.id; + }); + should.exist(idJohn); + should.exist(idJane); + done(); + }); + }); + + it('should be defined as function', function() { + Person.deleteAll.should.be.a.Function; + Person.destroyAll.should.be.a.Function; + }); + + it('should only delete instances that satisfy the where condition', + function(done) { + Person.deleteAll({id: idJohn}, function(err, info) { + if (err) return done(err); + should.not.exist(info.count); + Person.find({where: {name: 'John'}}, function(err, data) { + if (err) return done(err); + should.not.exist(data.count); + data.should.have.length(0); + Person.find({where: {name: 'Jane'}}, function(err, data) { + if (err) return done(err); + data.should.have.length(1); + done(); + }); + }); + }); + }); + + it('should report zero deleted instances when no matches are found', + function(done) { + var unknownId = uid.fromConnector(db) || 1234567890; + Person.deleteAll({id: unknownId}, function(err, info) { + if (err) return done(err); + should.not.exist(info.count); + Person.count(function(err, count) { + if (err) return done(err); + count.should.equal(2); + done(); + }); + }); + }); + + it('should delete all instances when the where condition is not provided', + function(done) { + Person.deleteAll(function(err, info) { + if (err) return done(err); + should.not.exist(info.count); + Person.count(function(err, count) { + if (err) return done(err); + count.should.equal(0); + done(); + }); + }); + }); + }); + describe('deleteById', function() { beforeEach(givenSomePeople); afterEach(function() { @@ -1573,26 +1674,37 @@ describe('manipulation', function() { Person.findOne(function(e, p) { Person.deleteById(p.id, function(err, info) { if (err) return done(err); - info.should.have.property('count', 1); + if (connectorCapabilities.reportDeletedCount !== false) { + info.should.have.property('count', 1); + } else { + should.not.exist(info.count); + } done(); }); }); }); it('should allow deleteById(id) - fail', function(done) { + var unknownId = uid.fromConnector(db) || 9999; Person.settings.strictDelete = false; - Person.deleteById(9999, function(err, info) { + Person.deleteById(unknownId, function(err, info) { if (err) return done(err); - info.should.have.property('count', 0); + if (connectorCapabilities.reportDeletedCount !== false) { + info.should.have.property('count', 0); + } else { + should.not.exist(info.count); + } done(); }); }); it('should allow deleteById(id) - fail with error', function(done) { + var unknownId = uid.fromConnector(db) || 9999; + var errMsg = 'No instance with id ' + unknownId.toString() + ' found for Person'; Person.settings.strictDelete = true; - Person.deleteById(9999, function(err) { + Person.deleteById(unknownId, function(err) { should.exist(err); - err.message.should.equal('No instance with id 9999 found for Person'); + err.message.should.equal(errMsg); err.should.have.property('code', 'NOT_FOUND'); err.should.have.property('statusCode', 404); done(); @@ -1611,7 +1723,11 @@ describe('manipulation', function() { if (e) return done(e); p.delete(function(err, info) { if (err) return done(err); - info.should.have.property('count', 1); + if (connectorCapabilities.reportDeletedCount !== false) { + info.should.have.property('count', 1); + } else { + should.not.exist(info.count); + } done(); }); }); @@ -1623,17 +1739,26 @@ describe('manipulation', function() { if (e) return done(e); p.delete(function(err, info) { if (err) return done(err); - info.should.have.property('count', 1); + if (connectorCapabilities.reportDeletedCount !== false) { + info.should.have.property('count', 1); + } else { + should.not.exist(info.count); + } p.delete(function(err, info) { if (err) return done(err); - info.should.have.property('count', 0); + if (connectorCapabilities.reportDeletedCount !== false) { + info.should.have.property('count', 0); + } else { + should.not.exist(info.count); + } done(); }); }); }); }); - it('should allow delete(id) - fail with error', function(done) { + bdd.itIf(connectorCapabilities.supportStrictDelete !== false, 'should allow delete(id) - ' + + 'fail with error', function(done) { Person.settings.strictDelete = true; Person.findOne(function(err, u) { if (err) return done(err); @@ -1888,7 +2013,11 @@ describe('manipulation', function() { }); describe('update/updateAll', function() { + var idBrett, idCarla, idDonna, idFrank, idGrace, idHarry; + var filterBrett, filterHarry; + beforeEach(function clearOldData(done) { + db = getSchema(); Person.destroyAll(done); }); @@ -1908,7 +2037,22 @@ describe('manipulation', function() { }, { name: 'Grace Goe', age: 23, - }], done); + }], function(err, data) { + should.not.exist(err); + data.forEach(function(person) { + if (person.name === 'Brett Boe') idBrett = person.id; + if (person.name === 'Carla Coe') idCarla = person.id; + if (person.name === 'Donna Doe') idDonna = person.id; + if (person.name === 'Frank Foe') idFrank = person.id; + if (person.name === 'Grace Goe') idGrace = person.id; + }); + should.exist(idBrett); + should.exist(idCarla); + should.exist(idDonna); + should.exist(idFrank); + should.exist(idGrace); + done(); + }); }); it('should be defined as a function', function() { @@ -1918,10 +2062,17 @@ describe('manipulation', function() { it('should not update instances that do not satisfy the where condition', function(done) { - Person.update({name: 'Harry Hoe'}, {name: 'Marta Moe'}, function(err, + idHarry = uid.fromConnector(db) || undefined; + var filter = connectorCapabilities.updateWithOtherThanId === false ? + {id: idHarry} : {name: 'Harry Hoe'}; + Person.update(filter, {name: 'Marta Moe'}, function(err, info) { if (err) return done(err); - info.should.have.property('count', 0); + if (connectorCapabilities.reportDeletedCount !== false) { + info.should.have.property('count', 0); + } else { + should.not.exist(info.count); + } Person.find({where: {name: 'Harry Hoe'}}, function(err, people) { if (err) return done(err); people.should.be.empty; @@ -1932,10 +2083,16 @@ describe('manipulation', function() { it('should only update instances that satisfy the where condition', function(done) { - Person.update({name: 'Brett Boe'}, {name: 'Harry Hoe'}, function(err, + var filter = connectorCapabilities.deleteWithOtherThanId === false ? + {id: idBrett} : {name: 'Brett Boe'}; + Person.update(filter, {name: 'Harry Hoe'}, function(err, info) { if (err) return done(err); - info.should.have.property('count', 1); + if (connectorCapabilities.reportDeletedCount !== false) { + info.should.have.property('count', 1); + } else { + should.not.exist(info.count); + } Person.find({where: {age: 19}}, function(err, people) { if (err) return done(err); people.should.have.length(1); @@ -1945,46 +2102,49 @@ describe('manipulation', function() { }); }); - it('should update all instances when the where condition is not provided', - function(done) { - Person.update({name: 'Harry Hoe'}, function(err, info) { + bdd.itIf(connectorCapabilities.updateWithoutId !== false, 'should update all instances when ' + + 'the where condition is not provided', function(done) { + filterHarry = connectorCapabilities.deleteWithOtherThanId === false ? + {id: idHarry} : {name: 'Harry Hoe'}; + filterBrett = connectorCapabilities.deleteWithOtherThanId === false ? + {id: idBrett} : {name: 'Brett Boe'}; + Person.update(filterHarry, function(err, info) { + if (err) return done(err); + info.should.have.property('count', 5); + Person.find({where: filterBrett}, function(err, people) { + if (err) return done(err); + people.should.be.empty(); + Person.find({where: filterHarry}, function(err, people) { if (err) return done(err); - info.should.have.property('count', 5); - Person.find({where: {name: 'Brett Boe'}}, function(err, people) { - if (err) return done(err); - people.should.be.empty; - Person.find({where: {name: 'Harry Hoe'}}, function(err, people) { - if (err) return done(err); - people.should.have.length(5); - done(); - }); - }); - }); - }); - - it('should ignore where conditions with undefined values', - function(done) { - Person.update({name: 'Brett Boe'}, {name: undefined, gender: 'male'}, - function(err, info) { - if (err) return done(err); - info.should.have.property('count', 1); - Person.find({where: {name: 'Brett Boe'}}, function(err, people) { - if (err) return done(err); - people.should.have.length(1); - people[0].name.should.equal('Brett Boe'); - done(); - }); - }); - }); - - it('should not coerce invalid values provided in where conditions', - function(done) { - Person.update({name: 'Brett Boe'}, {dob: 'Carla Coe'}, function(err) { - should.exist(err); - err.message.should.equal('Invalid date: Carla Coe'); + people.should.have.length(5); done(); }); }); + }); + }); + + bdd.itIf(connectorCapabilities.ignoreUndefinedConditionValue !== false, 'should ignore where ' + + 'conditions with undefined values', function(done) { + Person.update(filterBrett, {name: undefined, gender: 'male'}, + function(err, info) { + if (err) return done(err); + info.should.have.property('count', 1); + Person.find({where: filterBrett}, function(err, people) { + if (err) return done(err); + people.should.have.length(1); + people[0].name.should.equal('Brett Boe'); + done(); + }); + }); + }); + + it('should not coerce invalid values provided in where conditions', function(done) { + Person.update({name: 'Brett Boe'}, {dob: 'notadate'}, function(err) { + should.exist(err); + err.message.should.equal('Invalid date: notadate'); + done(); + }); + }); }); describe('upsertWithWhere', function() { @@ -2073,13 +2233,13 @@ describe('manipulation', function() { .then(function(p) { should.exist(p); p.should.be.instanceOf(Person); - p.id.should.be.equal(person.id); + p.id.should.eql(person.id); p.should.not.have.property('_id'); p.name.should.equal(person.name); p.city.should.equal(person.city); return Person.findById(p.id) .then(function(p) { - p.id.should.equal(person.id); + p.id.should.eql(person.id); p.id.should.not.have.property('_id'); p.name.should.equal(person.name); p.city.should.equal(person.city); @@ -2095,13 +2255,13 @@ describe('manipulation', function() { .then(function(p) { should.exist(p); p.should.be.instanceOf(Person); - p.id.should.be.equal(person.id); + p.id.should.eql(person.id); p.should.not.have.property('_id'); p.name.should.equal(person.name); p.city.should.equal(person.city); return Person.findById(p.id) .then(function(p) { - p.id.should.equal(person.id); + p.id.should.eql(person.id); p.id.should.not.have.property('_id'); p.name.should.equal(person.name); p.city.should.equal(person.city); @@ -2122,7 +2282,7 @@ describe('manipulation', function() { .then(function(p) { should.exist(p); p.should.be.instanceOf(Person); - p.id.should.equal(created.id); + p.id.should.eql(created.id); p.should.not.have.property('_id'); p.name.should.equal('BBB'); p.should.have.property('city', 'city AAA'); @@ -2149,7 +2309,7 @@ describe('manipulation', function() { .then(function(p) { should.exist(p); p.should.be.instanceOf(Person); - p.id.should.equal(created.id); + p.id.should.eql(created.id); p.should.not.have.property('_id'); p.name.should.equal('Carlton'); p.should.have.property('city', 'city CCC'); @@ -2217,7 +2377,7 @@ describe('manipulation', function() { if (err) return done(err); Person.findById(5, function(err, data) { if (err) return done(err); - data.id.should.equal(5); + should.equal(data.id, 5); data.name.should.equal('Brian'); data.city.should.equal('Kentucky'); done(); diff --git a/test/relations.test.js b/test/relations.test.js index b86e4183..c7967f53 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -6,9 +6,11 @@ // This test written in mocha+should.js 'use strict'; -/* global getSchema:false */ -var should = require('./init.js'); +/* global getSchema:false, connectorCapabilities:false */ var assert = require('assert'); +var bdd = require('./helpers/bdd-if'); +var should = require('./init.js'); +var uid = require('./helpers/uid-generator'); var jdb = require('../'); var DataSource = jdb.DataSource; var createPromiseCallback = require('../lib/utils.js').createPromiseCallback; @@ -34,6 +36,7 @@ describe('relations', function() { describe('hasMany', function() { before(function(done) { + db = getSchema(); Book = db.define('Book', {name: String, type: String}); Chapter = db.define('Chapter', {name: {type: String, index: true}, bookType: String}); @@ -58,6 +61,8 @@ describe('relations', function() { }); it('can be declared in short form', function(done) { + Author = db.define('Author', {name: String}); + Reader = db.define('Reader', {name: String}); Author.hasMany('readers'); (new Author).readers.should.be.an.instanceOf(Function); Object.keys((new Reader).toObject()).should.containEql('authorId'); @@ -73,7 +78,8 @@ describe('relations', function() { it('should build record on scope', function(done) { Book.create(function(err, book) { - var c = book.chapters.build(); + var chaps = book.chapters; + var c = chaps.build(); c.bookId.should.eql(book.id); c.save(done); }); @@ -82,10 +88,10 @@ describe('relations', function() { it('should create record on scope', function(done) { Book.create(function(err, book) { book.chapters.create(function(err, c) { - should.not.exist(err); + if (err) return done(err); should.exist(c); c.bookId.should.eql(book.id); - done(); + done(err); }); }); }); @@ -110,7 +116,7 @@ describe('relations', function() { ]; Book.create(function(err, book) { book.chapters.create(chapters, function(err, chs) { - should.not.exist(err); + if (err) return done(err); should.exist(chs); chs.should.have.lengthOf(chapters.length); chs.forEach(function(c) { @@ -152,19 +158,21 @@ describe('relations', function() { }); function verify(book) { book.chapters(function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(3); var chapters = book.chapters(); chapters.should.eql(ch); - book.chapters({order: 'name DESC'}, function(e, c) { + book.chapters(function(e, c) { should.not.exist(e); should.exist(c); - - c.shift().name.should.equal('z'); - c.pop().name.should.equal('a'); + ch.should.have.lengthOf(3); + var acz = ['a', 'c', 'z']; + acz.should.containEql(c[0].name); + acz.should.containEql(c[1].name); + acz.should.containEql(c[2].name); done(); }); }); @@ -191,16 +199,16 @@ describe('relations', function() { .then(function(ch) { should.exist(ch); ch.should.have.lengthOf(3); - var chapters = book.chapters(); chapters.should.eql(ch); - - return book.chapters.getAsync({order: 'name DESC'}) + return book.chapters.getAsync() .then(function(c) { should.exist(c); - - c.shift().name.should.equal('z'); - c.pop().name.should.equal('a'); + ch.should.have.lengthOf(3); + var acz = ['a', 'c', 'z']; + acz.should.containEql(c[0].name); + acz.should.containEql(c[1].name); + acz.should.containEql(c[2].name); done(); }); }); @@ -219,19 +227,20 @@ describe('relations', function() { }); function verify(book) { book.chapters(function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(3); var chapters = book.chapters(); chapters.should.eql(ch); - - book.chapters.getAsync({order: 'name DESC'}, function(e, c) { + book.chapters.getAsync(function(e, c) { should.not.exist(e); should.exist(c); - - c.shift().name.should.equal('z'); - c.pop().name.should.equal('a'); + ch.should.have.lengthOf(3); + var acz = ['a', 'c', 'z']; + acz.should.containEql(c[0].name); + acz.should.containEql(c[1].name); + acz.should.containEql(c[2].name); done(); }); }); @@ -250,7 +259,7 @@ describe('relations', function() { }); function verify(book) { book.chapters(function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(3); @@ -261,8 +270,11 @@ describe('relations', function() { should.not.exist(e); should.exist(c); should.exist(c.length); - c.shift().name.should.equal('a'); - c.pop().name.should.equal('c'); + c.should.have.lengthOf(3); + var acz = ['a', 'c', 'z']; + acz.should.containEql(c[0].name); + acz.should.containEql(c[1].name); + acz.should.containEql(c[2].name); done(); }); }); @@ -284,7 +296,7 @@ describe('relations', function() { function verify(book) { book.chapters.findById(id, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.id.should.eql(id); done(); @@ -332,10 +344,10 @@ describe('relations', function() { function verify(book) { book.chapters.count(function(err, count) { - should.not.exist(err); + if (err) return done(err); count.should.equal(3); book.chapters.count({name: 'b'}, function(err, count) { - should.not.exist(err); + if (err) return done(err); count.should.equal(1); done(); }); @@ -388,7 +400,7 @@ describe('relations', function() { function verify(book) { book.chapters.findById(id, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.id.should.eql(id); ch.name.should.equal('aa'); @@ -481,7 +493,7 @@ describe('relations', function() { function verify(book) { book.chapters.exists(id, function(err, flag) { - should.not.exist(err); + if (err) return done(err); flag.should.be.eql(true); done(); }); @@ -516,7 +528,7 @@ describe('relations', function() { it('should check ignore related data on creation - array', function(done) { Book.create({chapters: []}, function(err, book) { - should.not.exist(err); + if (err) return done(err); book.chapters.should.be.a.function; var obj = book.toObject(); should.not.exist(obj.chapters); @@ -536,7 +548,7 @@ describe('relations', function() { it('should check ignore related data on creation - object', function(done) { Book.create({chapters: {}}, function(err, book) { - should.not.exist(err); + if (err) return done(err); book.chapters.should.be.a.function; var obj = book.toObject(); should.not.exist(obj.chapters); @@ -560,7 +572,7 @@ describe('relations', function() { var Physician, Patient, Appointment, Address; before(function(done) { - // db = getSchema(); + db = getSchema(); Physician = db.define('Physician', {name: String}); Patient = db.define('Patient', {name: String, age: Number}); Appointment = db.define('Appointment', {date: {type: Date, @@ -578,7 +590,8 @@ describe('relations', function() { db.automigrate(['Physician', 'Patient', 'Appointment', 'Address'], done); }); - it('should build record on scope', function(done) { + bdd.itIf(connectorCapabilities.supportInclude !== false, + 'should build record on scope', function(done) { Physician.create(function(err, physician) { var patient = physician.patients.build(); patient.physicianId.should.eql(physician.id); @@ -589,11 +602,11 @@ describe('relations', function() { it('should create record on scope', function(done) { Physician.create(function(err, physician) { physician.patients.create(function(err, patient) { - should.not.exist(err); + if (err) return done(err); should.exist(patient); Appointment.find({where: {physicianId: physician.id, patientId: patient.id}}, function(err, apps) { - should.not.exist(err); + if (err) return done(err); apps.should.have.lengthOf(1); done(); }); @@ -620,7 +633,7 @@ describe('relations', function() { var async = require('async'); Physician.create(function(err, physician) { physician.patients.create([{}, {}], function(err, patients) { - should.not.exist(err); + if (err) return done(err); should.exist(patients); patients.should.have.lengthOf(2); function verifyPatient(patient, next) { @@ -629,7 +642,7 @@ describe('relations', function() { patientId: patient.id, }}, function(err, apps) { - should.not.exist(err); + if (err) return done(err); apps.should.have.lengthOf(1); next(); }); @@ -677,7 +690,7 @@ describe('relations', function() { var patients = physician.patients(); patients.should.eql(ch); - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(3); done(); @@ -703,7 +716,7 @@ describe('relations', function() { return physician.patients.getAsync() .then(function(ch) { var patients = physician.patients(); - patients.should.eql(ch); + should.equal(patients, ch); should.exist(ch); ch.should.have.lengthOf(3); @@ -719,9 +732,10 @@ describe('relations', function() { beforeEach(createSampleData); context('with filter skip', function() { - it('skips the first patient', function(done) { + bdd.itIf(connectorCapabilities.supportPagination !== false, + 'skips the first patient', function(done) { physician.patients({skip: 1}, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(2); ch[0].name.should.eql('z'); @@ -732,12 +746,21 @@ describe('relations', function() { }); context('with filter order', function() { it('orders the result by patient name', function(done) { - physician.patients({order: 'name DESC'}, function(err, ch) { - should.not.exist(err); + var filter = connectorCapabilities.adhocSort !== false ? {order: 'name DESC'} : {}; + physician.patients(filter, function(err, ch) { + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(3); - ch[0].name.should.eql('z'); - ch[2].name.should.eql('a'); + if (connectorCapabilities.adhocSort !== false) { + ch[0].name.should.eql('z'); + ch[1].name.should.eql('c'); + ch[2].name.should.eql('a'); + } else { + var acz = ['a', 'c', 'z']; + ch[0].name.should.be.oneOf(acz); + ch[1].name.should.be.oneOf(acz); + ch[2].name.should.be.oneOf(acz); + } done(); }); }); @@ -745,10 +768,14 @@ describe('relations', function() { context('with filter limit', function() { it('limits to 1 result', function(done) { physician.patients({limit: 1}, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(1); - ch[0].name.should.eql('a'); + if (connectorCapabilities.adhocSort !== false) { + ch[0].name.should.eql('a'); + } else { + ch[0].name.should.be.oneOf(['a', 'c', 'z']); + } done(); }); }); @@ -757,20 +784,25 @@ describe('relations', function() { it('includes field \'name\' but not \'age\'', function(done) { var fieldsFilter = {fields: {name: true, age: false}}; physician.patients(fieldsFilter, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); should.exist(ch[0].name); - ch[0].name.should.eql('a'); + if (connectorCapabilities.adhocSort !== false) { + ch[0].name.should.eql('a'); + } else { + ch[0].name.should.be.oneOf(['a', 'c', 'z']); + } should.not.exist(ch[0].age); done(); }); }); }); context('with filter include', function() { - it('returns physicians inluced in patient', function(done) { + bdd.itIf(connectorCapabilities.supportNonPrimaryKeyIN !== false, + 'returns physicians included in patient', function(done) { var includeFilter = {include: 'physicians'}; physician.patients(includeFilter, function(err, ch) { - should.not.exist(err); + if (err) return done(err); ch.should.have.lengthOf(3); should.exist(ch[0].physicians); done(); @@ -778,24 +810,26 @@ describe('relations', function() { }); }); context('with filter where', function() { - it('returns patient where id equal to samplePatientId', function(done) { + bdd.itIf(connectorCapabilities.supportNonPrimaryKeyIN !== false, + 'returns patient where id equal to samplePatientId', function(done) { var whereFilter = {where: {id: samplePatientId}}; physician.patients(whereFilter, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(1); ch[0].id.should.eql(samplePatientId); done(); }); }); - it('returns patients where id in an array', function(done) { + bdd.itIf(connectorCapabilities.supportNonPrimaryKeyIN !== false, + 'returns patients where id in an array', function(done) { var idArr = []; var whereFilter; physician.patients.create({name: 'b'}, function(err, p) { idArr.push(samplePatientId, p.id); whereFilter = {where: {id: {inq: idArr}}}; physician.patients(whereFilter, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(2); var resultIdArr = [ch[0].id, ch[1].id]; @@ -811,7 +845,7 @@ describe('relations', function() { var includeFilter = {include: 'physicians'}; physician.patients.findById(samplePatientId, includeFilter, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.id.should.eql(samplePatientId); should.exist(ch.physicians); @@ -825,7 +859,7 @@ describe('relations', function() { var fieldsFilter = {fields: {name: true, age: false}}; physician.patients.findById(samplePatientId, fieldsFilter, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); should.exist(ch.name); ch.name.should.eql('a'); @@ -834,13 +868,12 @@ describe('relations', function() { }); }); }); - context('findById with include filter that contains string fields', function() { it('should accept string and convert it to array', function(done) { var includeFilter = {include: {relation: 'patients', scope: {fields: 'name'}}}; var physicianId = physician.id; Physician.findById(physicianId, includeFilter, function(err, result) { - should.not.exist(err); + if (err) return done(err); should.exist(result); result.id.should.eql(physicianId); should.exist(result.patients); @@ -883,7 +916,7 @@ describe('relations', function() { function verify(physician) { physician.patients.findById(id, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.id.should.eql(id); done(); @@ -910,7 +943,7 @@ describe('relations', function() { function verify(physician) { return physician.patients.findById(id, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.id.should.eql(id); done(); @@ -922,7 +955,7 @@ describe('relations', function() { Physician.create(function(err, physician) { physician.patients.create({name: 'a'}, function(err, patient) { Address.create({name: 'z'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); patient.address(address); patient.save(function() { verify(physician, address.id); @@ -932,7 +965,7 @@ describe('relations', function() { }); function verify(physician, addressId) { physician.patients({include: 'address'}, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.should.have.lengthOf(1); ch[0].addressId.should.eql(addressId); @@ -966,7 +999,7 @@ describe('relations', function() { .then(function(ch) { should.exist(ch); ch.should.have.lengthOf(1); - ch[0].addressId.should.eql(addressId); + ch[0].addressId.toString().should.eql(addressId.toString()); var address = ch[0].address(); should.exist(address); address.should.be.an.instanceof(Address); @@ -993,7 +1026,7 @@ describe('relations', function() { function verify(physician) { physician.patients.findById(id, function(err, ch) { - should.not.exist(err); + if (err) return done(err); should.exist(ch); ch.id.should.eql(id); ch.name.should.equal('aa'); @@ -1027,7 +1060,8 @@ describe('relations', function() { } }); - it('should destroy scoped record', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should destroy scoped record', function(done) { var id; Physician.create(function(err, physician) { physician.patients.create({name: 'a'}, function(err, ch) { @@ -1046,7 +1080,8 @@ describe('relations', function() { } }); - it('should destroy scoped record with promises', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should destroy scoped record with promises', function(done) { var id; Physician.create() .then(function(physician) { @@ -1077,7 +1112,7 @@ describe('relations', function() { var id; Physician.create(function(err, physician) { physician.patients.create({name: 'a'}, function(err, ch) { - should.not.exist(err); + if (err) return done(err); id = ch.id; physician.patients.create({name: 'z'}, function() { physician.patients.create({name: 'c'}, function() { @@ -1089,7 +1124,7 @@ describe('relations', function() { function verify(physician) { physician.patients.exists(id, function(err, flag) { - should.not.exist(err); + if (err) return done(err); flag.should.be.eql(true); done(); }); @@ -1192,7 +1227,8 @@ describe('relations', function() { }).catch(done); }); - it('should allow to remove connection with instance', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should allow to remove connection with instance', function(done) { var id; Physician.create(function(err, physician) { physician.patients.create({name: 'a'}, function(err, patient) { @@ -1205,14 +1241,15 @@ describe('relations', function() { function verify(physician) { physician.patients.exists(id, function(err, flag) { - should.not.exist(err); + if (err) return done(err); flag.should.be.eql(false); done(); }); } }); - it('should allow to remove connection with instance with promises', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should allow to remove connection with instance with promises', function(done) { var id; Physician.create() .then(function(physician) { @@ -1246,9 +1283,12 @@ describe('relations', function() { describe('hasMany through - collect', function() { var Physician, Patient, Appointment, Address; + var idPatient, idPhysician; beforeEach(function(done) { - // db = getSchema(); + db = getSchema(); + idPatient = uid.fromConnector(db) || 1234; + idPhysician = uid.fromConnector(db) || 2345; Physician = db.define('Physician', {name: String}); Patient = db.define('Patient', {name: String}); Appointment = db.define('Appointment', {date: {type: Date, @@ -1267,11 +1307,11 @@ describe('relations', function() { Patient.belongsTo(Address); Appointment.belongsTo(Physician); Appointment.belongsTo(Patient); - var physician = new Physician({id: 1}); + var physician = new Physician({id: idPhysician}); var scope1 = physician.patients._scope; scope1.should.have.property('collect', 'patient'); scope1.should.have.property('include', 'patient'); - var patient = new Patient({id: 1}); + var patient = new Patient({id: idPatient}); var scope2 = patient.yyy._scope; scope2.should.have.property('collect', 'physician'); scope2.should.have.property('include', 'physician'); @@ -1291,11 +1331,11 @@ describe('relations', function() { Patient.belongsTo(Address); // jam. Appointment.belongsTo(Patient, {as: 'car'}); // jam. Should we complain in this case??? - var physician = new Physician({id: 1}); + var physician = new Physician({id: idPhysician}); var scope1 = physician.patients._scope; scope1.should.have.property('collect', 'bar'); scope1.should.have.property('include', 'bar'); - var patient = new Patient({id: 1}); + var patient = new Patient({id: idPatient}); var scope2 = patient.yyy._scope; scope2.should.have.property('collect', 'foo'); scope2.should.have.property('include', 'foo'); @@ -1308,11 +1348,11 @@ describe('relations', function() { Appointment.belongsTo(Patient, {as: 'bar', foreignKey: 'patientId'}); Patient.belongsTo(Address); // jam. - var physician = new Physician({id: 1}); + var physician = new Physician({id: idPhysician}); var scope1 = physician.patients._scope; scope1.should.have.property('collect', 'bar'); scope1.should.have.property('include', 'bar'); - var patient = new Patient({id: 1}); + var patient = new Patient({id: idPatient}); var scope2 = patient.yyy._scope; scope2.should.have.property('collect', 'foo'); scope2.should.have.property('include', 'foo'); @@ -1327,11 +1367,11 @@ describe('relations', function() { Appointment.belongsTo(Physician, {as: 'goo', foreignKey: 'physicianId'}); // jam. Should we complain in this case??? Appointment.belongsTo(Patient, {as: 'car', foreignKey: 'patientId'}); // jam. Should we complain in this case??? - var physician = new Physician({id: 1}); + var physician = new Physician({id: idPhysician}); var scope1 = physician.patients._scope; scope1.should.have.property('collect', 'bar'); scope1.should.have.property('include', 'bar'); - var patient = new Patient({id: 1}); + var patient = new Patient({id: idPatient}); var scope2 = patient.yyy._scope; scope2.should.have.property('collect', 'foo'); // first matched relation scope2.should.have.property('include', 'foo'); // first matched relation @@ -1340,6 +1380,7 @@ describe('relations', function() { describe('when custom reverse belongsTo name for one side only', function() { beforeEach(function() { + db = getSchema(); Physician.hasMany(Patient, {as: 'xxx', through: Appointment, foreignKey: 'fooId'}); Patient.hasMany(Physician, {as: 'yyy', through: Appointment, keyThrough: 'fooId'}); Appointment.belongsTo(Physician, {as: 'foo'}); @@ -1349,14 +1390,14 @@ describe('relations', function() { }); it('can determine the collect via model name', function() { - var physician = new Physician({id: 1}); + var physician = new Physician({id: idPhysician}); var scope1 = physician.xxx._scope; scope1.should.have.property('collect', 'patient'); scope1.should.have.property('include', 'patient'); }); it('can determine the collect via keyThrough', function() { - var patient = new Patient({id: 1}); + var patient = new Patient({id: idPatient}); var scope2 = patient.yyy._scope; scope2.should.have.property('collect', 'foo'); scope2.should.have.property('include', 'foo'); @@ -1368,7 +1409,7 @@ describe('relations', function() { var Physician, Patient, Appointment; beforeEach(function(done) { - // db = getSchema(); + db = getSchema(); Physician = db.define('Physician', {name: String}); Patient = db.define('Patient', {name: String}); Appointment = db.define('Appointment', {date: {type: Date, defaultFn: 'now'}}); @@ -1390,9 +1431,12 @@ describe('relations', function() { describe('hasMany through bi-directional relations on the same model', function() { var User, Follow, Address; + var idFollower, idFollowee; before(function(done) { - // db = getSchema(); + db = getSchema(); + idFollower = uid.fromConnector(db) || 3456; + idFollowee = uid.fromConnector(db) || 4567; User = db.define('User', {name: String}); Follow = db.define('Follow', {date: {type: Date, default: function() { @@ -1414,10 +1458,10 @@ describe('relations', function() { it('should set foreignKeys of through model correctly in first relation', function(done) { - var follower = new User({id: 1}); - var followee = new User({id: 2}); + var follower = new User({id: idFollower}); + var followee = new User({id: idFollowee}); followee.followers.add(follower, function(err, throughInst) { - should.not.exist(err); + if (err) return done(err); should.exist(throughInst); throughInst.followerId.should.eql(follower.id); throughInst.followeeId.should.eql(followee.id); @@ -1427,13 +1471,13 @@ describe('relations', function() { it('should set foreignKeys of through model correctly in second relation', function(done) { - var follower = new User({id: 3}); - var followee = new User({id: 4}); + var follower = new User({id: idFollower}); + var followee = new User({id: idFollowee}); follower.following.add(followee, function(err, throughInst) { - should.not.exist(err); + if (err) return done(err); should.exist(throughInst); - throughInst.followeeId.should.eql(followee.id); - throughInst.followerId.should.eql(follower.id); + throughInst.followeeId.toString().should.eql(followee.id.toString()); + throughInst.followerId.toString().should.eql(follower.id.toString()); done(); }); }); @@ -1441,9 +1485,12 @@ describe('relations', function() { describe('hasMany through - between same models', function() { var User, Follow, Address; + var idFollower, idFollowee; before(function(done) { - // db = getSchema(); + db = getSchema(); + idFollower = uid.fromConnector(db) || 3456; + idFollowee = uid.fromConnector(db) || 4567; User = db.define('User', {name: String}); Follow = db.define('Follow', {date: {type: Date, default: function() { @@ -1464,10 +1511,10 @@ describe('relations', function() { }); it('should set the keyThrough and the foreignKey', function(done) { - var user = new User({id: 1}); - var user2 = new User({id: 2}); + var user = new User({id: idFollower}); + var user2 = new User({id: idFollowee}); user.following.add(user2, function(err, f) { - should.not.exist(err); + if (err) return done(err); should.exist(f); f.followeeId.should.eql(user2.id); f.followerId.should.eql(user.id); @@ -1476,7 +1523,7 @@ describe('relations', function() { }); it('can determine the collect via keyThrough for each side', function() { - var user = new User({id: 1}); + var user = new User({id: idFollower}); var scope1 = user.followers._scope; scope1.should.have.property('collect', 'follower'); scope1.should.have.property('include', 'follower'); @@ -1487,7 +1534,11 @@ describe('relations', function() { }); describe('hasMany with properties', function() { - it('can be declared with properties', function(done) { + before(function(done) { + db = getSchema(); + Book = db.define('Book', {name: String, type: String}); + Chapter = db.define('Chapter', {name: {type: String, index: true}, + bookType: String}); Book.hasMany(Chapter, {properties: {type: 'bookType'}}); db.automigrate(['Book', 'Chapter'], done); }); @@ -1495,7 +1546,7 @@ describe('relations', function() { it('should create record on scope', function(done) { Book.create({type: 'fiction'}, function(err, book) { book.chapters.create(function(err, c) { - should.not.exist(err); + if (err) return done(err); should.exist(c); c.bookId.should.eql(book.id); c.bookType.should.equal('fiction'); @@ -1520,7 +1571,7 @@ describe('relations', function() { describe('hasMany with scope and properties', function() { it('can be declared with properties', function(done) { - // db = getSchema(); + db = getSchema(); Category = db.define('Category', {name: String, jobType: String}); Job = db.define('Job', {name: String, type: String}); @@ -1689,7 +1740,8 @@ describe('relations', function() { }); }); - it('should delete records on scope - scoped', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should delete records on scope - scoped', function(done) { Category.findOne(function(err, c) { should.not.exists(err); c.jobType = 'tool'; // temporary, for scoping @@ -1699,13 +1751,14 @@ describe('relations', function() { }); }); - it('should find record on scope - verify', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should find record on scope - verify', function(done) { Category.findOne(function(err, c) { should.not.exists(err); c.jobs(function(err, jobs) { should.not.exists(err); jobs.should.have.length(2); - done(); + done(err); }); }); }); @@ -1713,7 +1766,7 @@ describe('relations', function() { describe('polymorphic hasOne', function() { before(function(done) { - // db = getSchema(); + db = getSchema(); Picture = db.define('Picture', {name: String}); Author = db.define('Author', {name: String}); Reader = db.define('Reader', {name: String}); @@ -1732,7 +1785,7 @@ describe('relations', function() { Author.create({name: 'Author 1'}, function(err, author) { should.not.exists(err); author.avatar.create({name: 'Avatar'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); should.exist(p); p.imageableId.should.eql(author.id); p.imageableType.should.equal('Author'); @@ -1758,7 +1811,7 @@ describe('relations', function() { Reader.create({name: 'Reader 1'}, function(err, reader) { should.not.exists(err); reader.mugshot.create({name: 'Mugshot'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); should.exist(p); p.imageableId.should.eql(reader.id); p.imageableType.should.equal('Reader'); @@ -1771,13 +1824,13 @@ describe('relations', function() { Author.findOne(function(err, author) { should.not.exists(err); author.avatar(function(err, p) { - should.not.exist(err); + if (err) return done(err); var avatar = author.avatar(); avatar.should.equal(p); p.name.should.equal('Avatar'); - p.imageableId.should.eql(author.id); + p.imageableId.toString().should.eql(author.id.toString()); p.imageableType.should.equal('Author'); done(); }); @@ -1788,9 +1841,9 @@ describe('relations', function() { Reader.findOne(function(err, reader) { should.not.exists(err); reader.mugshot(function(err, p) { - should.not.exist(err); + if (err) return done(err); p.name.should.equal('Mugshot'); - p.imageableId.should.eql(reader.id); + p.imageableId.toString().should.eql(reader.id.toString()); p.imageableType.should.equal('Reader'); done(); }); @@ -1813,7 +1866,7 @@ describe('relations', function() { return reader.mugshot.getAsync() .then(function(p) { p.name.should.equal('Mugshot'); - p.imageableId.should.eql(reader.id); + p.imageableId.toString().should.eql(reader.id.toString()); p.imageableType.should.equal('Reader'); done(); }); @@ -1824,7 +1877,7 @@ describe('relations', function() { Picture.findOne({where: {name: 'Avatar'}}, function(err, p) { should.not.exists(err); p.imageable(function(err, imageable) { - should.not.exist(err); + if (err) return done(err); imageable.should.be.instanceof(Author); imageable.name.should.equal('Author 1'); done(); @@ -1848,7 +1901,7 @@ describe('relations', function() { Picture.findOne({where: {name: 'Mugshot'}}, function(err, p) { should.not.exists(err); p.imageable(function(err, imageable) { - should.not.exist(err); + if (err) return done(err); imageable.should.be.instanceof(Reader); imageable.name.should.equal('Reader 1'); done(); @@ -1859,7 +1912,7 @@ describe('relations', function() { describe('polymorphic hasOne with non standard ids', function() { before(function(done) { - // db = getSchema(); + db = getSchema(); Picture = db.define('Picture', {name: String}); Author = db.define('Author', { username: {type: String, id: true, generated: true}, @@ -1903,7 +1956,7 @@ describe('relations', function() { Author.create({name: 'Author 1'}, function(err, author) { should.not.exists(err); author.avatar.create({name: 'Avatar'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); should.exist(p); p.oid.toString().should.equal(author.username.toString()); p.type.should.equal('Author'); @@ -1929,7 +1982,7 @@ describe('relations', function() { Reader.create({name: 'Reader 1'}, function(err, reader) { should.not.exists(err); reader.mugshot.create({name: 'Mugshot'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); should.exist(p); p.oid.toString().should.equal(reader.username.toString()); p.type.should.equal('Reader'); @@ -1942,7 +1995,7 @@ describe('relations', function() { Author.findOne(function(err, author) { should.not.exists(err); author.avatar(function(err, p) { - should.not.exist(err); + if (err) return done(err); var avatar = author.avatar(); avatar.should.equal(p); @@ -1959,7 +2012,7 @@ describe('relations', function() { Reader.findOne(function(err, reader) { should.not.exists(err); reader.mugshot(function(err, p) { - should.not.exist(err); + if (err) return done(err); p.name.should.equal('Mugshot'); p.oid.toString().should.equal(reader.username.toString()); p.type.should.equal('Reader'); @@ -1972,7 +2025,7 @@ describe('relations', function() { Picture.findOne({where: {name: 'Avatar'}}, function(err, p) { should.not.exists(err); p.owner(function(err, owner) { - should.not.exist(err); + if (err) return done(err); owner.should.be.instanceof(Author); owner.name.should.equal('Author 1'); done(); @@ -1984,7 +2037,7 @@ describe('relations', function() { Picture.findOne({where: {name: 'Mugshot'}}, function(err, p) { should.not.exists(err); p.owner(function(err, owner) { - should.not.exist(err); + if (err) return done(err); owner.should.be.instanceof(Reader); owner.name.should.equal('Reader 1'); done(); @@ -2018,7 +2071,7 @@ describe('relations', function() { describe('polymorphic hasMany', function() { before(function(done) { - // db = getSchema(); + db = getSchema(); Picture = db.define('Picture', {name: String}); Author = db.define('Author', {name: String}); Reader = db.define('Reader', {name: String}); @@ -2071,7 +2124,7 @@ describe('relations', function() { Author.create({name: 'Author 1'}, function(err, author) { should.not.exists(err); author.pictures.create({name: 'Author Pic'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); should.exist(p); p.imageableId.should.eql(author.id); p.imageableType.should.equal('Author'); @@ -2084,7 +2137,7 @@ describe('relations', function() { Reader.create({name: 'Reader 1'}, function(err, reader) { should.not.exists(err); reader.pictures.create({name: 'Reader Pic'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); should.exist(p); p.imageableId.should.eql(reader.id); p.imageableType.should.equal('Reader'); @@ -2098,7 +2151,7 @@ describe('relations', function() { should.not.exists(err); if (!author) return done(); author.pictures(function(err, pics) { - should.not.exist(err); + if (err) return done(err); var pictures = author.pictures(); pictures.should.eql(pics); @@ -2114,7 +2167,7 @@ describe('relations', function() { Reader.findOne(function(err, reader) { should.not.exists(err); reader.pictures(function(err, pics) { - should.not.exist(err); + if (err) return done(err); pics.should.have.length(1); pics[0].name.should.equal('Reader Pic'); done(); @@ -2124,10 +2177,10 @@ describe('relations', function() { it('should find the inverse of polymorphic relation - author', function(done) { Picture.findOne({where: {name: 'Author Pic'}}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.imageableType.should.equal('Author'); p.imageable(function(err, imageable) { - should.not.exist(err); + if (err) return done(err); imageable.should.be.instanceof(Author); imageable.name.should.equal('Author 1'); done(); @@ -2137,10 +2190,10 @@ describe('relations', function() { it('should find the inverse of polymorphic relation - reader', function(done) { Picture.findOne({where: {name: 'Reader Pic'}}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.imageableType.should.equal('Reader'); p.imageable(function(err, imageable) { - should.not.exist(err); + if (err) return done(err); imageable.should.be.instanceof(Reader); imageable.name.should.equal('Reader 1'); done(); @@ -2148,14 +2201,37 @@ describe('relations', function() { }); }); - it('should include the inverse of polymorphic relation', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, + 'should include the inverse of polymorphic relation', function(done) { Picture.find({include: 'imageable'}, function(err, pics) { - should.not.exist(err); + if (err) return done(err); pics.should.have.length(2); - pics[0].name.should.equal('Author Pic'); - pics[0].imageable().name.should.equal('Author 1'); - pics[1].name.should.equal('Reader Pic'); - pics[1].imageable().name.should.equal('Reader 1'); + + const actual = pics.map( + function(p) { + return {imageName: p.name, name: p.imageable().name}; + }); + + actual.should.containDeep([ + {name: 'Author 1', imageName: 'Author Pic'}, + {name: 'Reader 1', imageName: 'Reader Pic'}, + ]); + + done(); + }); + }); + + bdd.itIf(connectorCapabilities.adhocSort === false, + 'should include the inverse of polymorphic relation w/o adhocSort', function(done) { + Picture.find({include: 'imageable'}, function(err, pics) { + if (err) return done(err); + pics.should.have.length(2); + var names = ['Author Pic', 'Reader Pic']; + var imageables = ['Author 1', 'Reader 1']; + names.should.containEql(pics[0].name); + names.should.containEql(pics[1].name); + imageables.should.containEql(pics[0].imageable().name); + imageables.should.containEql(pics[1].imageable().name); done(); }); }); @@ -2175,7 +2251,7 @@ describe('relations', function() { Author.findOne({where: {name: 'Author 2'}}, function(err, author) { should.not.exists(err); author.pictures(function(err, pics) { - should.not.exist(err); + if (err) return done(err); pics.should.have.length(1); pics[0].name.should.equal('Sample'); done(); @@ -2185,10 +2261,10 @@ describe('relations', function() { it('should find the inverse of polymorphic relation - author', function(done) { Picture.findOne({where: {name: 'Sample'}}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.imageableType.should.equal('Author'); p.imageable(function(err, imageable) { - should.not.exist(err); + if (err) return done(err); imageable.should.be.instanceof(Author); imageable.name.should.equal('Author 2'); done(); @@ -2200,7 +2276,7 @@ describe('relations', function() { function(done) { Picture.findOne({where: {name: 'Sample'}, include: 'imageable'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); var imageable = p.imageable(); should.exist(imageable); imageable.should.be.instanceof(Author); @@ -2211,8 +2287,12 @@ describe('relations', function() { }); describe('polymorphic hasAndBelongsToMany through', function() { + var idAuthor, idReader; + before(function(done) { - // db = getSchema(); + db = getSchema(); + idAuthor = uid.fromConnector(db) || 3456; + idReader = uid.fromConnector(db) || 4567; Picture = db.define('Picture', {name: String}); Author = db.define('Author', {name: String}); Reader = db.define('Reader', {name: String}); @@ -2236,15 +2316,15 @@ describe('relations', function() { // Optionally, define inverse relations: Picture.hasMany(Author, {through: PictureLink, polymorphic: 'imageable', invert: true}); Picture.hasMany(Reader, {through: PictureLink, polymorphic: 'imageable', invert: true}); - var author = new Author({id: 1}); + var author = new Author({id: idAuthor}); var scope1 = author.pictures._scope; scope1.should.have.property('collect', 'picture'); scope1.should.have.property('include', 'picture'); - var reader = new Reader({id: 1}); + var reader = new Reader({id: idReader}); var scope2 = reader.pictures._scope; scope2.should.have.property('collect', 'picture'); scope2.should.have.property('include', 'picture'); - var picture = new Picture({id: 1}); + var picture = new Picture({id: idAuthor}); var scope3 = picture.authors._scope; scope3.should.have.property('collect', 'imageable'); scope3.should.have.property('include', 'imageable'); @@ -2256,13 +2336,13 @@ describe('relations', function() { var author, reader, pictures = []; it('should create polymorphic relation - author', function(done) { Author.create({name: 'Author 1'}, function(err, a) { - should.not.exist(err); + if (err) return done(err); author = a; author.pictures.create({name: 'Author Pic 1'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); pictures.push(p); author.pictures.create({name: 'Author Pic 2'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); pictures.push(p); done(); }); @@ -2272,10 +2352,10 @@ describe('relations', function() { it('should create polymorphic relation - reader', function(done) { Reader.create({name: 'Reader 1'}, function(err, r) { - should.not.exist(err); + if (err) return done(err); reader = r; reader.pictures.create({name: 'Reader Pic 1'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); pictures.push(p); done(); }); @@ -2284,28 +2364,43 @@ describe('relations', function() { it('should create polymorphic through model', function(done) { PictureLink.findOne(function(err, link) { - should.not.exist(err); - link.pictureId.should.eql(pictures[0].id); // eql for mongo ObjectId - link.imageableId.should.eql(author.id); - link.imageableType.should.equal('Author'); - link.imageable(function(err, imageable) { - imageable.should.be.instanceof(Author); - imageable.id.should.eql(author.id); - done(); - }); + if (err) return done(err); + if (connectorCapabilities.adhocSort !== false) { + link.pictureId.should.eql(pictures[0].id); + link.imageableId.should.eql(author.id); + link.imageableType.should.equal('Author'); + link.imageable(function(err, imageable) { + imageable.should.be.instanceof(Author); + imageable.id.should.eql(author.id); + done(); + }); + } else { + const picIds = pictures.map(pic => pic.id.toString()); + picIds.should.containEql(link.pictureId.toString()); + link.imageableType.should.be.oneOf('Author', 'Reader'); + link.imageable(function(err, imageable) { + imageable.id.should.be.oneOf(author.id, reader.id); + done(); + }); + } }); }); it('should get polymorphic relation through model - author', function(done) { if (!author) return done(); Author.findById(author.id, function(err, author) { - should.not.exist(err); + if (err) return done(err); author.name.should.equal('Author 1'); author.pictures(function(err, pics) { - should.not.exist(err); + if (err) return done(err); pics.should.have.length(2); - pics[0].name.should.equal('Author Pic 1'); - pics[1].name.should.equal('Author Pic 2'); + const names = pics.map(p => p.name); + const expected = ['Author Pic 1', 'Author Pic 2']; + if (connectorCapabilities.adhocSort !== false) { + names.should.eql(expected); + } else { + names.should.containDeep(expected); + } done(); }); }); @@ -2313,10 +2408,10 @@ describe('relations', function() { it('should get polymorphic relation through model - reader', function(done) { Reader.findById(reader.id, function(err, reader) { - should.not.exist(err); + if (err) return done(err); reader.name.should.equal('Reader 1'); reader.pictures(function(err, pics) { - should.not.exist(err); + if (err) return done(err); pics.should.have.length(1); pics[0].name.should.equal('Reader Pic 1'); done(); @@ -2330,8 +2425,13 @@ describe('relations', function() { if (!authors) return done(); authors[0].pictures(function(err, pics) { pics.should.have.length(2); - pics[0].name.should.equal('Author Pic 1'); - pics[1].name.should.equal('Author Pic 2'); + const names = pics.map(p => p.name); + const expected = ['Author Pic 1', 'Author Pic 2']; + if (connectorCapabilities.adhocSort !== false) { + names.should.eql(expected); + } else { + names.should.containDeep(expected); + } done(); }); }); @@ -2342,7 +2442,7 @@ describe('relations', function() { if (!author) return done(); Author.findById(author.id, function(err, author) { Picture.create({name: 'Example'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); pictures.push(p); anotherPicture = p; author.pictures.add(p, function(err, link) { @@ -2360,9 +2460,9 @@ describe('relations', function() { if (!anotherPicture) return done(); PictureLink.findOne({where: {pictureId: anotherPicture.id, imageableType: 'Author'}}, function(err, link) { - should.not.exist(err); - link.pictureId.should.eql(anotherPicture.id); - link.imageableId.should.eql(author.id); + if (err) return done(err); + link.pictureId.toString().should.eql(anotherPicture.id.toString()); + link.imageableId.toString().should.eql(author.id.toString()); link.imageableType.should.equal('Author'); done(); }); @@ -2371,11 +2471,11 @@ describe('relations', function() { var anotherAuthor, anotherReader; it('should add to a polymorphic relation - author', function(done) { Author.create({name: 'Author 2'}, function(err, author) { - should.not.exist(err); + if (err) return done(err); anotherAuthor = author; if (!anotherPicture) return done(); author.pictures.add(anotherPicture.id, function(err, p) { - should.not.exist(err); + if (err) return done(err); done(); }); }); @@ -2383,11 +2483,11 @@ describe('relations', function() { it('should add to a polymorphic relation - author', function(done) { Reader.create({name: 'Reader 2'}, function(err, reader) { - should.not.exist(err); + if (err) return done(err); anotherReader = reader; if (!anotherPicture) return done(); reader.pictures.add(anotherPicture.id, function(err, p) { - should.not.exist(err); + if (err) return done(err); done(); }); }); @@ -2398,8 +2498,13 @@ describe('relations', function() { Picture.findById(anotherPicture.id, function(err, p) { p.authors(function(err, authors) { authors.should.have.length(2); - authors[0].name.should.equal('Author 1'); - authors[1].name.should.equal('Author 2'); + const names = authors.map(p => p.name); + const expected = ['Author 1', 'Author 2']; + if (connectorCapabilities.adhocSort !== false) { + names.should.eql(expected); + } else { + names.should.containDeep(expected); + } done(); }); }); @@ -2410,7 +2515,12 @@ describe('relations', function() { Picture.findById(anotherPicture.id, function(err, p) { p.readers(function(err, readers) { readers.should.have.length(1); - readers[0].name.should.equal('Reader 2'); + if (connectorCapabilities.adhocSort !== false) { + readers[0].name.should.equal('Reader 2'); + } else { + var readerNames = ['Reader 1', 'Reader 2']; + readers[0].name.should.be.oneOf(readerNames); + } done(); }); }); @@ -2421,9 +2531,13 @@ describe('relations', function() { Author.findById(author.id, function(err, author) { author.pictures(function(err, pics) { pics.should.have.length(3); - pics[0].name.should.equal('Author Pic 1'); - pics[1].name.should.equal('Author Pic 2'); - pics[2].name.should.equal('Example'); + const names = pics.map(p => p.name); + const expected = ['Author Pic 1', 'Author Pic 2', 'Example']; + if (connectorCapabilities.adhocSort !== false) { + names.should.eql(expected); + } else { + names.should.containDeep(expected); + } done(); }); }); @@ -2439,11 +2553,12 @@ describe('relations', function() { }); }); - it('should remove from a polymorphic relation - author', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should remove from a polymorphic relation - author', function(done) { if (!author || !anotherPicture) return done(); Author.findById(author.id, function(err, author) { author.pictures.remove(anotherPicture.id, function(err) { - should.not.exist(err); + if (err) return done(err); done(); }); }); @@ -2453,9 +2568,18 @@ describe('relations', function() { if (!author) return done(); Author.findById(author.id, function(err, author) { author.pictures(function(err, pics) { - pics.should.have.length(2); - pics[0].name.should.equal('Author Pic 1'); - pics[1].name.should.equal('Author Pic 2'); + // If deleteWithOtherThanId is not implemented, the above test is skipped and + // the remove did not take place. Thus +1. + var expectedLength = connectorCapabilities.deleteWithOtherThanId !== false ? + 2 : 3; + pics.should.have.length(expectedLength); + + const names = pics.map(p => p.name); + if (connectorCapabilities.adhocSort !== false) { + names.should.eql(['Author Pic 1', 'Author Pic 2']); + } else { + names.should.containDeep(['Author Pic 1', 'Author Pic 2', 'Example']); + } done(); }); }); @@ -2475,9 +2599,9 @@ describe('relations', function() { if (!anotherPicture) return done(); Picture.findById(anotherPicture.id, function(err, p) { p.authors.create({name: 'Author 3'}, function(err, a) { - should.not.exist(err); + if (err) return done(err); author = a; - author.name.should.equal('Author 3'); + should.equal(author.name, 'Author 3'); done(); }); }); @@ -2488,9 +2612,9 @@ describe('relations', function() { PictureLink.findOne({where: { pictureId: anotherPicture.id, imageableId: author.id, imageableType: 'Author', }}, function(err, link) { - should.not.exist(err); - link.pictureId.should.eql(anotherPicture.id); - link.imageableId.should.eql(author.id); + if (err) return done(err); + link.pictureId.toString().should.eql(anotherPicture.id.toString()); + link.imageableId.toString().should.eql(author.id.toString()); link.imageableType.should.equal('Author'); done(); }); @@ -2579,7 +2703,7 @@ describe('relations', function() { should.not.exist(e); should.exist(l); l.should.be.an.instanceOf(List); - todo.list().id.should.equal(l.id); + todo.list().id.should.eql(l.id); todo.list().name.should.equal('List 1'); done(); }); @@ -2601,7 +2725,7 @@ describe('relations', function() { should.not.exist(e); should.exist(l); l.should.be.an.instanceOf(List); - todo.list().id.should.equal(l.id); + todo.list().id.should.eql(l.id); todo.list().name.should.equal('List 1'); done(); }); @@ -2625,7 +2749,7 @@ describe('relations', function() { .then(function(l) { should.exist(l); l.should.be.an.instanceOf(List); - todo.list().id.should.equal(l.id); + todo.list().id.should.eql(l.id); todo.list().name.should.equal('List 1'); done(); }); @@ -2639,10 +2763,10 @@ describe('relations', function() { should.not.exist(e); should.exist(list); Item.create({list: list}, function(err, item) { - should.not.exist(err); + if (err) return done(err); should.exist(item); should.exist(item.listId); - item.listId.should.equal(list.id); + item.listId.should.eql(list.id); item.__cachedRelations.list.should.equal(list); done(); }); @@ -2652,7 +2776,7 @@ describe('relations', function() { it('should update related item on scope', function(done) { Item.findById(itemId, function(e, todo) { todo.list.update({name: 'List A'}, function(err, list) { - should.not.exist(err); + if (err) return done(err); should.exist(list); list.name.should.equal('List A'); done(); @@ -2663,7 +2787,7 @@ describe('relations', function() { it('should get related item on scope', function(done) { Item.findById(itemId, function(e, todo) { todo.list(function(err, list) { - should.not.exist(err); + if (err) return done(err); should.exist(list); list.name.should.equal('List A'); done(); @@ -2674,7 +2798,7 @@ describe('relations', function() { it('should destroy related item on scope', function(done) { Item.findById(itemId, function(e, todo) { todo.list.destroy(function(err) { - should.not.exist(err); + if (err) return done(err); done(); }); }); @@ -2683,7 +2807,7 @@ describe('relations', function() { it('should get related item on scope - verify', function(done) { Item.findById(itemId, function(e, todo) { todo.list(function(err, list) { - should.not.exist(err); + if (err) return done(err); should.not.exist(list); done(); }); @@ -2709,7 +2833,7 @@ describe('relations', function() { Fear.create(function(err, fear) { should.not.exists(err); should.exists(fear); - fear.mindId.should.be.equal(mind.id); + fear.mindId.should.eql(mind.id); should.exists(fear.mind()); done(); }); @@ -2727,7 +2851,7 @@ describe('relations', function() { Fear.create() .then(function(fear) { should.exists(fear); - fear.mindId.should.be.equal(mind.id); + fear.mindId.should.eql(mind.id); should.exists(fear.mind()); done(); }).catch(done); @@ -2752,7 +2876,7 @@ describe('relations', function() { var p = new Passport({name: 'Passport', notes: 'Some notes...'}); p.person.create({name: 'Fred', age: 36}, function(err, person) { personCreated = person; - p.personId.should.equal(person.id); + p.personId.should.eql(person.id); person.name.should.equal('Fred'); person.passportNotes.should.equal('Some notes...'); p.save(function(err, passport) { @@ -2764,7 +2888,7 @@ describe('relations', function() { it('should find record on scope', function(done) { Passport.findOne(function(err, p) { - p.personId.should.eql(personCreated.id); + should.equal(p.personId, personCreated.id); p.person(function(err, person) { person.name.should.equal('Fred'); person.should.have.property('age', undefined); @@ -2778,7 +2902,7 @@ describe('relations', function() { var p = new Passport({name: 'Passport', notes: 'Some notes...'}); p.person.create({name: 'Fred', age: 36}) .then(function(person) { - p.personId.should.equal(person.id); + p.personId.should.eql(person.id); person.name.should.equal('Fred'); person.passportNotes.should.equal('Some notes...'); return p.save(); @@ -2792,7 +2916,11 @@ describe('relations', function() { it('should find record on scope with promises', function(done) { Passport.findOne() .then(function(p) { - p.personId.should.eql(personCreated.id); + if (connectorCapabilities.adhocSort !== false) { + // We skip the check if adhocSort is not supported because + // the first row returned may or may not be the same + p.personId.should.eql(personCreated.id); + } return p.person.getAsync(); }) .then(function(person) { @@ -2825,9 +2953,9 @@ describe('relations', function() { Person.create({name: 'Fred', age: 36}, function(err, person) { var p = new Passport({name: 'Passport', notes: 'Some notes...'}); p.person(person); - p.personId.should.equal(person.id); + p.personId.should.eql(person.id); var data = p.toObject(true); - data.person.id.should.equal(person.id); + data.person.id.should.eql(person.id); data.person.name.should.equal('Fred'); p.save(function(err) { should.not.exists(err); @@ -2840,7 +2968,7 @@ describe('relations', function() { Passport.findOne(function(err, p) { should.not.exists(err); var data = p.toObject(true); - data.person.id.should.equal(p.personId); + data.person.id.should.eql(p.personId); data.person.name.should.equal('Fred'); done(); }); @@ -2850,7 +2978,7 @@ describe('relations', function() { Passport.findOne() .then(function(p) { var data = p.toObject(true); - data.person.id.should.equal(p.personId); + data.person.id.should.eql(p.personId); data.person.name.should.equal('Fred'); done(); }).catch(done); @@ -2862,7 +2990,7 @@ describe('relations', function() { var supplierId, accountId; before(function() { - // db = getSchema(); + db = getSchema(); Supplier = db.define('Supplier', {name: String}); Account = db.define('Account', {accountNo: String, supplierName: String}); }); @@ -2903,7 +3031,7 @@ describe('relations', function() { should.not.exist(e); should.exist(act); act.should.be.an.instanceOf(Account); - supplier.account().id.should.equal(act.id); + supplier.account().id.should.eql(act.id); act.supplierName.should.equal(supplier.name); done(); }); @@ -2924,7 +3052,7 @@ describe('relations', function() { should.not.exist(e); should.exist(act); act.should.be.an.instanceOf(Account); - supplier.account().id.should.equal(act.id); + supplier.account().id.should.eql(act.id); act.supplierName.should.equal(supplier.name); done(); }); @@ -2947,7 +3075,7 @@ describe('relations', function() { accountId = act.id; should.exist(act); act.should.be.an.instanceOf(Account); - supplier.account().id.should.equal(act.id); + supplier.account().id.should.eql(act.id); act.supplierName.should.equal(supplier.name); done(); }); @@ -2996,7 +3124,7 @@ describe('relations', function() { function(err, act) { should.not.exist(e); act.supplierName.should.equal('Supplier A'); - act.supplierId.should.eql(supplierId); + act.supplierId.toString().should.eql(supplierId.toString()); done(); }); }); @@ -3095,7 +3223,7 @@ describe('relations', function() { var supplierId, accountId; before(function() { - // db = getSchema(); + db = getSchema(); Supplier = db.define('Supplier', {name: String}); Account = db.define('Account', {accountNo: String, supplierName: String, block: Boolean}); Supplier.hasOne(Account, {scope: {where: {block: false}}, properties: {name: 'supplierName'}}); @@ -3115,7 +3243,7 @@ describe('relations', function() { act.should.be.an.instanceOf(Account); should.exist(act.block); act.block.should.be.false; - supplier.account().id.should.equal(act.id); + supplier.account().id.should.eql(act.id); act.supplierName.should.equal(supplier.name); done(); }); @@ -3134,8 +3262,10 @@ describe('relations', function() { }); }); - it('should not find record that does not match scope', function(done) { + bdd.itIf(connectorCapabilities.supportUpdateWithoutId !== false, + 'should not find record that does not match scope', function(done) { Account.updateAll({block: true}, function(err) { + if (err) return done(err); Supplier.findById(supplierId, function(err, supplier) { supplier.account(function(err, account) { should.not.exists(account); @@ -3145,8 +3275,10 @@ describe('relations', function() { }); }); - it('should not include record that does not match scope', function(done) { + bdd.itIf(connectorCapabilities.supportUpdateWithoutId !== false, + 'should not include record that does not match scope', function(done) { Account.updateAll({block: true}, function(err) { + if (err) return done(err); Supplier.findById(supplierId, {include: 'account'}, function(err, supplier) { should.not.exists(supplier.toJSON().account); supplier.account(function(err, account) { @@ -3173,7 +3305,7 @@ describe('relations', function() { act.should.be.an.instanceOf(Account); should.exist(act.block); act.block.should.be.false; - supplier.account().id.should.equal(act.id); + supplier.account().id.should.eql(act.id); act.supplierName.should.equal(supplier.name); done(); }); @@ -3182,7 +3314,8 @@ describe('relations', function() { }); }); - it('should find record that match scope with promises', function(done) { + bdd.itIf(connectorCapabilities.supportUpdateWithoutId !== false, + 'should find record that match scope with promises', function(done) { Account.updateAll({block: true}) .then(function() { return Supplier.findById(supplierId); @@ -3194,7 +3327,9 @@ describe('relations', function() { should.not.exist(account); done(); }) - .catch(done); + .catch(function(err) { + done(); + }); }); }); @@ -3203,7 +3338,7 @@ describe('relations', function() { var supplierId, accountId; before(function() { - // db = getSchema(); + db = getSchema(); Supplier = db.define('Supplier', { sid: { type: String, @@ -3243,7 +3378,7 @@ describe('relations', function() { should.not.exist(e); should.exist(act); act.should.be.an.instanceOf(Account); - supplier.account().accid.should.equal(act.accid); + supplier.account().accid.should.eql(act.accid); act.supplierName.should.equal(supplier.name); done(); }); @@ -3390,7 +3525,7 @@ describe('relations', function() { should.exist(employee); employees.length.should.equal(2); employee.should.be.an.instanceOf(Employee); - employee.companyId.should.be.eql(boss.companyId); + employee.companyId.should.eql(boss.companyId); return employees; }); }); @@ -3468,8 +3603,8 @@ describe('relations', function() { t.should.be.an.instanceOf(TagName); ArticleTag.findOne(function(e, at) { should.exist(at); - at.tagNameId.toString().should.equal(t.id.toString()); - at.articleId.toString().should.equal(article.id.toString()); + at.tagNameId.toString().should.eql(t.id.toString()); + at.articleId.toString().should.eql(article.id.toString()); done(); }); }); @@ -3504,7 +3639,8 @@ describe('relations', function() { }); }); - it('should allow to remove connection with instance', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should allow to remove connection with instance', function(done) { Article.findOne(function(e, article) { article.tagNames(function(e, tags) { var len = tags.length; @@ -3530,8 +3666,8 @@ describe('relations', function() { return ArticleTag.findOne() .then(function(at) { should.exist(at); - at.tagNameId.toString().should.equal(t.id.toString()); - at.articleId.toString().should.equal(article.id.toString()); + at.tagNameId.toString().should.eql(t.id.toString()); + at.articleId.toString().should.eql(article.id.toString()); done(); }); }); @@ -3569,7 +3705,8 @@ describe('relations', function() { .catch(done); }); - it('should allow to remove connection with instance with promises', function(done) { + bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false, + 'should allow to remove connection with instance with promises', function(done) { Article.findOne() .then(function(article) { return article.tagNames.getAsync() @@ -3653,7 +3790,7 @@ describe('relations', function() { before(function() { tmp = getTransientDataSource(); - // db = getSchema(); + db = getSchema(); Person = db.define('Person', {name: String}); Passport = tmp.define('Passport', {name: {type: 'string', required: true}}, @@ -3714,7 +3851,7 @@ describe('relations', function() { p.passport.toObject().should.eql({name: 'Anonymous'}); p.passportItem().should.equal(p.passport); p.passportItem(function(err, passport) { - should.not.exist(err); + err.should.not.exist(); passport.should.equal(p.passport); }); }); @@ -3736,10 +3873,10 @@ describe('relations', function() { var personId; it('should create an embedded item on scope', function(done) { Person.create({name: 'Fred'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); personId = p.id; p.passportItem.create({name: 'Fredric'}, function(err, passport) { - should.not.exist(err); + if (err) return done(err); p.passport.toObject().should.eql({name: 'Fredric'}); p.passport.should.be.an.instanceOf(Passport); done(); @@ -3749,7 +3886,7 @@ describe('relations', function() { it('should get an embedded item on scope', function(done) { Person.findById(personId, function(err, p) { - should.not.exist(err); + if (err) return done(err); var passport = p.passportItem(); passport.toObject().should.eql({name: 'Fredric'}); passport.should.be.an.instanceOf(Passport); @@ -3786,7 +3923,7 @@ describe('relations', function() { it('should update an embedded item on scope', function(done) { Person.findById(personId, function(err, p) { p.passportItem.update({name: 'Freddy'}, function(err, passport) { - should.not.exist(err); + if (err) return done(err); passport = p.passportItem(); passport.toObject().should.eql({name: 'Freddy'}); passport.should.be.an.instanceOf(Passport); @@ -3798,7 +3935,7 @@ describe('relations', function() { it('should get an embedded item on scope - verify', function(done) { Person.findById(personId, function(err, p) { - should.not.exist(err); + if (err) return done(err); var passport = p.passportItem(); passport.toObject().should.eql({name: 'Freddy'}); done(); @@ -3808,7 +3945,7 @@ describe('relations', function() { it('should destroy an embedded item on scope', function(done) { Person.findById(personId, function(err, p) { p.passportItem.destroy(function(err) { - should.not.exist(err); + if (err) return done(err); should.equal(p.passport, null); done(); }); @@ -3817,7 +3954,7 @@ describe('relations', function() { it('should get an embedded item on scope - verify', function(done) { Person.findById(personId, function(err, p) { - should.not.exist(err); + if (err) return done(err); should.equal(p.passport, null); done(); }); @@ -3827,7 +3964,7 @@ describe('relations', function() { var p = new Person({name: 'Fred'}); p.isNewRecord().should.be.true; p.passportItem.create({name: 'Fredric'}, function(err, passport) { - should.not.exist(err); + if (err) return done(err); p.passport.should.equal(passport); p.isNewRecord().should.be.false; done(); @@ -3988,7 +4125,7 @@ describe('relations', function() { // This test spefically uses the Memory connector // in order to test the use of the auto-generated // id, in the sequence of the related model. - var Passport; + var Passport, Person; before(function() { db = getMemoryDataSource(); Person = db.define('Person', {name: String}); @@ -4006,7 +4143,7 @@ describe('relations', function() { it('should create an item - to offset id', function(done) { Passport.create({name: 'Wilma'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.id.should.equal(1); p.name.should.equal('Wilma'); done(); @@ -4015,9 +4152,9 @@ describe('relations', function() { it('should create an embedded item on scope', function(done) { Person.create({name: 'Fred'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.passportItem.create({name: 'Fredric'}, function(err, passport) { - should.not.exist(err); + if (err) return done(err); p.passport.id.should.eql(2); p.passport.name.should.equal('Fredric'); done(); @@ -4042,7 +4179,7 @@ describe('relations', function() { var Passport; before(function() { tmp = getTransientDataSource(); - // db = getSchema(); + db = getSchema(); Person = db.define('Person', {name: String}); Passport = tmp.define('Passport', { @@ -4059,9 +4196,9 @@ describe('relations', function() { it('should create an embedded item on scope', function(done) { Person.create({name: 'Fred'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.passportItem.create({name: 'Fredric'}, function(err, passport) { - should.not.exist(err); + if (err) return done(err); passport.id.should.match(/^[0-9a-fA-F]{24}$/); p.passport.name.should.equal('Fredric'); done(); @@ -4075,7 +4212,7 @@ describe('relations', function() { before(function(done) { tmp = getTransientDataSource({defaultIdType: Number}); - // db = getSchema(); + db = getSchema(); Person = db.define('Person', {name: String}); Address = tmp.define('Address', {street: String}); Address.validatesPresenceOf('street'); @@ -4104,7 +4241,7 @@ describe('relations', function() { it('should create embedded items on scope', function(done) { Person.create({name: 'Fred'}, function(err, p) { p.addressList.create({street: 'Street 1'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address1 = address; should.exist(address1.id); address1.street.should.equal('Street 1'); @@ -4116,7 +4253,7 @@ describe('relations', function() { it('should create embedded items on scope', function(done) { Person.findOne(function(err, p) { p.addressList.create({street: 'Street 2'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address2 = address; should.exist(address2.id); address2.street.should.equal('Street 2'); @@ -4128,7 +4265,7 @@ describe('relations', function() { it('should return embedded items from scope', function(done) { Person.findOne(function(err, p) { p.addressList(function(err, addresses) { - should.not.exist(err); + if (err) return done(err); var list = p.addressList(); list.should.equal(addresses); @@ -4149,7 +4286,7 @@ describe('relations', function() { it('should filter embedded items on scope', function(done) { Person.findOne(function(err, p) { p.addressList({where: {street: 'Street 2'}}, function(err, addresses) { - should.not.exist(err); + if (err) return done(err); addresses.should.have.length(1); addresses[0].id.should.eql(address2.id); addresses[0].street.should.equal('Street 2'); @@ -4184,7 +4321,7 @@ describe('relations', function() { it('should check if item exists', function(done) { Person.findOne(function(err, p) { p.addressList.exists(address2.id, function(err, exists) { - should.not.exist(err); + if (err) return done(err); exists.should.be.true; done(); }); @@ -4225,12 +4362,12 @@ describe('relations', function() { it('should have accessors: at, get, set', function(done) { Person.findOne(function(err, p) { - p.addressList.at(0).id.should.equal(address1.id); - p.addressList.get(address1.id).id.should.equal(address1.id); + p.addressList.at(0).id.should.eql(address1.id); + p.addressList.get(address1.id).id.should.eql(address1.id); p.addressList.set(address1.id, {street: 'Changed 1'}); p.addresses[0].street.should.equal('Changed 1'); - p.addressList.at(1).id.should.equal(address2.id); - p.addressList.get(address2.id).id.should.equal(address2.id); + p.addressList.at(1).id.should.eql(address2.id); + p.addressList.get(address2.id).id.should.eql(address2.id); p.addressList.set(address2.id, {street: 'Changed 2'}); p.addresses[1].street.should.equal('Changed 2'); done(); @@ -4241,7 +4378,7 @@ describe('relations', function() { Person.findOne(function(err, p) { p.addresses.should.have.length(2); p.addressList.destroy(address1.id, function(err) { - should.not.exist(err); + if (err) return done(err); p.addresses.should.have.length(1); done(); }); @@ -4258,7 +4395,7 @@ describe('relations', function() { it('should create embedded items on scope', function(done) { Person.findOne(function(err, p) { p.addressList.create({street: 'Street 3'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address.street.should.equal('Street 3'); done(); }); @@ -4269,7 +4406,7 @@ describe('relations', function() { Person.findOne(function(err, p) { p.addresses.should.have.length(2); p.addressList.destroyAll({street: 'Street 3'}, function(err) { - should.not.exist(err); + if (err) return done(err); p.addresses.should.have.length(1); done(); }); @@ -4280,7 +4417,7 @@ describe('relations', function() { Person.findOne(function(err, p) { p.addresses.should.have.length(1); p.addressList.destroyAll(function(err) { - should.not.exist(err); + if (err) return done(err); p.addresses.should.have.length(0); done(); }); @@ -4298,7 +4435,7 @@ describe('relations', function() { var p = new Person({name: 'Fred'}); p.isNewRecord().should.be.true; p.addressList.create({street: 'Street 4'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address.street.should.equal('Street 4'); p.isNewRecord().should.be.false; done(); @@ -4309,7 +4446,7 @@ describe('relations', function() { describe('embedsMany - numeric ids + forceId', function() { before(function(done) { tmp = getTransientDataSource(); - // db = getSchema(); + db = getSchema(); Person = db.define('Person', {name: String}); Address = tmp.define('Address', { id: {type: Number, id: true}, @@ -4327,7 +4464,7 @@ describe('relations', function() { it('should create embedded items on scope', function(done) { Person.create({name: 'Fred'}, function(err, p) { p.addressList.create({street: 'Street 1'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address.id.should.equal(1); p.addressList.create({street: 'Street 2'}, function(err, address) { address.id.should.equal(2); @@ -4344,7 +4481,7 @@ describe('relations', function() { describe('embedsMany - explicit ids', function() { before(function(done) { tmp = getTransientDataSource(); - // db = getSchema(); + db = getSchema(); Person = db.define('Person', {name: String}); Address = tmp.define('Address', {street: String}, {forceId: false}); Address.validatesPresenceOf('street'); @@ -4360,9 +4497,9 @@ describe('relations', function() { it('should create embedded items on scope', function(done) { Person.create({name: 'Fred'}, function(err, p) { p.addressList.create({id: 'home', street: 'Street 1'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); p.addressList.create({id: 'work', street: 'Work Street 2'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address.id.should.equal('work'); address.street.should.equal('Work Street 2'); done(); @@ -4408,7 +4545,7 @@ describe('relations', function() { Person.findOne(function(err, p) { p.addresses.should.have.length(2); p.addressList.destroy('home', function(err) { - should.not.exist(err); + if (err) return done(err); p.addresses.should.have.length(1); done(); }); @@ -4478,7 +4615,7 @@ describe('relations', function() { {id: 'work', street: 'Work Street'}, ]; Person.create({name: 'Wilma', addresses: addresses}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.addressList.at(0).id.should.equal('home'); p.addressList.at(1).id.should.equal('work'); done(); @@ -4491,7 +4628,7 @@ describe('relations', function() { {id: 'work', street: 'Work Street'}, ]; Person.create({name: 'Wilma', addressList: addresses}, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.addresses.should.have.length(0); done(); }); @@ -4500,7 +4637,7 @@ describe('relations', function() { it('should create embedded items with auto-generated id', function(done) { Person.create({name: 'Wilma'}, function(err, p) { p.addressList.create({street: 'Home Street 1'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address.id.should.match(/^[0-9a-fA-F]{24}$/); address.street.should.equal('Home Street 1'); done(); @@ -4571,11 +4708,11 @@ describe('relations', function() { Person.create({name: 'Fred'}, function(err, p) { person = p; p.addressList.create(address1.toObject(), function(err, address) { - should.not.exist(err); + if (err) return done(err); address.id.should.eql(2); address.street.should.equal('Street 1'); p.addressList.create(address2.toObject(), function(err, address) { - should.not.exist(err); + if (err) return done(err); address.id.should.eql(3); address.street.should.equal('Street 2'); done(); @@ -4587,7 +4724,7 @@ describe('relations', function() { it('should create embedded items on scope', function(done) { Person.findById(person.id, function(err, p) { p.addressList.create({street: 'Street 4'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address.id.should.equal(5); // in Address sequence, correct offset address.street.should.equal('Street 4'); done(); @@ -4598,7 +4735,7 @@ describe('relations', function() { it('should have embedded items on scope', function(done) { Person.findById(person.id, function(err, p) { p.addressList(function(err, addresses) { - should.not.exist(err); + if (err) return done(err); addresses.should.have.length(3); addresses[0].street.should.equal('Street 1'); addresses[1].street.should.equal('Street 2'); @@ -4611,7 +4748,7 @@ describe('relations', function() { it('should validate embedded items on scope - id', function(done) { Person.create({name: 'Wilma'}, function(err, p) { p.addressList.create({id: null, street: 'Street 1'}, function(err, address) { - should.not.exist(err); + if (err) return done(err); address.street.should.equal('Street 1'); done(); }); @@ -4619,8 +4756,9 @@ describe('relations', function() { }); it('should validate embedded items on scope - street', function(done) { + var newId = uid.fromConnector(db) || 1234; Person.create({name: 'Wilma'}, function(err, p) { - p.addressList.create({id: 1234}, function(err, address) { + p.addressList.create({id: newId}, function(err, address) { should.exist(err); err.name.should.equal('ValidationError'); err.details.codes.street.should.eql(['presence']); @@ -4637,7 +4775,7 @@ describe('relations', function() { var category, job1, job2, job3; before(function() { - // db = getSchema(); + db = getSchema(); Category = db.define('Category', {name: String}); Job = db.define('Job', {name: String}); Link = db.define('Link', {name: String, notes: String}, {forceId: false}); @@ -4729,7 +4867,7 @@ describe('relations', function() { cat.links.should.have.length(2); cat.items.destroy(job1.id, function(err) { if (err) return done(err); - should.not.exist(err); + if (err) return done(err); cat.links.should.have.length(1); done(); }); @@ -4792,7 +4930,7 @@ describe('relations', function() { cat.links.should.have.length(2); cat.items.remove(job2.id, function(err) { if (err) return done(err); - should.not.exist(err); + if (err) return done(err); cat.links.should.have.length(1); done(); }); @@ -4894,7 +5032,7 @@ describe('relations', function() { var person1, person2; before(function(done) { - // db = getSchema(); + db = getSchema(); tmp = getTransientDataSource(); Book = db.define('Book', {name: String}); @@ -4943,7 +5081,7 @@ describe('relations', function() { link = book.people.build(); link.linked(person2); book.save(function(err, book) { - should.not.exist(err); + if (err) return done(err); var link = book.people.at(0); link.should.be.instanceof(Link); @@ -4970,14 +5108,14 @@ describe('relations', function() { var link = book.people.at(0); link.should.be.instanceof(Link); - link.id.should.equal(1); + link.id.should.eql(1); link.linkedId.should.eql(person1.id); link.linkedType.should.equal('Author'); link.notes.should.equal('Something ...'); link = book.people.at(1); link.should.be.instanceof(Link); - link.id.should.equal(2); + link.id.should.eql(2); link.linkedId.should.eql(person2.id); link.linkedType.should.equal('Reader'); @@ -4995,7 +5133,8 @@ describe('relations', function() { }); }); - it('should include nested related items on scope', function(done) { + bdd.itIf(connectorCapabilities.supportInclude !== false, + 'should include nested related items on scope', function(done) { // There's some date duplication going on, so it might // make sense to override toObject on a case-by-case basis // to sort this out (delete links, keep people). @@ -5008,8 +5147,8 @@ describe('relations', function() { obj.should.have.property('people'); obj.links.should.have.length(2); - obj.links[0].name.should.equal('Author 1'); - obj.links[1].name.should.equal('Reader 1'); + obj.links[0].name.should.be.oneOf('Author 1', 'Reader 1'); + obj.links[1].name.should.be.oneOf('Author 1', 'Reader 1'); obj.people.should.have.length(2); @@ -5028,7 +5167,7 @@ describe('relations', function() { var job1, job2, job3; before(function(done) { - // db = getSchema(); + db = getSchema(); Category = db.define('Category', {name: String}); Job = db.define('Job', {name: String}); @@ -5076,7 +5215,7 @@ describe('relations', function() { cat.jobIds.should.be.an.array; cat.jobIds.should.have.length(0); cat.jobs.create({name: 'Job 2'}, function(err, p) { - should.not.exist(err); + if (err) return done(err); cat.jobIds.should.have.length(1); cat.jobIds.should.eql([p.id]); p.name.should.equal('Job 2'); @@ -5102,7 +5241,7 @@ describe('relations', function() { Category.findOne(function(err, cat) { cat.jobIds.should.eql([job2.id]); cat.jobs(function(err, jobs) { - should.not.exist(err); + if (err) return done(err); var p = jobs[0]; p.id.should.eql(job2.id); p.name.should.equal('Job 2'); @@ -5115,7 +5254,7 @@ describe('relations', function() { Category.findOne(function(err, cat) { cat.jobIds.should.eql([job2.id]); cat.jobs.findById(job2.id, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.should.be.instanceof(Job); p.id.should.eql(job2.id); p.name.should.equal('Job 2'); @@ -5127,7 +5266,7 @@ describe('relations', function() { it('should check if a record exists on scope', function(done) { Category.findOne(function(err, cat) { cat.jobs.exists(job2.id, function(err, exists) { - should.not.exist(err); + if (err) return done(err); should.exist(exists); done(); }); @@ -5138,7 +5277,7 @@ describe('relations', function() { Category.findOne(function(err, cat) { var attrs = {name: 'Job 2 - edit'}; cat.jobs.updateById(job2.id, attrs, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.name.should.equal(attrs.name); done(); }); @@ -5148,7 +5287,7 @@ describe('relations', function() { it('should get a record by index - at', function(done) { Category.findOne(function(err, cat) { cat.jobs.at(0, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.should.be.instanceof(Job); p.id.should.eql(job2.id); p.name.should.equal('Job 2 - edit'); @@ -5160,8 +5299,9 @@ describe('relations', function() { it('should add a record to scope - object', function(done) { Category.findOne(function(err, cat) { cat.jobs.add(job1, function(err, prod) { - should.not.exist(err); - cat.jobIds.should.eql([job2.id, job1.id]); + if (err) return done(err); + cat.jobIds[0].should.eql(job2.id); + cat.jobIds[1].should.eql(job1.id); prod.id.should.eql(job1.id); prod.should.have.property('name'); done(); @@ -5172,9 +5312,11 @@ describe('relations', function() { it('should add a record to scope - object', function(done) { Category.findOne(function(err, cat) { cat.jobs.add(job3.id, function(err, prod) { - should.not.exist(err); + if (err) return done(err); var expected = [job2.id, job1.id, job3.id]; - cat.jobIds.should.eql(expected); + cat.jobIds[0].should.eql(expected[0]); + cat.jobIds[1].should.eql(expected[1]); + cat.jobIds[2].should.eql(expected[2]); prod.id.should.eql(job3.id); prod.should.have.property('name'); done(); @@ -5185,7 +5327,7 @@ describe('relations', function() { it('should find items on scope - findById', function(done) { Category.findOne(function(err, cat) { cat.jobs.findById(job3.id, function(err, p) { - should.not.exist(err); + if (err) return done(err); p.id.should.eql(job3.id); p.name.should.equal('Job 3'); done(); @@ -5197,7 +5339,7 @@ describe('relations', function() { Category.findOne(function(err, cat) { var filter = {where: {name: 'Job 1'}}; cat.jobs(filter, function(err, jobs) { - should.not.exist(err); + if (err) return done(err); jobs.should.have.length(1); var p = jobs[0]; p.id.should.eql(job1.id); @@ -5210,10 +5352,12 @@ describe('relations', function() { it('should remove items from scope', function(done) { Category.findOne(function(err, cat) { cat.jobs.remove(job1.id, function(err, ids) { - should.not.exist(err); + if (err) return done(err); var expected = [job2.id, job3.id]; - cat.jobIds.should.eql(expected); - ids.should.eql(cat.jobIds); + cat.jobIds[0].should.eql(expected[0]); + cat.jobIds[1].should.eql(expected[1]); + cat.jobIds[0].should.eql(ids[0]); + cat.jobIds[1].should.eql(ids[1]); done(); }); }); @@ -5222,9 +5366,10 @@ describe('relations', function() { it('should find items on scope - verify', function(done) { Category.findOne(function(err, cat) { var expected = [job2.id, job3.id]; - cat.jobIds.should.eql(expected); + cat.jobIds[0].should.eql(expected[0]); + cat.jobIds[1].should.eql(expected[1]); cat.jobs(function(err, jobs) { - should.not.exist(err); + if (err) return done(err); jobs.should.have.length(2); jobs[0].id.should.eql(job2.id); jobs[1].id.should.eql(job3.id); @@ -5233,11 +5378,12 @@ describe('relations', function() { }); }); - it('should find items on scope and ordered them by name DESC', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, + 'should find items on scope and ordered them by name DESC', function(done) { Category.find(function(err, categories) { categories.should.have.length(1); categories[0].jobs({order: 'name DESC'}, function(err, jobs) { - should.not.exist(err); + if (err) return done(err); jobs.should.have.length(2); jobs[0].id.should.eql(job3.id); jobs[1].id.should.eql(job2.id); @@ -5246,18 +5392,36 @@ describe('relations', function() { }); }); - it('should allow custom scope methods - reverse', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, + 'should allow custom scope methods - reverse', function(done) { Category.findOne(function(err, cat) { cat.jobs.reverse(function(err, ids) { var expected = [job3.id, job2.id]; - ids.should.eql(expected); - cat.jobIds.should.eql(expected); + ids[0].should.eql(expected[0]); + ids[1].should.eql(expected[1]); + cat.jobIds[0].should.eql(expected[0]); + cat.jobIds[1].should.eql(expected[1]); done(); }); }); }); - it('should include related items from scope', function(done) { + bdd.itIf(connectorCapabilities.adhocSort === false, + 'should allow custom scope methods - reverse', function(done) { + Category.findOne(function(err, cat) { + cat.jobs.reverse(function(err, ids) { + var expected = [job3.id, job2.id]; + ids[0].should.be.oneOf(expected); + ids[1].should.be.oneOf(expected); + cat.jobIds[0].should.be.oneOf(expected); + cat.jobIds[1].should.be.oneOf(expected); + done(); + }); + }); + }); + + bdd.itIf(connectorCapabilities.supportInclude !== false, + 'should include related items from scope', function(done) { Category.find({include: 'jobs'}, function(err, categories) { categories.should.have.length(1); var cat = categories[0].toObject(); @@ -5272,11 +5436,11 @@ describe('relations', function() { it('should destroy items from scope - destroyById', function(done) { Category.findOne(function(err, cat) { cat.jobs.destroy(job2.id, function(err) { - should.not.exist(err); + if (err) return done(err); var expected = [job3.id]; cat.jobIds.should.eql(expected); Job.exists(job2.id, function(err, exists) { - should.not.exist(err); + if (err) return done(err); should.exist(exists); exists.should.be.false; done(); @@ -5290,7 +5454,7 @@ describe('relations', function() { var expected = [job3.id]; cat.jobIds.should.eql(expected); cat.jobs(function(err, jobs) { - should.not.exist(err); + if (err) return done(err); jobs.should.have.length(1); jobs[0].id.should.eql(job3.id); done(); @@ -5343,7 +5507,8 @@ describe('relations', function() { }); }); - it('should find items on scope with promises', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, + 'should find items on scope with promises', function(done) { Category.findOne() .then(function(cat) { cat.jobIds.should.eql([job2.id]); @@ -5358,6 +5523,24 @@ describe('relations', function() { .catch(done); }); + bdd.itIf(connectorCapabilities.adhocSort === false, + 'should find items on scope with promises', function(done) { + var theExpectedIds = [job1.id, job2.id, job3.id]; + var theExpectedNames = ['Job 1', 'Job 2', 'Job 3']; + Category.findOne() + .then(function(cat) { + cat.jobIds[0].should.be.oneOf(theExpectedIds); + return cat.jobs.getAsync(); + }) + .then(function(jobs) { + var p = jobs[0]; + p.id.should.be.oneOf(theExpectedIds); + p.name.should.be.oneOf(theExpectedNames); + done(); + }) + .catch(done); + }); + it('should find items on scope with promises - findById', function(done) { Category.findOne() .then(function(cat) { @@ -5476,7 +5659,7 @@ describe('relations', function() { .then(function(ids) { var expected = [job2.id, job3.id]; cat.jobIds.should.eql(expected); - ids.should.eql(cat.jobIds); + cat.jobIds.should.eql(ids); done(); }); }) @@ -5499,7 +5682,8 @@ describe('relations', function() { .catch(done); }); - it('should find items on scope and ordered them by name DESC', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, + 'should find items on scope and ordered them by name DESC', function(done) { Category.find() .then(function(categories) { categories.should.have.length(1); @@ -5514,7 +5698,23 @@ describe('relations', function() { .catch(done); }); - it('should allow custom scope methods with promises - reverse', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false, + 'should allow custom scope methods with promises - reverse', function(done) { + Category.findOne() + .then(function(cat) { + return cat.jobs.reverse() + .then(function(ids) { + var expected = [job3.id, job2.id]; + cat.jobIds.should.eql(expected); + cat.jobIds.should.eql(ids); + done(); + }); + }) + .catch(done); + }); + + bdd.itIf(connectorCapabilities.adhocSort === false, + 'should allow custom scope methods with promises - reverse', function(done) { Category.findOne() .then(function(cat) { return cat.jobs.reverse() @@ -5528,7 +5728,9 @@ describe('relations', function() { .catch(done); }); - it('should include related items from scope with promises', function(done) { + bdd.itIf(connectorCapabilities.adhocSort !== false && + connectorCapabilities.supportInclude !== false, + 'should include related items from scope with promises', function(done) { Category.find({include: 'jobs'}) .then(function(categories) { categories.should.have.length(1); @@ -5541,13 +5743,29 @@ describe('relations', function() { }).catch(done); }); + bdd.itIf(connectorCapabilities.adhocSort === false && + connectorCapabilities.supportInclude !== false, + 'should include related items from scope with promises', function(done) { + Category.find({include: 'jobs'}) + .then(function(categories) { + categories.should.have.length(1); + var cat = categories[0].toObject(); + cat.name.should.equal('Category A'); + cat.jobIds.should.have.length(2); + var theExpectedIds = [job1.id, job2.id, job3.id]; + cat.jobIds[0].id.should.be.oneOf(theExpectedIds); + cat.jobIds[1].id.should.be.oneOf(theExpectedIds); + done(); + }).catch(done); + }); + it('should destroy items from scope with promises - destroyById', function(done) { Category.findOne() .then(function(cat) { return cat.jobs.destroy(job2.id) .then(function() { var expected = [job3.id]; - cat.jobIds.should.eql(expected); + cat.jobIds[0].should.eql(expected[0]); return Job.exists(job2.id); }) .then(function(exists) { @@ -5579,7 +5797,7 @@ describe('relations', function() { var categoryId; before(function(done) { - // db = getSchema(); + db = getSchema(); Category = db.define('Category', {name: String}); Job = db.define('Job', {name: String}); @@ -5628,28 +5846,33 @@ describe('relations', function() { }); it('should allow custom scope methods - summarize', function(done) { + var categoryIdStr = categoryId.toString(); var expected = [ - {name: 'Job 1', categoryId: categoryId, categoryName: 'Category A'}, - {name: 'Job 2', categoryId: categoryId, categoryName: 'Category A'}, + {name: 'Job 1', categoryId: categoryIdStr, categoryName: 'Category A'}, + {name: 'Job 2', categoryId: categoryIdStr, categoryName: 'Category A'}, ]; Category.findOne(function(err, cat) { cat.jobs.summarize(function(err, summary) { - should.not.exist(err); + if (err) return done(err); var result = summary.map(function(item) { delete item.id; + item.categoryId = item.categoryId.toString(); return item; }); - result.should.eql(expected); + // order-independent match + result.should.containDeep(expected); + expected.should.containDeep(result); done(); }); }); }); it('should allow custom scope methods with promises - summarize', function(done) { + var categoryIdStr = categoryId.toString(); var expected = [ - {name: 'Job 1', categoryId: categoryId, categoryName: 'Category A'}, - {name: 'Job 2', categoryId: categoryId, categoryName: 'Category A'}, + {name: 'Job 1', categoryId: categoryIdStr, categoryName: 'Category A'}, + {name: 'Job 2', categoryId: categoryIdStr, categoryName: 'Category A'}, ]; Category.findOne() @@ -5659,9 +5882,12 @@ describe('relations', function() { .then(function(summary) { var result = summary.map(function(item) { delete item.id; + item.categoryId = item.categoryId.toString(); return item; }); - result.should.eql(expected); + // order-independent match + result.should.containDeep(expected); + expected.should.containDeep(result); done(); }) .catch(done);