loopback-datasource-jugglernext
Version:
LoopBack DataSoure Juggler
1,562 lines (1,420 loc) • 178 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
// This test written in mocha+should.js
var should = require('./init.js');
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 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 });
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();
});
}
});
it('should fetch scoped instances with paging filters', 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) {
//limit plus skip
physician.patients({ limit: 1, skip: 1 }, function(err, ch) {
should.not.exist(err);
should.exist(ch);
ch.should.have.lengthOf(1);
ch[0].name.should.eql('z');
//offset plus skip
physician.patients({ limit: 1, offset: 1 }, function(err1, ch1) {
should.not.exist(err1);
should.exist(ch1);
ch1.should.have.lengthOf(1);
ch1[0].name.should.eql('z');
//order
physician.patients({ order: 'patientId DESC' }, function(err2, ch2) {
should.not.exist(err2);
should.exist(ch2);
ch2.should.have.lengthOf(3);
ch2[0].name.should.eql('c');
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);
p.type.should.equal('widget');
done();
});
});
});
});
it('should create record on scope with promises', function(done) {
Category.create()
.then(function(c) {
return c.jobs.create({ type: 'book' })
.then(function(p) {
p.categoryId.should.eql(c.id);
p.type.should.equal('book');
return c.jobs.create({ type: 'widget' })
.then(function(p) {
p.categoryId.should.eql(c.id);
p.type.should.equal('widget');
done();
});
});
}).catch(done);
});
it('should find records on scope', 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();
});
});
});
it('should find records on scope with promises', function(done) {
Category.findOne()
.then(function(c) {
return c.jobs.getAsync();
})
.then(function(jobs) {
jobs.should.have.length(2);
done();
})
.catch(done);
});
it('should find record on scope - filtered', function(done) {
Category.findOne(function(err, c) {
should.not.exists(err);
c.jobs({ where: { type: 'book' }}, function(err, jobs) {
should.not.exists(err);
jobs.should.have.length(1);
jobs[0].type.should.equal('book');
done();
});
});
});
it('should find record on scope with promises - filtered', function(done) {
Category.findOne()
.then(function(c) {
return c.jobs.getAsync({ where: { type: 'book' }});
})
.then(function(jobs) {
jobs.should.have.length(1);
jobs[0].type.should.equal('book');
done();
})
.catch(done);
});
// So why not just do the above? In LoopBack, the context
// that gets passed into a beforeRemote handler contains
// a reference to the parent scope/instance: ctx.instance
// in order to enforce a (dynamic scope) at runtime
// a temporary property can be set in the beforeRemoting
// handler. Optionally,properties dynamic properties can be declared.
//
// The code below simulates this.
it('should create record on scope - properties', function(done) {
Category.findOne(function(err, c) {
should.not.exists(err);
c.jobType = 'tool'; // temporary
c.jobs.create(function(err, p) {
p.categoryId.should.eql(c.id);
p.type.should.equal('tool');
done();
});
});
});
it('should find records on scope', function(done) {
Category.findOne(function(err, c) {
should.not.exists(err);
c.jobs(function(err, jobs) {
should.not.exists(err);
jobs.should.have.length(3);
done();
});
});
});
it('should find record on scope - scoped', function(done) {
Category.findOne(function(err, c) {
should.not.exists(err);
c.jobType = 'book'; // temporary, for scoping
c.jobs(function(err, jobs) {
should.not.exists(err);
jobs.should.have.length(1);
jobs[0].type.should.equal('book');
done();
});
});
});
it('should find record on scope - scoped', function(done) {
Category.findOne(function(err, c) {
should.not.exists(err);
c.jobType = 'tool'; // temporary, for scoping
c.jobs(function(err, jobs) {
should.not.exists(err);
jobs.should.have.length(1);
jobs[0].type.should.equal('tool');
done();
});
});
});
it('should find count of records on scope - scoped', function(done) {
Category.findOne(function(err, c) {
should.not.exists(err);
c.jobType = 'tool'; // temporary, for scoping
c.jobs.count(function(err, count) {
should.not.exists(err);
count.should.equal(1);
done();
});
});
});
it('should delete records on scope - scoped', function(done) {
Cat