loopback-datasource-juggler
Version:
LoopBack DataSource Juggler
1,469 lines (1,354 loc) • 227 kB
JavaScript
// Copyright IBM Corp. 2013,2019. 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
// This test written in mocha+should.js
'use strict';
/* global getSchema:false, connectorCapabilities:false */
const assert = require('assert');
const bdd = require('./helpers/bdd-if');
const should = require('./init.js');
const uid = require('./helpers/uid-generator');
const jdb = require('../');
const DataSource = jdb.DataSource;
const createPromiseCallback = require('../lib/utils.js').createPromiseCallback;
let db, tmp, Book, Chapter, Author, Reader, Article, Employee;
let Category, Job;
let Picture, PictureLink;
let Person, Address;
let Link;
const getTransientDataSource = function(settings) {
return new DataSource('transient', settings, db.modelBuilder);
};
const 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'});
const 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 = 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');
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) {
const chaps = book.chapters;
const c = chaps.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) {
if (err) return done(err);
should.exist(c);
c.bookId.should.eql(book.id);
done(err);
});
});
});
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) {
const chapters = [
{name: 'a'},
{name: 'z'},
{name: 'c'},
];
Book.create(function(err, book) {
book.chapters.create(chapters, function(err, chs) {
if (err) return done(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) {
const 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) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(3);
const chapters = book.chapters();
chapters.should.eql(ch);
book.chapters(function(e, c) {
should.not.exist(e);
should.exist(c);
ch.should.have.lengthOf(3);
const acz = ['a', 'c', 'z'];
acz.should.containEql(c[0].name);
acz.should.containEql(c[1].name);
acz.should.containEql(c[2].name);
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.find()
.then(function(ch) {
should.exist(ch);
ch.should.have.lengthOf(3);
const chapters = book.chapters();
chapters.should.eql(ch);
return book.chapters.find()
.then(function(c) {
should.exist(c);
ch.should.have.lengthOf(3);
const acz = ['a', 'c', 'z'];
acz.should.containEql(c[0].name);
acz.should.containEql(c[1].name);
acz.should.containEql(c[2].name);
done();
});
});
}
});
it('should fetch all scoped instances with find() 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) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(3);
const chapters = book.chapters();
chapters.should.eql(ch);
book.chapters.find(function(e, c) {
should.not.exist(e);
should.exist(c);
ch.should.have.lengthOf(3);
const acz = ['a', 'c', 'z'];
acz.should.containEql(c[0].name);
acz.should.containEql(c[1].name);
acz.should.containEql(c[2].name);
done();
});
});
}
});
it('should fetch all scoped instances with find() 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) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(3);
const chapters = book.chapters();
chapters.should.eql(ch);
book.chapters.find(function(e, c) {
should.not.exist(e);
should.exist(c);
should.exist(c.length);
c.should.have.lengthOf(3);
const acz = ['a', 'c', 'z'];
acz.should.containEql(c[0].name);
acz.should.containEql(c[1].name);
acz.should.containEql(c[2].name);
done();
});
});
}
});
it('should find scoped record', function(done) {
let 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) {
if (err) return done(err);
should.exist(ch);
ch.id.should.eql(id);
done();
});
}
});
it('should find scoped record with promises', function(done) {
let 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) {
if (err) return done(err);
count.should.equal(3);
book.chapters.count({name: 'b'}, function(err, count) {
if (err) return done(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) {
let 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) {
if (err) return done(err);
should.exist(ch);
ch.id.should.eql(id);
ch.name.should.equal('aa');
done();
});
}
});
it('should update scoped record with promises', function(done) {
let 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) {
let 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) {
let 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) {
let 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) {
if (err) return done(err);
flag.should.be.eql(true);
done();
});
}
});
it('should check existence of a scoped record with promises', function(done) {
let 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) {
if (err) return done(err);
book.chapters.should.be.a.function;
const 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;
const 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) {
if (err) return done(err);
book.chapters.should.be.a.function;
const 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;
const obj = book.toObject();
should.not.exist(obj.chapters);
done();
}).catch(done);
});
});
});
describe('hasMany through', function() {
let Physician, Patient, Appointment, Address;
before(function(done) {
Physician = db.define('Physician', {name: String});
Patient = db.define('Patient', {name: String, age: Number, realm: String,
sequence: {type: Number, index: true}});
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) {
const 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) {
if (err) return done(err);
should.exist(patient);
Appointment.find({where: {physicianId: physician.id, patientId: patient.id}},
function(err, apps) {
if (err) return done(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) {
const async = require('async');
Physician.create(function(err, physician) {
physician.patients.create([{}, {}], function(err, patients) {
if (err) return done(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) {
if (err) return done(err);
apps.should.have.lengthOf(1);
next();
});
}
async.forEach(patients, verifyPatient, done);
});
});
});
it('should create multiple records on scope with promises', function(done) {
const 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) {
const patients = physician.patients();
patients.should.eql(ch);
if (err) return done(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.find()
.then(function(ch) {
const patients = physician.patients();
should.equal(patients, ch);
should.exist(ch);
ch.should.have.lengthOf(3);
done();
});
}
});
describe('fetch scoped instances with paging filters', function() {
let samplePatientId;
let physician;
beforeEach(createSampleData);
context('with filter skip', function() {
bdd.itIf(connectorCapabilities.supportPagination !== false,
'skips the first patient', function(done) {
physician.patients({skip: 1, order: 'sequence'}, function(err, ch) {
if (err) return done(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) {
const 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);
if (connectorCapabilities.adhocSort !== false) {
ch[0].name.should.eql('z');
ch[1].name.should.eql('c');
ch[2].name.should.eql('a');
} else {
const 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();
});
});
});
context('with filter limit', function() {
it('limits to 1 result', function(done) {
physician.patients({limit: 1, order: 'sequence'}, function(err, ch) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(1);
if (connectorCapabilities.adhocSort !== false) {
ch[0].name.should.eql('a');
} else {
ch[0].name.should.be.oneOf(['a', 'c', 'z']);
}
done();
});
});
});
context('with filter fields', function() {
it('includes field \'name\' but not \'age\'', function(done) {
const fieldsFilter = {
fields: {name: true, age: false},
order: 'sequence',
};
physician.patients(fieldsFilter, function(err, ch) {
if (err) return done(err);
should.exist(ch);
should.exist(ch[0].name);
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 included in patient', function(done) {
const includeFilter = {include: 'physicians'};
physician.patients(includeFilter, function(err, ch) {
if (err) return done(err);
ch.should.have.lengthOf(3);
should.exist(ch[0].physicians);
done();
});
});
});
context('with filter where', function() {
it('returns patient where id equal to samplePatientId', function(done) {
findByFlexibleId(
physician.patients.bind(physician),
samplePatientId,
function(err, ch) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(1);
(ch[0].id || ch[0]._id).should.eql(samplePatientId);
done();
},
);
});
it('returns patient where name equal to samplePatient name', function(done) {
const whereFilter = {where: {name: 'a'}};
physician.patients(whereFilter, function(err, ch) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(1);
ch[0].name.should.eql('a');
done();
});
});
it('returns patients where id in an array', function(done) {
const idArr = [];
let 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) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(2);
if (typeof idArr[0] === 'object') {
// mongodb returns `id` as an object
idArr[0] = idArr[0].toString();
idArr[1] = idArr[1].toString();
idArr.indexOf(ch[0].id.toString()).should.not.equal(-1);
idArr.indexOf(ch[1].id.toString()).should.not.equal(-1);
} else {
idArr.indexOf(ch[0].id).should.not.equal(-1);
idArr.indexOf(ch[1].id).should.not.equal(-1);
}
done();
});
});
});
it('returns empty result when patientId does not belongs to physician', function(done) {
Patient.create({name: 'x'}, function(err, p) {
if (err) return done(err);
should.exist(p);
const wrongWhereFilter = {where: {id: p.id}};
physician.patients(wrongWhereFilter, function(err, ch) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(0);
done();
});
});
});
});
context('findById with filter include', function() {
it('returns patient where id equal to \'samplePatientId\'' +
'with included physicians', function(done) {
const includeFilter = {include: 'physicians'};
physician.patients.findById(samplePatientId,
includeFilter, function(err, ch) {
if (err) return done(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) {
const fieldsFilter = {fields: {name: true, age: false}};
physician.patients.findById(samplePatientId,
fieldsFilter, function(err, ch) {
if (err) return done(err);
should.exist(ch);
should.exist(ch.name);
ch.name.should.eql('a');
should.not.exist(ch.age);
done();
});
});
});
context('findById with include filter that contains string fields', function() {
it('should accept string and convert it to array', function(done) {
const includeFilter = {include: {relation: 'patients', scope: {fields: 'name'}}};
const physicianId = physician.id;
Physician.findById(physicianId, includeFilter, function(err, result) {
if (err) return done(err);
should.exist(result);
result.id.should.eql(physicianId);
should.exist(result.patients);
result.patients().should.be.an.instanceOf(Array);
should.exist(result.patients()[0]);
should.exist(result.patients()[0].name);
should.not.exist(result.patients()[0].age);
done();
});
});
});
function createSampleData(done) {
Physician.create(function(err, result) {
result.patients.create({name: 'a', age: '10', sequence: 1},
function(err, p) {
samplePatientId = p.id;
result.patients.create({name: 'z', age: '20', sequence: 2},
function() {
result.patients.create({name: 'c', sequence: 3}, function() {
physician = result;
done();
});
});
});
});
}
function findByFlexibleId(scopeFn, sampleId, callback) {
scopeFn({where: {id: sampleId}}, function(err, results) {
if (err) return callback(err);
if (results.length > 0) return callback(null, results);
scopeFn({where: {_id: sampleId}}, callback);
});
}
});
describe('find over related model with options', function() {
after(function() {
Physician.clearObservers('access');
Patient.clearObservers('access');
});
before(function() {
Physician.observe('access', beforeAccessFn);
Patient.observe('access', beforeAccessFn);
function beforeAccessFn(ctx, next) {
ctx.query.where.realm = ctx.options.realm;
next();
}
});
it('should find be filtered from option', function(done) {
let id;
Physician.create(function(err, physician) {
if (err) return done(err);
physician.patients.create({name: 'a', realm: 'test'}, function(err, ch) {
if (err) return done(err);
id = ch.id;
physician.patients.create({name: 'z', realm: 'test'}, function(err) {
if (err) return done(err);
physician.patients.create({name: 'c', realm: 'anotherRealm'}, function(err) {
if (err) return done(err);
verify(physician);
});
});
});
});
function verify(physician) {
physician.patients({order: 'name ASC'}, {realm: 'test'}, function(err, records) {
if (err) return done(err);
should.exist(records);
records.length.should.eql(2);
const expected = ['a:test', 'z:test'];
const actual = records.map(function(r) { return r.name + ':' + r.realm; });
actual.sort().should.eql(expected.sort());
done();
});
}
});
});
it('should find scoped record', function(done) {
let 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) {
if (err) return done(err);
should.exist(ch);
ch.id.should.eql(id);
done();
});
}
});
it('should find scoped record with promises', function(done) {
let 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) {
if (err) return done(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) {
if (err) return done(err);
patient.address(address);
patient.save(function() {
verify(physician, address.id);
});
});
});
});
function verify(physician, addressId) {
physician.patients({include: 'address'}, function(err, ch) {
if (err) return done(err);
should.exist(ch);
ch.should.have.lengthOf(1);
ch[0].addressId.should.eql(addressId);
const 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.find({include: 'address'})
.then(function(ch) {
should.exist(ch);
ch.should.have.lengthOf(1);
ch[0].addressId.toString().should.eql(addressId.toString());
const 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) {
let 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) {
if (err) return done(err);
should.exist(ch);
ch.id.should.eql(id);
ch.name.should.equal('aa');
done();
});
}
});
it('should update scoped record with promises', function(done) {
let 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();
});
}
});
bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false,
'should destroy scoped record', function(done) {
let 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();
});
}
});
bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false,
'should destroy scoped record with promises', function(done) {
let 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) {
let id;
Physician.create(function(err, physician) {
physician.patients.create({name: 'a'}, function(err, ch) {
if (err) return done(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) {
if (err) return done(err);
flag.should.be.eql(true);
done();
});
}
});
it('should check existence of a scoped record with promises', function(done) {
let 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) {
const 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) {
const 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);
});
bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false,
'should allow to remove connection with instance', function(done) {
let 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) {
if (err) return done(err);
flag.should.be.eql(false);
done();
});
}
});
bdd.itIf(connectorCapabilities.deleteWithOtherThanId !== false,
'should allow to remove connection with instance with promises', function(done) {
let 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() {
let Physician, Patient, Appointment, Address;
let idPatient, idPhysician;
beforeEach(function(done) {
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,
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);
const physician = new Physician({id: idPhysician});
const scope1 = physician.patients._scope;
scope1.should.have.property('collect', 'patient');
scope1.should.have.property('include', 'patient');
const patient = new Patient({id: idPatient});
const 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???
const physician = new Physician({id: idPhysician});
const scope1 = physician.patients._scope;
scope1.should.have.property('collect', 'bar');
scope1.should.have.property('include', 'bar');
const patient = new Patient({id: idPatient});
const 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.
const physician = new Physician({id: idPhysician});
const scope1 = physician.patients._scope;
scope1.should.have.property('collect', 'bar');
scope1.should.have.property('include', 'bar');
const patient = new Patient({id: idPatient});
const 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'