loopback-datasource-juggler-regevbr
Version:
LoopBack DataSoure Juggler
1,542 lines (1,407 loc) • 187 kB
JavaScript
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// This test written in mocha+should.js
var should = require('./init.js');
var assert = require('assert');
var jdb = require('../');
var DataSource = jdb.DataSource;
var createPromiseCallback = require('../lib/utils.js').createPromiseCallback;
var db, tmp, Book, Chapter, Author, Reader;
var Category, Job;
var Picture, PictureLink;
var Person, Address;
var Link;
var getTransientDataSource = function(settings) {
return new DataSource('transient', settings, db.modelBuilder);
};
var getMemoryDataSource = function(settings) {
return new DataSource('memory', settings, db.modelBuilder);
};
describe('relations', function() {
before(function() {
db = getSchema();
});
describe('hasMany', function() {
before(function(done) {
Book = db.define('Book', {name: String, type: String});
Chapter = db.define('Chapter', {name: {type: String, index: true},
bookType: String});
Author = db.define('Author', {name: String});
Reader = db.define('Reader', {name: String});
db.automigrate(['Book', 'Chapter', 'Author', 'Reader'], done);
});
it('can be declared in different ways', function(done) {
Book.hasMany(Chapter);
Book.hasMany(Reader, {as: 'users'});
Book.hasMany(Author, {foreignKey: 'projectId'});
var b = new Book;
b.chapters.should.be.an.instanceOf(Function);
b.users.should.be.an.instanceOf(Function);
b.authors.should.be.an.instanceOf(Function);
Object.keys((new Chapter).toObject()).should.containEql('bookId');
Object.keys((new Author).toObject()).should.containEql('projectId');
db.automigrate(['Book', 'Chapter', 'Author', 'Reader'], done);
});
it('can be declared in short form', function(done) {
Author.hasMany('readers');
(new Author).readers.should.be.an.instanceOf(Function);
Object.keys((new Reader).toObject()).should.containEql('authorId');
db.autoupdate(['Author', 'Reader'], done);
});
describe('with scope', function() {
before(function(done) {
Book.hasMany(Chapter);
done();
});
it('should build record on scope', function(done) {
Book.create(function(err, book) {
var c = book.chapters.build();
c.bookId.should.eql(book.id);
c.save(done);
});
});
it('should create record on scope', function(done) {
Book.create(function(err, book) {
book.chapters.create(function(err, c) {
should.not.exist(err);
should.exist(c);
c.bookId.should.eql(book.id);
done();
});
});
});
it('should not update FK', function(done) {
Book.create(function(err, book) {
book.chapters.create({name: 'chapter 1'}, function(err, c) {
if (err) return done(err);
should.exist(c);
c.bookId.should.eql(book.id);
c.name.should.eql('chapter 1');
book.chapters.updateById(c.id, {name: 'chapter 0', bookId: 10}, function(err, cc) {
should.exist(err);
err.message.should.startWith('Cannot override foreign key');
done();
});
});
});
});
it('should create record on scope with promises', function(done) {
Book.create()
.then (function(book) {
return book.chapters.create()
.then (function(c) {
should.exist(c);
c.bookId.should.eql(book.id);
done();
});
}).catch(done);
});
it('should create a batch of records on scope', function(done) {
var chapters = [
{name: 'a'},
{name: 'z'},
{name: 'c'},
];
Book.create(function(err, book) {
book.chapters.create(chapters, function(err, chs) {
should.not.exist(err);
should.exist(chs);
chs.should.have.lengthOf(chapters.length);
chs.forEach(function(c) {
c.bookId.should.eql(book.id);
});
done();
});
});
});
it('should create a batch of records on scope with promises', function(done) {
var chapters = [
{name: 'a'},
{name: 'z'},
{name: 'c'},
];
Book.create(function(err, book) {
book.chapters.create(chapters)
.then(function(chs) {
should.exist(chs);
chs.should.have.lengthOf(chapters.length);
chs.forEach(function(c) {
c.bookId.should.eql(book.id);
});
done();
}).catch(done);
});
});
it('should fetch all scoped instances', function(done) {
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function() {
book.chapters.create({name: 'z'}, function() {
book.chapters.create({name: 'c'}, function() {
verify(book);
});
});
});
});
function verify(book) {
book.chapters(function(err, ch) {
should.not.exist(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) {
should.not.exist(e);
should.exist(c);
c.shift().name.should.equal('z');
c.pop().name.should.equal('a');
done();
});
});
}
});
it('should fetch all scoped instances with promises', function(done) {
Book.create()
.then(function(book) {
return book.chapters.create({name: 'a'})
.then(function() {
return book.chapters.create({name: 'z'});
})
.then(function() {
return book.chapters.create({name: 'c'});
})
.then(function() {
return verify(book);
});
}).catch(done);
function verify(book) {
return book.chapters.getAsync()
.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'})
.then(function(c) {
should.exist(c);
c.shift().name.should.equal('z');
c.pop().name.should.equal('a');
done();
});
});
}
});
it('should fetch all scoped instances with getAsync with callback and condition', function(done) {
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function() {
book.chapters.create({name: 'z'}, function() {
book.chapters.create({name: 'c'}, function() {
verify(book);
});
});
});
});
function verify(book) {
book.chapters(function(err, ch) {
should.not.exist(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) {
should.not.exist(e);
should.exist(c);
c.shift().name.should.equal('z');
c.pop().name.should.equal('a');
done();
});
});
}
});
it('should fetch all scoped instances with getAsync with callback and no condition', function(done) {
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function() {
book.chapters.create({name: 'z'}, function() {
book.chapters.create({name: 'c'}, function() {
verify(book);
});
});
});
});
function verify(book) {
book.chapters(function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.should.have.lengthOf(3);
var chapters = book.chapters();
chapters.should.eql(ch);
book.chapters.getAsync(function(e, c) {
should.not.exist(e);
should.exist(c);
should.exist(c.length);
c.shift().name.should.equal('a');
c.pop().name.should.equal('c');
done();
});
});
}
});
it('should find scoped record', function(done) {
var id;
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function(err, ch) {
id = ch.id;
book.chapters.create({name: 'z'}, function() {
book.chapters.create({name: 'c'}, function() {
verify(book);
});
});
});
});
function verify(book) {
book.chapters.findById(id, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.eql(id);
done();
});
}
});
it('should find scoped record with promises', function(done) {
var id;
Book.create()
.then(function(book) {
return book.chapters.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return book.chapters.create({name: 'z'});
})
.then(function() {
return book.chapters.create({name: 'c'});
})
.then(function() {
return verify(book);
});
}).catch(done);
function verify(book) {
return book.chapters.findById(id)
.then(function(ch) {
should.exist(ch);
ch.id.should.eql(id);
done();
});
}
});
it('should count scoped records - all and filtered', function(done) {
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function(err, ch) {
book.chapters.create({name: 'b'}, function() {
book.chapters.create({name: 'c'}, function() {
verify(book);
});
});
});
});
function verify(book) {
book.chapters.count(function(err, count) {
should.not.exist(err);
count.should.equal(3);
book.chapters.count({name: 'b'}, function(err, count) {
should.not.exist(err);
count.should.equal(1);
done();
});
});
}
});
it('should count scoped records - all and filtered with promises', function(done) {
Book.create()
.then(function(book) {
book.chapters.create({name: 'a'})
.then(function() {
return book.chapters.create({name: 'b'});
})
.then(function() {
return book.chapters.create({name: 'c'});
})
.then(function() {
return verify(book);
});
}).catch(done);
function verify(book) {
return book.chapters.count()
.then(function(count) {
count.should.equal(3);
return book.chapters.count({name: 'b'});
})
.then(function(count) {
count.should.equal(1);
done();
});
}
});
it('should set targetClass on scope property', function() {
should.equal(Book.prototype.chapters._targetClass, 'Chapter');
});
it('should update scoped record', function(done) {
var id;
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function(err, ch) {
id = ch.id;
book.chapters.updateById(id, {name: 'aa'}, function(err, ch) {
verify(book);
});
});
});
function verify(book) {
book.chapters.findById(id, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.eql(id);
ch.name.should.equal('aa');
done();
});
}
});
it('should update scoped record with promises', function(done) {
var id;
Book.create()
.then(function(book) {
return book.chapters.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return book.chapters.updateById(id, {name: 'aa'});
})
.then(function(ch) {
return verify(book);
});
})
.catch(done);
function verify(book) {
return book.chapters.findById(id)
.then(function(ch) {
should.exist(ch);
ch.id.should.eql(id);
ch.name.should.equal('aa');
done();
});
}
});
it('should destroy scoped record', function(done) {
var id;
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function(err, ch) {
id = ch.id;
book.chapters.destroy(id, function(err, ch) {
verify(book);
});
});
});
function verify(book) {
book.chapters.findById(id, function(err, ch) {
should.exist(err);
done();
});
}
});
it('should destroy scoped record with promises', function(done) {
var id;
Book.create()
.then(function(book) {
return book.chapters.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return book.chapters.destroy(id);
})
.then(function(ch) {
return verify(book);
});
})
.catch(done);
function verify(book) {
return book.chapters.findById(id)
.catch(function(err) {
should.exist(err);
done();
});
}
});
it('should check existence of a scoped record', function(done) {
var id;
Book.create(function(err, book) {
book.chapters.create({name: 'a'}, function(err, ch) {
id = ch.id;
book.chapters.create({name: 'z'}, function() {
book.chapters.create({name: 'c'}, function() {
verify(book);
});
});
});
});
function verify(book) {
book.chapters.exists(id, function(err, flag) {
should.not.exist(err);
flag.should.be.eql(true);
done();
});
}
});
it('should check existence of a scoped record with promises', function(done) {
var id;
Book.create()
.then(function(book) {
return book.chapters.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return book.chapters.create({name: 'z'});
})
.then(function() {
return book.chapters.create({name: 'c'});
})
.then(function() {
return verify(book);
});
}).catch(done);
function verify(book) {
return book.chapters.exists(id)
.then(function(flag) {
flag.should.be.eql(true);
done();
});
}
});
it('should check ignore related data on creation - array', function(done) {
Book.create({chapters: []}, function(err, book) {
should.not.exist(err);
book.chapters.should.be.a.function;
var obj = book.toObject();
should.not.exist(obj.chapters);
done();
});
});
it('should check ignore related data on creation with promises - array', function(done) {
Book.create({chapters: []})
.then(function(book) {
book.chapters.should.be.a.function;
var obj = book.toObject();
should.not.exist(obj.chapters);
done();
}).catch(done);
});
it('should check ignore related data on creation - object', function(done) {
Book.create({chapters: {}}, function(err, book) {
should.not.exist(err);
book.chapters.should.be.a.function;
var obj = book.toObject();
should.not.exist(obj.chapters);
done();
});
});
it('should check ignore related data on creation with promises - object', function(done) {
Book.create({chapters: {}})
.then(function(book) {
book.chapters.should.be.a.function;
var obj = book.toObject();
should.not.exist(obj.chapters);
done();
}).catch(done);
});
});
});
describe('hasMany through', function() {
var Physician, Patient, Appointment, Address;
before(function(done) {
// db = getSchema();
Physician = db.define('Physician', {name: String});
Patient = db.define('Patient', {name: String, age: Number});
Appointment = db.define('Appointment', {date: {type: Date,
default: function() {
return new Date();
}}});
Address = db.define('Address', {name: String});
Physician.hasMany(Patient, {through: Appointment});
Patient.hasMany(Physician, {through: Appointment});
Patient.belongsTo(Address);
Appointment.belongsTo(Patient);
Appointment.belongsTo(Physician);
db.automigrate(['Physician', 'Patient', 'Appointment', 'Address'], done);
});
it('should build record on scope', function(done) {
Physician.create(function(err, physician) {
var patient = physician.patients.build();
patient.physicianId.should.eql(physician.id);
patient.save(done);
});
});
it('should create record on scope', function(done) {
Physician.create(function(err, physician) {
physician.patients.create(function(err, patient) {
should.not.exist(err);
should.exist(patient);
Appointment.find({where: {physicianId: physician.id, patientId: patient.id}},
function(err, apps) {
should.not.exist(err);
apps.should.have.lengthOf(1);
done();
});
});
});
});
it('should create record on scope with promises', function(done) {
Physician.create()
.then(function(physician) {
return physician.patients.create()
.then(function(patient) {
should.exist(patient);
return Appointment.find({where: {physicianId: physician.id, patientId: patient.id}})
.then(function(apps) {
apps.should.have.lengthOf(1);
done();
});
});
}).catch(done);
});
it('should create multiple records on scope', function(done) {
var async = require('async');
Physician.create(function(err, physician) {
physician.patients.create([{}, {}], function(err, patients) {
should.not.exist(err);
should.exist(patients);
patients.should.have.lengthOf(2);
function verifyPatient(patient, next) {
Appointment.find({where: {
physicianId: physician.id,
patientId: patient.id,
}},
function(err, apps) {
should.not.exist(err);
apps.should.have.lengthOf(1);
next();
});
}
async.forEach(patients, verifyPatient, done);
});
});
});
it('should create multiple records on scope with promises', function(done) {
var async = require('async');
Physician.create()
.then(function(physician) {
return physician.patients.create([{}, {}])
.then(function(patients) {
should.exist(patients);
patients.should.have.lengthOf(2);
function verifyPatient(patient, next) {
Appointment.find({where: {
physicianId: physician.id,
patientId: patient.id,
}})
.then(function(apps) {
apps.should.have.lengthOf(1);
next();
});
}
async.forEach(patients, verifyPatient, done);
});
}).catch(done);
});
it('should fetch all scoped instances', function(done) {
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function() {
physician.patients.create({name: 'z'}, function() {
physician.patients.create({name: 'c'}, function() {
verify(physician);
});
});
});
});
function verify(physician) {
physician.patients(function(err, ch) {
var patients = physician.patients();
patients.should.eql(ch);
should.not.exist(err);
should.exist(ch);
ch.should.have.lengthOf(3);
done();
});
}
});
it('should fetch all scoped instances with promises', function(done) {
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function() {
return physician.patients.create({name: 'z'});
})
.then(function() {
return physician.patients.create({name: 'c'});
})
.then(function() {
return verify(physician);
});
}).catch(done);
function verify(physician) {
return physician.patients.getAsync()
.then(function(ch) {
var patients = physician.patients();
patients.should.eql(ch);
should.exist(ch);
ch.should.have.lengthOf(3);
done();
});
}
});
describe('fetch scoped instances with paging filters', function() {
var samplePatientId;
var physician;
beforeEach(createSampleData);
context('with filter skip', function() {
it('skips the first patient', function(done) {
physician.patients({skip: 1}, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.should.have.lengthOf(2);
ch[0].name.should.eql('z');
ch[1].name.should.eql('c');
done();
});
});
});
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);
should.exist(ch);
ch.should.have.lengthOf(3);
ch[0].name.should.eql('z');
ch[1].name.should.eql('c');
ch[2].name.should.eql('a');
done();
});
});
});
context('with filter limit', function() {
it('limits to 1 result', function(done) {
physician.patients({limit: 1}, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.should.have.lengthOf(1);
ch[0].name.should.eql('a');
done();
});
});
});
context('with filter fields', 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);
should.exist(ch);
should.exist(ch[0].name);
ch[0].name.should.eql('a');
should.not.exist(ch[0].age);
done();
});
});
});
context('with filter include', function() {
it('returns physicians included in patient', function(done) {
var includeFilter = {include: 'physicians'};
physician.patients(includeFilter, function(err, ch) {
should.not.exist(err);
ch.should.have.lengthOf(3);
var includedInstances = ch[0].toObject().physicians;
includedInstances.should.have.lengthOf(1);
includedInstances[0].id.should.eql(physician.id);
done();
});
});
});
context('with filter where', function() {
it('returns patient where id equal to samplePatientId', function(done) {
var whereFilter = {where: {id: samplePatientId}};
physician.patients(whereFilter, function(err, ch) {
should.not.exist(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) {
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);
should.exist(ch);
ch.should.have.lengthOf(2);
var resultIdArr = [ch[0].id, ch[1].id];
assert.deepEqual(resultIdArr, idArr);
done();
});
});
});
});
context('findById with filter include', function() {
it('returns patient where id equal to \'samplePatientId\'' +
'with included physicians', function(done) {
var includeFilter = {include: 'physicians'};
physician.patients.findById(samplePatientId,
includeFilter, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.eql(samplePatientId);
should.exist(ch.physicians);
done();
});
});
});
context('findById with filter fields', function() {
it('returns patient where id equal to \'samplePatientId\'' +
'with field \'name\' but not \'age\'', function(done) {
var fieldsFilter = {fields: {name: true, age: false}};
physician.patients.findById(samplePatientId,
fieldsFilter, function(err, ch) {
should.not.exist(err);
should.exist(ch);
should.exist(ch.name);
ch.name.should.eql('a');
should.not.exist(ch.age);
done();
});
});
});
function createSampleData(done) {
Physician.create(function(err, result) {
result.patients.create({name: 'a', age: '10'}, function(err, p) {
samplePatientId = p.id;
result.patients.create({name: 'z', age: '20'}, function() {
result.patients.create({name: 'c'}, function() {
physician = result;
done();
});
});
});
});
};
});
it('should find scoped record', function(done) {
var id;
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, ch) {
id = ch.id;
physician.patients.create({name: 'z'}, function() {
physician.patients.create({name: 'c'}, function() {
verify(physician);
});
});
});
});
function verify(physician) {
physician.patients.findById(id, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.eql(id);
done();
});
}
});
it('should find scoped record with promises', function(done) {
var id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.create({name: 'z'});
})
.then(function() {
return physician.patients.create({name: 'c'});
})
.then(function() {
return verify(physician);
});
}).catch(done);
function verify(physician) {
return physician.patients.findById(id, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.eql(id);
done();
});
}
});
it('should allow to use include syntax on related data', function(done) {
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, patient) {
Address.create({name: 'z'}, function(err, address) {
should.not.exist(err);
patient.address(address);
patient.save(function() {
verify(physician, address.id);
});
});
});
});
function verify(physician, addressId) {
physician.patients({include: 'address'}, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.should.have.lengthOf(1);
ch[0].addressId.should.eql(addressId);
var address = ch[0].address();
should.exist(address);
address.should.be.an.instanceof(Address);
address.name.should.equal('z');
done();
});
}
});
it('should allow to use include syntax on related data with promises', function(done) {
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(patient) {
return Address.create({name: 'z'})
.then(function(address) {
patient.address(address);
return patient.save()
.then(function() {
return verify(physician, address.id);
});
});
});
}).catch(done);
function verify(physician, addressId) {
return physician.patients.getAsync({include: 'address'})
.then(function(ch) {
should.exist(ch);
ch.should.have.lengthOf(1);
ch[0].addressId.should.eql(addressId);
var address = ch[0].address();
should.exist(address);
address.should.be.an.instanceof(Address);
address.name.should.equal('z');
done();
});
}
});
it('should set targetClass on scope property', function() {
should.equal(Physician.prototype.patients._targetClass, 'Patient');
});
it('should update scoped record', function(done) {
var id;
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, ch) {
id = ch.id;
physician.patients.updateById(id, {name: 'aa'}, function(err, ch) {
verify(physician);
});
});
});
function verify(physician) {
physician.patients.findById(id, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.id.should.eql(id);
ch.name.should.equal('aa');
done();
});
}
});
it('should update scoped record with promises', function(done) {
var id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.updateById(id, {name: 'aa'})
.then(function(ch) {
return verify(physician);
});
});
}).catch(done);
function verify(physician) {
return physician.patients.findById(id)
.then(function(ch) {
should.exist(ch);
ch.id.should.eql(id);
ch.name.should.equal('aa');
done();
});
}
});
it('should destroy scoped record', function(done) {
var id;
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, ch) {
id = ch.id;
physician.patients.destroy(id, function(err, ch) {
verify(physician);
});
});
});
function verify(physician) {
physician.patients.findById(id, function(err, ch) {
should.exist(err);
done();
});
}
});
it('should destroy scoped record with promises', function(done) {
var id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.destroy(id)
.then(function(ch) {
return verify(physician);
});
});
}).catch(done);
function verify(physician) {
return physician.patients.findById(id)
.then(function(ch) {
should.not.exist(ch);
done();
})
.catch(function(err) {
should.exist(err);
done();
});
}
});
it('should check existence of a scoped record', function(done) {
var id;
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, ch) {
should.not.exist(err);
id = ch.id;
physician.patients.create({name: 'z'}, function() {
physician.patients.create({name: 'c'}, function() {
verify(physician);
});
});
});
});
function verify(physician) {
physician.patients.exists(id, function(err, flag) {
should.not.exist(err);
flag.should.be.eql(true);
done();
});
}
});
it('should check existence of a scoped record with promises', function(done) {
var id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(ch) {
id = ch.id;
return physician.patients.create({name: 'z'});
})
.then(function() {
return physician.patients.create({name: 'c'});
})
.then(function() {
return verify(physician);
});
}).catch(done);
function verify(physician) {
return physician.patients.exists(id)
.then(function(flag) {
flag.should.be.eql(true);
done();
});
}
});
it('should allow to add connection with instance', function(done) {
Physician.create({name: 'ph1'}, function(e, physician) {
Patient.create({name: 'pa1'}, function(e, patient) {
physician.patients.add(patient, function(e, app) {
should.not.exist(e);
should.exist(app);
app.should.be.an.instanceOf(Appointment);
app.physicianId.should.eql(physician.id);
app.patientId.should.eql(patient.id);
done();
});
});
});
});
it('should allow to add connection with instance with promises', function(done) {
Physician.create({name: 'ph1'})
.then(function(physician) {
return Patient.create({name: 'pa1'})
.then(function(patient) {
return physician.patients.add(patient)
.then(function(app) {
should.exist(app);
app.should.be.an.instanceOf(Appointment);
app.physicianId.should.eql(physician.id);
app.patientId.should.eql(patient.id);
done();
});
});
}).catch(done);
});
it('should allow to add connection with through data', function(done) {
Physician.create({name: 'ph1'}, function(e, physician) {
Patient.create({name: 'pa1'}, function(e, patient) {
var now = Date.now();
physician.patients.add(patient, {date: new Date(now)}, function(e, app) {
should.not.exist(e);
should.exist(app);
app.should.be.an.instanceOf(Appointment);
app.physicianId.should.eql(physician.id);
app.patientId.should.eql(patient.id);
app.patientId.should.eql(patient.id);
app.date.getTime().should.equal(now);
done();
});
});
});
});
it('should allow to add connection with through data with promises', function(done) {
Physician.create({name: 'ph1'})
.then(function(physician) {
return Patient.create({name: 'pa1'})
.then(function(patient) {
var now = Date.now();
return physician.patients.add(patient, {date: new Date(now)})
.then(function(app) {
should.exist(app);
app.should.be.an.instanceOf(Appointment);
app.physicianId.should.eql(physician.id);
app.patientId.should.eql(patient.id);
app.patientId.should.eql(patient.id);
app.date.getTime().should.equal(now);
done();
});
});
}).catch(done);
});
it('should allow to remove connection with instance', function(done) {
var id;
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, patient) {
id = patient.id;
physician.patients.remove(id, function(err, ch) {
verify(physician);
});
});
});
function verify(physician) {
physician.patients.exists(id, function(err, flag) {
should.not.exist(err);
flag.should.be.eql(false);
done();
});
}
});
it('should allow to remove connection with instance with promises', function(done) {
var id;
Physician.create()
.then(function(physician) {
return physician.patients.create({name: 'a'})
.then(function(patient) {
id = patient.id;
return physician.patients.remove(id)
.then(function(ch) {
return verify(physician);
});
});
}).catch(done);
function verify(physician) {
return physician.patients.exists(id)
.then(function(flag) {
flag.should.be.eql(false);
done();
});
}
});
beforeEach(function(done) {
Appointment.destroyAll(function(err) {
Physician.destroyAll(function(err) {
Patient.destroyAll(done);
});
});
});
});
describe('hasMany through - collect', function() {
var Physician, Patient, Appointment, Address;
beforeEach(function(done) {
// db = getSchema();
Physician = db.define('Physician', {name: String});
Patient = db.define('Patient', {name: String});
Appointment = db.define('Appointment', {date: {type: Date,
default: function() {
return new Date();
}}});
Address = db.define('Address', {name: String});
db.automigrate(['Physician', 'Patient', 'Appointment', 'Address'], done);
});
describe('with default options', function() {
it('can determine the collect by modelTo\'s name as default', function() {
Physician.hasMany(Patient, {through: Appointment});
Patient.hasMany(Physician, {through: Appointment, as: 'yyy'});
Patient.belongsTo(Address);
Appointment.belongsTo(Physician);
Appointment.belongsTo(Patient);
var physician = new Physician({id: 1});
var scope1 = physician.patients._scope;
scope1.should.have.property('collect', 'patient');
scope1.should.have.property('include', 'patient');
var patient = new Patient({id: 1});
var scope2 = patient.yyy._scope;
scope2.should.have.property('collect', 'physician');
scope2.should.have.property('include', 'physician');
});
});
describe('when custom reverse belongsTo names for both sides', function() {
it('can determine the collect via keyThrough', function() {
Physician.hasMany(Patient, {
through: Appointment,
foreignKey: 'fooId',
keyThrough: 'barId',
});
Patient.hasMany(Physician, {
through: Appointment,
foreignKey: 'barId',
keyThrough: 'fooId',
as: 'yyy',
});
Appointment.belongsTo(Physician, {as: 'foo'});
Appointment.belongsTo(Patient, {as: 'bar'});
Patient.belongsTo(Address); // jam.
Appointment.belongsTo(Patient, {as: 'car'}); // jam. Should we complain in this case???
var physician = new Physician({id: 1});
var scope1 = physician.patients._scope;
scope1.should.have.property('collect', 'bar');
scope1.should.have.property('include', 'bar');
var patient = new Patient({id: 1});
var scope2 = patient.yyy._scope;
scope2.should.have.property('collect', 'foo');
scope2.should.have.property('include', 'foo');
});
it('can determine the collect via modelTo name', function() {
Physician.hasMany(Patient, {through: Appointment});
Patient.hasMany(Physician, {through: Appointment, as: 'yyy'});
Appointment.belongsTo(Physician, {as: 'foo', foreignKey: 'physicianId'});
Appointment.belongsTo(Patient, {as: 'bar', foreignKey: 'patientId'});
Patient.belongsTo(Address); // jam.
var physician = new Physician({id: 1});
var scope1 = physician.patients._scope;
scope1.should.have.property('collect', 'bar');
scope1.should.have.property('include', 'bar');
var patient = new Patient({id: 1});
var scope2 = patient.yyy._scope;
scope2.should.have.property('collect', 'foo');
scope2.should.have.property('include', 'foo');
});
it('can determine the collect via modelTo name (with jams)', function() {
Physician.hasMany(Patient, {through: Appointment});
Patient.hasMany(Physician, {through: Appointment, as: 'yyy'});
Appointment.belongsTo(Physician, {as: 'foo', foreignKey: 'physicianId'});
Appointment.belongsTo(Patient, {as: 'bar', foreignKey: 'patientId'});
Patient.belongsTo(Address); // jam.
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 scope1 = physician.patients._scope;
scope1.should.have.property('collect', 'bar');
scope1.should.have.property('include', 'bar');
var patient = new Patient({id: 1});
var scope2 = patient.yyy._scope;
scope2.should.have.property('collect', 'foo'); // first matched relation
scope2.should.have.property('include', 'foo'); // first matched relation
});
});
describe('when custom reverse belongsTo name for one side only', function() {
beforeEach(function() {
Physician.hasMany(Patient, {as: 'xxx', through: Appointment, foreignKey: 'fooId'});
Patient.hasMany(Physician, {as: 'yyy', through: Appointment, keyThrough: 'fooId'});
Appointment.belongsTo(Physician, {as: 'foo'});
Appointment.belongsTo(Patient);
Patient.belongsTo(Address); // jam.
Appointment.belongsTo(Physician, {as: 'bar'}); // jam. Should we complain in this case???
});
it('can determine the collect via model name', function() {
var physician = new Physician({id: 1});
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 scope2 = patient.yyy._scope;
scope2.should.have.property('collect', 'foo');
scope2.should.have.property('include', 'foo');
});
});
});
describe('hasMany through bi-directional relations on the same model', function() {
var User, Follow, Address;
before(function(done) {
// db = getSchema();
User = db.define('User', {name: String});
Follow = db.define('Follow', {date: {type: Date,
default: function() {
return new Date();
}}});
Address = db.define('Address', {name: String});
User.hasMany(User, {
as: 'followers',
foreignKey: 'followeeId',
keyThrough: 'followerId',
through: Follow,
});
User.hasMany(User, {
as: 'following',
foreignKey: 'followerId',
keyThrough: 'followeeId',
through: Follow,
});
User.belongsTo(Address);
Follow.belongsTo(User, {as: 'follower'});
Follow.belongsTo(User, {as: 'followee'});
db.automigrate(['User', 'Follow'], done);
});
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});
followee.followers.add(follower, function(err, throughInst) {
should.not.exist(err);
should.exist(throughInst);
throughInst.followerId.should.eql(follower.id);
throughInst.followeeId.should.eql(followee.id);
done();
});
});
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});
follower.following.add(followee, function(err, throughInst) {
should.not.exist(err);
should.exist(throughInst);
throughInst.followeeId.should.eql(followee.id);
throughInst.followerId.should.eql(follower.id);
done();
});
});
});
describe('hasMany through - between same models', function() {
var User, Follow, Address;
before(function(done) {
// db = getSchema();
User = db.define('User', {name: String});
Follow = db.define('Follow', {date: {type: Date,
default: function() {
return new Date();
}}});
Address = db.define('Address', {name: String});
User.hasMany(User, {
as: 'followers',
foreignKey: 'followeeId',
keyThrough: 'followerId',
through: Follow,
});
User.hasMany(User, {
as: 'following',
foreignKey: 'followerId',
keyThrough: 'followeeId',
through: Follow,
});
User.belongsTo(Address);
Follow.belongsTo(User, {as: 'follower'});
Follow.belongsTo(User, {as: 'followee'});
db.automigrate(['User', 'Follow', 'Address'], done);
});
it('should set the keyThrough and the foreignKey', function(done) {
var user = new User({id: 1});
var user2 = new User({id: 2});
user.following.add(user2, function(err, f) {
should.not.exist(err);
should.exist(f);
f.followeeId.should.eql(user2.id);
f.followerId.should.eql(user.id);
done();
});
});
it('can determine the collect via keyThrough for each side', function() {
var user = new User({id: 1});
var scope1 = user.followers._scope;
scope1.should.have.property('collect', 'follower');
scope1.should.have.property('include', 'follower');
var scope2 = user.following._scope;
scope2.should.have.property('collect', 'followee');
scope2.should.have.property('include', 'followee');
});
});
describe('hasMany with properties', function() {
it('can be declared with properties', function(done) {
Book.hasMany(Chapter, {properties: {type: 'bookType'}});
db.automigrate(['Book', 'Chapter'], done);
});
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);
should.exist(c);
c.bookId.should.eql(book.id);
c.bookType.should.equal('fiction');
done();
});
});
});
it('should create record on scope with promises', function(done) {
Book.create({type: 'fiction'})
.then(function(book) {
return book.chapters.create()
.then(function(c) {
should.exist(c);
c.bookId.should.eql(book.id);
c.bookType.should.equal('fiction');
done();
});
}).catch(done);
});
});
describe('hasMany with scope and properties', function() {
it('can be declared with properties', function(done) {
// db = getSchema();
Category = db.define('Category', {name: String, jobType: String});
Job = db.define('Job', {name: String, type: String});
Category.hasMany(Job, {
properties: function(inst, target) {
if (!inst.jobType) return; // skip
return {type: inst.jobType};
},
scope: function(inst, filter) {
var m = this.properties(inst); // re-use properties
if (m) return {where: m};
},
});
db.automigrate(['Category', 'Job'], done);
});
it('should create record on scope', function(done) {
Category.create(function(err, c) {
should.not.exists(err);
c.jobs.create({type: 'book'}, function(err, p) {
should.not.exists(err);
p.categoryId.should.eql(c.id);
p.type.should.equal('book');
c.jobs.create({type: 'widget'}, function(err, p) {
should.not.exists(err);
p.categoryId.should.eql(c.id);