diff --git a/lib/models/data-model.js b/lib/models/data-model.js index 6120e1ae..d950f7c8 100644 --- a/lib/models/data-model.js +++ b/lib/models/data-model.js @@ -31,6 +31,9 @@ var DataModel = module.exports = Model.extend('DataModel'); */ DataModel.setup = function setupDataModel() { + // call Model.setup first + Model.setup.call(this); + var DataModel = this; var typeName = this.modelName; @@ -406,7 +409,8 @@ DataModel.setupRemoting = function() { setRemoting(DataModel.deleteById, { description: 'Delete a model instance by id from the data source', - accepts: {arg: 'id', type: 'any', description: 'Model id', required: true}, + accepts: {arg: 'id', type: 'any', description: 'Model id', required: true, + http: {source: 'path'}}, http: {verb: 'del', path: '/:id'} }); diff --git a/lib/models/model.js b/lib/models/model.js index 8d50976f..d28cb786 100644 --- a/lib/models/model.js +++ b/lib/models/model.js @@ -182,7 +182,7 @@ Model.setup = function () { ]; ModelCtor.sharedCtor.returns = {root: true}; - + // enable change tracking (usually for replication) if(options.trackChanges) { ModelCtor.once('dataSourceAttached', function() { diff --git a/test/model.test.js b/test/model.test.js index 564044ed..4abb4b89 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -3,6 +3,7 @@ var loopback = require('../'); var ACL = loopback.ACL; var Change = loopback.Change; var defineModelTestsWithDataSource = require('./util/model-tests'); +var DataModel = loopback.DataModel; describe('Model', function() { defineModelTestsWithDataSource({ @@ -11,3 +12,511 @@ describe('Model', function() { } }); }); + +describe.onServer('Remote Methods', function(){ + + var User; + var dataSource; + var app; + + beforeEach(function () { + User = DataModel.extend('user', { + 'first': String, + 'last': String, + 'age': Number, + 'password': String, + 'gender': String, + 'domain': String, + 'email': String + }, { + trackChanges: true + }); + + dataSource = loopback.createDataSource({ + connector: loopback.Memory + }); + + User.attachTo(dataSource); + + User.login = function (username, password, fn) { + if(username === 'foo' && password === 'bar') { + fn(null, 123); + } else { + throw new Error('bad username and password!'); + } + } + + loopback.remoteMethod( + User.login, + { + accepts: [ + {arg: 'username', type: 'string', required: true}, + {arg: 'password', type: 'string', required: true} + ], + returns: {arg: 'sessionId', type: 'any', root: true}, + http: {path: '/sign-in', verb: 'get'} + } + ); + + app = loopback(); + app.use(loopback.rest()); + app.model(User); + }); + + describe('Example Remote Method', function () { + it('Call the method using HTTP / REST', function(done) { + request(app) + .get('/users/sign-in?username=foo&password=bar') + .expect('Content-Type', /json/) + .expect(200) + .end(function(err, res){ + if(err) return done(err); + assert.equal(res.body, 123); + done(); + }); + }); + + it('Converts null result of findById to 404 Not Found', function(done) { + request(app) + .get('/users/not-found') + .expect(404) + .end(done); + }); + }); + + describe('Model.beforeRemote(name, fn)', function(){ + it('Run a function before a remote method is called by a client', function(done) { + var hookCalled = false; + + User.beforeRemote('create', function(ctx, user, next) { + hookCalled = true; + next(); + }); + + // invoke save + request(app) + .post('/users') + .send({data: {first: 'foo', last: 'bar'}}) + .expect('Content-Type', /json/) + .expect(200) + .end(function(err, res) { + if(err) return done(err); + assert(hookCalled, 'hook wasnt called'); + done(); + }); + }); + }); + + describe('Model.afterRemote(name, fn)', function(){ + it('Run a function after a remote method is called by a client', function(done) { + var beforeCalled = false; + var afterCalled = false; + + User.beforeRemote('create', function(ctx, user, next) { + assert(!afterCalled); + beforeCalled = true; + next(); + }); + User.afterRemote('create', function(ctx, user, next) { + assert(beforeCalled); + afterCalled = true; + next(); + }); + + // invoke save + request(app) + .post('/users') + .send({data: {first: 'foo', last: 'bar'}}) + .expect('Content-Type', /json/) + .expect(200) + .end(function(err, res) { + if(err) return done(err); + assert(beforeCalled, 'before hook was not called'); + assert(afterCalled, 'after hook was not called'); + done(); + }); + }); + }); + + describe('Remote Method invoking context', function () { + // describe('ctx.user', function() { + // it("The remote user model calling the method remotely", function(done) { + // done(new Error('test not implemented')); + // }); + // }); + + describe('ctx.req', function() { + it("The express ServerRequest object", function(done) { + var hookCalled = false; + + User.beforeRemote('create', function(ctx, user, next) { + hookCalled = true; + assert(ctx.req); + assert(ctx.req.url); + assert(ctx.req.method); + assert(ctx.res); + assert(ctx.res.write); + assert(ctx.res.end); + next(); + }); + + // invoke save + request(app) + .post('/users') + .send({data: {first: 'foo', last: 'bar'}}) + .expect('Content-Type', /json/) + .expect(200) + .end(function(err, res) { + if(err) return done(err); + assert(hookCalled); + done(); + }); + }); + }); + + describe('ctx.res', function() { + it("The express ServerResponse object", function(done) { + var hookCalled = false; + + User.beforeRemote('create', function(ctx, user, next) { + hookCalled = true; + assert(ctx.req); + assert(ctx.req.url); + assert(ctx.req.method); + assert(ctx.res); + assert(ctx.res.write); + assert(ctx.res.end); + next(); + }); + + // invoke save + request(app) + .post('/users') + .send({data: {first: 'foo', last: 'bar'}}) + .expect('Content-Type', /json/) + .expect(200) + .end(function(err, res) { + if(err) return done(err); + assert(hookCalled); + done(); + }); + }); + }); + }) + + describe('in compat mode', function() { + before(function() { + loopback.compat.usePluralNamesForRemoting = true; + }); + after(function() { + loopback.compat.usePluralNamesForRemoting = false; + }); + + it('correctly install before/after hooks', function(done) { + var hooksCalled = []; + + User.beforeRemote('**', function(ctx, user, next) { + hooksCalled.push('beforeRemote'); + next(); + }); + + User.afterRemote('**', function(ctx, user, next) { + hooksCalled.push('afterRemote'); + next(); + }); + + request(app).get('/users') + .expect(200, function(err, res) { + if (err) return done(err); + expect(hooksCalled, 'hooks called') + .to.eql(['beforeRemote', 'afterRemote']); + done(); + }); + }); + }); + + describe('Model.hasMany(Model)', function() { + it("Define a one to many relationship", function(done) { + var Book = dataSource.createModel('book', {title: String, author: String}); + var Chapter = dataSource.createModel('chapter', {title: String}); + + // by referencing model + Book.hasMany(Chapter); + + Book.create({title: 'Into the Wild', author: 'Jon Krakauer'}, function(err, book) { + // using 'chapters' scope for build: + var c = book.chapters.build({title: 'Chapter 1'}); + book.chapters.create({title: 'Chapter 2'}, function () { + c.save(function () { + Chapter.count({bookId: book.id}, function (err, count) { + assert.equal(count, 2); + book.chapters({where: {title: 'Chapter 1'}}, function(err, chapters) { + assert.equal(chapters.length, 1); + assert.equal(chapters[0].title, 'Chapter 1'); + done(); + }); + }); + }); + }); + }); + }); + }); + + describe('Model.properties', function(){ + it('Normalized properties passed in originally by loopback.createModel()', function() { + var props = { + s: String, + n: {type: 'Number'}, + o: {type: 'String', min: 10, max: 100}, + d: Date, + g: loopback.GeoPoint + }; + + var MyModel = loopback.createModel('foo', props); + + Object.keys(MyModel.definition.properties).forEach(function (key) { + var p = MyModel.definition.properties[key]; + var o = MyModel.definition.properties[key]; + assert(p); + assert(o); + assert(typeof p.type === 'function'); + + if(typeof o === 'function') { + // the normalized property + // should match the given property + assert( + p.type.name === o.name + || + p.type.name === o + ) + } + }); + }); + }); + + describe('Model.extend()', function(){ + it('Create a new model by extending an existing model', function() { + var User = loopback.Model.extend('test-user', { + email: String + }); + + User.foo = function () { + return 'bar'; + } + + User.prototype.bar = function () { + return 'foo'; + } + + var MyUser = User.extend('my-user', { + a: String, + b: String + }); + + assert.equal(MyUser.prototype.bar, User.prototype.bar); + assert.equal(MyUser.foo, User.foo); + + var user = new MyUser({ + email: 'foo@bar.com', + a: 'foo', + b: 'bar' + }); + + assert.equal(user.email, 'foo@bar.com'); + assert.equal(user.a, 'foo'); + assert.equal(user.b, 'bar'); + }); + }); + + describe('Model.extend() events', function() { + it('create isolated emitters for subclasses', function() { + var User1 = loopback.createModel('User1', { + 'first': String, + 'last': String + }); + + var User2 = loopback.createModel('User2', { + 'name': String + }); + + var user1Triggered = false; + User1.once('x', function(event) { + user1Triggered = true; + }); + + + var user2Triggered = false; + User2.once('x', function(event) { + user2Triggered = true; + }); + + assert(User1.once !== User2.once); + assert(User1.once !== loopback.Model.once); + + User1.emit('x', User1); + + assert(user1Triggered); + assert(!user2Triggered); + }); + + }); + + describe('Model.checkAccessTypeForMethod(remoteMethod)', function () { + shouldReturn('create', ACL.WRITE); + shouldReturn('updateOrCreate', ACL.WRITE); + shouldReturn('upsert', ACL.WRITE); + shouldReturn('exists', ACL.READ); + shouldReturn('findById', ACL.READ); + shouldReturn('find', ACL.READ); + shouldReturn('findOne', ACL.READ); + shouldReturn('destroyById', ACL.WRITE); + shouldReturn('deleteById', ACL.WRITE); + shouldReturn('removeById', ACL.WRITE); + shouldReturn('count', ACL.READ); + shouldReturn('unkown-model-method', ACL.EXECUTE); + + function shouldReturn(methodName, expectedAccessType) { + describe(methodName, function () { + it('should return ' + expectedAccessType, function() { + var remoteMethod = {name: methodName}; + assert.equal( + User._getAccessTypeForMethod(remoteMethod), + expectedAccessType + ); + }); + }); + } + }); + + describe('Model.getChangeModel()', function() { + it('Get the Change Model', function () { + var UserChange = User.getChangeModel(); + var change = new UserChange(); + assert(change instanceof Change); + }); + }); + + describe('Model.getSourceId(callback)', function() { + it('Get the Source Id', function (done) { + User.getSourceId(function(err, id) { + assert.equal('memory-user', id); + done(); + }); + }); + }); + + describe('Model.checkpoint(callback)', function() { + it('Create a checkpoint', function (done) { + var Checkpoint = User.getChangeModel().getCheckpointModel(); + var tasks = [ + getCurrentCheckpoint, + checkpoint + ]; + var result; + var current; + + async.parallel(tasks, function(err) { + if(err) return done(err); + + assert.equal(result, current + 1); + done(); + }); + + function getCurrentCheckpoint(cb) { + Checkpoint.current(function(err, cp) { + current = cp; + cb(err); + }); + } + + function checkpoint(cb) { + User.checkpoint(function(err, cp) { + result = cp.id; + cb(err); + }); + } + }); + }); + + describe('Replication / Change APIs', function() { + beforeEach(function(done) { + var test = this; + this.dataSource = dataSource; + var SourceModel = this.SourceModel = this.dataSource.createModel('SourceModel', {}, { + trackChanges: true + }); + var TargetModel = this.TargetModel = this.dataSource.createModel('TargetModel', {}, { + trackChanges: true + }); + + var createOne = SourceModel.create.bind(SourceModel, { + name: 'baz' + }); + + async.parallel([ + createOne, + function(cb) { + SourceModel.currentCheckpoint(function(err, id) { + if(err) return cb(err); + test.startingCheckpoint = id; + cb(); + }); + } + ], process.nextTick.bind(process, done)); + }); + + describe('Model.changes(since, filter, callback)', function() { + it('Get changes since the given checkpoint', function (done) { + this.SourceModel.changes(this.startingCheckpoint, {}, function(err, changes) { + assert.equal(changes.length, 1); + done(); + }); + }); + }); + + describe.skip('Model.replicate(since, targetModel, options, callback)', function() { + it('Replicate data using the target model', function (done) { + var test = this; + var options = {}; + var sourceData; + var targetData; + + this.SourceModel.replicate(this.startingCheckpoint, this.TargetModel, + options, function(err, conflicts) { + assert(conflicts.length === 0); + async.parallel([ + function(cb) { + test.SourceModel.find(function(err, result) { + if(err) return cb(err); + sourceData = result; + cb(); + }); + }, + function(cb) { + test.TargetModel.find(function(err, result) { + if(err) return cb(err); + targetData = result; + cb(); + }); + } + ], function(err) { + if(err) return done(err); + + assert.deepEqual(sourceData, targetData); + done(); + }); + }); + }); + }); + }); + + describe('Model._getACLModel()', function() { + it('should return the subclass of ACL', function() { + var Model = require('../').Model; + var acl = ACL.extend('acl'); + Model._ACL(null); // Reset the ACL class for the base model + var model = Model._ACL(); + assert.equal(model, acl); + }); + }); +}); diff --git a/test/remote-connector.test.js b/test/remote-connector.test.js index 10ac01e7..9e6f875d 100644 --- a/test/remote-connector.test.js +++ b/test/remote-connector.test.js @@ -9,7 +9,6 @@ describe('RemoteConnector', function() { beforeEach: function(done) { var test = this; remoteApp = loopback(); - remoteApp.use(loopback.logger('dev')); remoteApp.use(loopback.rest()); remoteApp.listen(0, function() { test.dataSource = loopback.createDataSource({ @@ -22,7 +21,9 @@ describe('RemoteConnector', function() { }, onDefine: function(Model) { var RemoteModel = Model.extend(Model.modelName); - RemoteModel.attachTo(loopback.memory()); + RemoteModel.attachTo(loopback.createDataSource({ + connector: loopback.Memory + })); remoteApp.model(RemoteModel); } }); diff --git a/test/support.js b/test/support.js index 33dcc855..81d420e1 100644 --- a/test/support.js +++ b/test/support.js @@ -10,6 +10,7 @@ GeoPoint = loopback.GeoPoint; app = null; TaskEmitter = require('strong-task-emitter'); request = require('supertest'); +var RemoteObjects = require('strong-remoting'); // Speed up the password hashing algorithm // for tests using the built-in User model diff --git a/test/util/model-tests.js b/test/util/model-tests.js index b17466d5..302589c1 100644 --- a/test/util/model-tests.js +++ b/test/util/model-tests.js @@ -3,8 +3,8 @@ Before merging notes: - must fix the ".skip" tests below before merging - - somehow need to handle callback values that are model typed - - findById isn't getting an id... perhaps a remoting bug? + [x] somehow need to handle callback values that are model typed + [x] findById isn't getting an id... perhaps a remoting bug? eg. @@ -20,8 +20,12 @@ var loopback = require('../../'); var ACL = loopback.ACL; var Change = loopback.Change; var DataModel = loopback.DataModel; +var RemoteObjects = require('strong-remoting'); module.exports = function defineModelTestsWithDataSource(options) { + +describe('Model Tests', function() { + var User, dataSource; if(options.beforeEach) { @@ -41,7 +45,7 @@ module.exports = function defineModelTestsWithDataSource(options) { var extendedModel = extend.apply(DataModel, arguments); if(options.onDefine) { - options.onDefine.call(test, extendedModel) + options.onDefine.call(test, extendedModel); } return extendedModel; @@ -60,8 +64,6 @@ module.exports = function defineModelTestsWithDataSource(options) { }); User.attachTo(dataSource); - - }); describe('Model.validatesPresenceOf(properties...)', function() { @@ -147,7 +149,7 @@ module.exports = function defineModelTestsWithDataSource(options) { it('Asynchronously validate the model', function(done) { User.validatesNumericalityOf('age', {int: true}); - var user = new User({first: 'joe', age: 'flarg'}) + var user = new User({first: 'joe', age: 'flarg'}); user.isValid(function (valid) { assert(valid === false); assert(user.errors.age, 'model should have age error'); @@ -227,12 +229,10 @@ module.exports = function defineModelTestsWithDataSource(options) { describe('model.destroy([callback])', function() { it("Remove a model from the attached data source", function(done) { User.create({first: 'joe', last: 'bob'}, function (err, user) { - console.log(User.findById.accepts); User.findById(user.id, function (err, foundUser) { assert.equal(user.id, foundUser.id); foundUser.destroy(function () { User.findById(user.id, function (err, notFound) { - assert(!err); assert.equal(notFound, null); done(); }); @@ -247,9 +247,8 @@ module.exports = function defineModelTestsWithDataSource(options) { User.create({first: 'joe', last: 'bob'}, function (err, user) { User.deleteById(user.id, function (err) { User.findById(user.id, function (err, notFound) { - assert(!err); - assert.equal(notFound, null); - done(); + assert.equal(notFound, null); + done(); }); }); }); @@ -308,488 +307,7 @@ module.exports = function defineModelTestsWithDataSource(options) { }); }); - describe.onServer('Remote Methods', function(){ - - beforeEach(function () { - User.login = function (username, password, fn) { - if(username === 'foo' && password === 'bar') { - fn(null, 123); - } else { - throw new Error('bad username and password!'); - } - } - - loopback.remoteMethod( - User.login, - { - accepts: [ - {arg: 'username', type: 'string', required: true}, - {arg: 'password', type: 'string', required: true} - ], - returns: {arg: 'sessionId', type: 'any', root: true}, - http: {path: '/sign-in', verb: 'get'} - } - ); - - app.use(loopback.rest()); - app.model(User); - }); - - describe('Example Remote Method', function () { - it('Call the method using HTTP / REST', function(done) { - request(app) - .get('/users/sign-in?username=foo&password=bar') - .expect('Content-Type', /json/) - .expect(200) - .end(function(err, res){ - if(err) return done(err); - assert.equal(res.body, 123); - done(); - }); - }); - - it('Converts null result of findById to 404 Not Found', function(done) { - request(app) - .get('/users/not-found') - .expect(404) - .end(done); - }); - }); - - describe('Model.beforeRemote(name, fn)', function(){ - it('Run a function before a remote method is called by a client', function(done) { - var hookCalled = false; - - User.beforeRemote('create', function(ctx, user, next) { - hookCalled = true; - next(); - }); - - // invoke save - request(app) - .post('/users') - .send({data: {first: 'foo', last: 'bar'}}) - .expect('Content-Type', /json/) - .expect(200) - .end(function(err, res) { - if(err) return done(err); - assert(hookCalled, 'hook wasnt called'); - done(); - }); - }); - }); - - describe('Model.afterRemote(name, fn)', function(){ - it('Run a function after a remote method is called by a client', function(done) { - var beforeCalled = false; - var afterCalled = false; - - User.beforeRemote('create', function(ctx, user, next) { - assert(!afterCalled); - beforeCalled = true; - next(); - }); - User.afterRemote('create', function(ctx, user, next) { - assert(beforeCalled); - afterCalled = true; - next(); - }); - - // invoke save - request(app) - .post('/users') - .send({data: {first: 'foo', last: 'bar'}}) - .expect('Content-Type', /json/) - .expect(200) - .end(function(err, res) { - if(err) return done(err); - assert(beforeCalled, 'before hook was not called'); - assert(afterCalled, 'after hook was not called'); - done(); - }); - }); - }); - - describe('Remote Method invoking context', function () { - // describe('ctx.user', function() { - // it("The remote user model calling the method remotely", function(done) { - // done(new Error('test not implemented')); - // }); - // }); - - describe('ctx.req', function() { - it("The express ServerRequest object", function(done) { - var hookCalled = false; - - User.beforeRemote('create', function(ctx, user, next) { - hookCalled = true; - assert(ctx.req); - assert(ctx.req.url); - assert(ctx.req.method); - assert(ctx.res); - assert(ctx.res.write); - assert(ctx.res.end); - next(); - }); - - // invoke save - request(app) - .post('/users') - .send({data: {first: 'foo', last: 'bar'}}) - .expect('Content-Type', /json/) - .expect(200) - .end(function(err, res) { - if(err) return done(err); - assert(hookCalled); - done(); - }); - }); - }); - - describe('ctx.res', function() { - it("The express ServerResponse object", function(done) { - var hookCalled = false; - - User.beforeRemote('create', function(ctx, user, next) { - hookCalled = true; - assert(ctx.req); - assert(ctx.req.url); - assert(ctx.req.method); - assert(ctx.res); - assert(ctx.res.write); - assert(ctx.res.end); - next(); - }); - - // invoke save - request(app) - .post('/users') - .send({data: {first: 'foo', last: 'bar'}}) - .expect('Content-Type', /json/) - .expect(200) - .end(function(err, res) { - if(err) return done(err); - assert(hookCalled); - done(); - }); - }); - }); - }) - - describe('in compat mode', function() { - before(function() { - loopback.compat.usePluralNamesForRemoting = true; - }); - after(function() { - loopback.compat.usePluralNamesForRemoting = false; - }); - - it('correctly install before/after hooks', function(done) { - var hooksCalled = []; - - User.beforeRemote('**', function(ctx, user, next) { - hooksCalled.push('beforeRemote'); - next(); - }); - - User.afterRemote('**', function(ctx, user, next) { - hooksCalled.push('afterRemote'); - next(); - }); - - request(app).get('/users') - .expect(200, function(err, res) { - if (err) return done(err); - expect(hooksCalled, 'hooks called') - .to.eql(['beforeRemote', 'afterRemote']); - done(); - }); - }); - }); - }); - - describe('Model.hasMany(Model)', function() { - it("Define a one to many relationship", function(done) { - var Book = dataSource.createModel('book', {title: String, author: String}); - var Chapter = dataSource.createModel('chapter', {title: String}); - - // by referencing model - Book.hasMany(Chapter); - - Book.create({title: 'Into the Wild', author: 'Jon Krakauer'}, function(err, book) { - // using 'chapters' scope for build: - var c = book.chapters.build({title: 'Chapter 1'}); - book.chapters.create({title: 'Chapter 2'}, function () { - c.save(function () { - Chapter.count({bookId: book.id}, function (err, count) { - assert.equal(count, 2); - book.chapters({where: {title: 'Chapter 1'}}, function(err, chapters) { - assert.equal(chapters.length, 1); - assert.equal(chapters[0].title, 'Chapter 1'); - done(); - }); - }); - }); - }); - }); - }); - }); - - describe('Model.properties', function(){ - it('Normalized properties passed in originally by loopback.createModel()', function() { - var props = { - s: String, - n: {type: 'Number'}, - o: {type: 'String', min: 10, max: 100}, - d: Date, - g: loopback.GeoPoint - }; - - var MyModel = loopback.createModel('foo', props); - - Object.keys(MyModel.definition.properties).forEach(function (key) { - var p = MyModel.definition.properties[key]; - var o = MyModel.definition.properties[key]; - assert(p); - assert(o); - assert(typeof p.type === 'function'); - - if(typeof o === 'function') { - // the normalized property - // should match the given property - assert( - p.type.name === o.name - || - p.type.name === o - ) - } - }); - }); - }); - - describe('Model.extend()', function(){ - it('Create a new model by extending an existing model', function() { - var User = loopback.Model.extend('test-user', { - email: String - }); - - User.foo = function () { - return 'bar'; - } - - User.prototype.bar = function () { - return 'foo'; - } - - var MyUser = User.extend('my-user', { - a: String, - b: String - }); - - assert.equal(MyUser.prototype.bar, User.prototype.bar); - assert.equal(MyUser.foo, User.foo); - - var user = new MyUser({ - email: 'foo@bar.com', - a: 'foo', - b: 'bar' - }); - - assert.equal(user.email, 'foo@bar.com'); - assert.equal(user.a, 'foo'); - assert.equal(user.b, 'bar'); - }); - }); - - describe('Model.extend() events', function() { - it('create isolated emitters for subclasses', function() { - var User1 = loopback.createModel('User1', { - 'first': String, - 'last': String - }); - - var User2 = loopback.createModel('User2', { - 'name': String - }); - - var user1Triggered = false; - User1.once('x', function(event) { - user1Triggered = true; - }); +}); - var user2Triggered = false; - User2.once('x', function(event) { - user2Triggered = true; - }); - - assert(User1.once !== User2.once); - assert(User1.once !== loopback.Model.once); - - User1.emit('x', User1); - - assert(user1Triggered); - assert(!user2Triggered); - }); - - }); - - describe('Model.checkAccessTypeForMethod(remoteMethod)', function () { - shouldReturn('create', ACL.WRITE); - shouldReturn('updateOrCreate', ACL.WRITE); - shouldReturn('upsert', ACL.WRITE); - shouldReturn('exists', ACL.READ); - shouldReturn('findById', ACL.READ); - shouldReturn('find', ACL.READ); - shouldReturn('findOne', ACL.READ); - shouldReturn('destroyById', ACL.WRITE); - shouldReturn('deleteById', ACL.WRITE); - shouldReturn('removeById', ACL.WRITE); - shouldReturn('count', ACL.READ); - shouldReturn('unkown-model-method', ACL.EXECUTE); - - function shouldReturn(methodName, expectedAccessType) { - describe(methodName, function () { - it('should return ' + expectedAccessType, function() { - var remoteMethod = {name: methodName}; - assert.equal( - User._getAccessTypeForMethod(remoteMethod), - expectedAccessType - ); - }); - }); - } - }); - - describe('Model.getChangeModel()', function() { - it('Get the Change Model', function () { - var UserChange = User.getChangeModel(); - var change = new UserChange(); - assert(change instanceof Change); - }); - }); - - describe('Model.getSourceId(callback)', function() { - it('Get the Source Id', function (done) { - User.getSourceId(function(err, id) { - assert.equal('memory-user', id); - done(); - }); - }); - }); - - describe('Model.checkpoint(callback)', function() { - it('Create a checkpoint', function (done) { - var Checkpoint = User.getChangeModel().getCheckpointModel(); - var tasks = [ - getCurrentCheckpoint, - checkpoint - ]; - var result; - var current; - - async.parallel(tasks, function(err) { - if(err) return done(err); - - assert.equal(result, current + 1); - done(); - }); - - function getCurrentCheckpoint(cb) { - Checkpoint.current(function(err, cp) { - current = cp; - cb(err); - }); - } - - function checkpoint(cb) { - User.checkpoint(function(err, cp) { - result = cp.id; - cb(err); - }); - } - }); - }); - - describe('Replication / Change APIs', function() { - beforeEach(function(done) { - var test = this; - this.dataSource = loopback.createDataSource(options.dataSource); - var SourceModel = this.SourceModel = this.dataSource.createModel('SourceModel', {}, { - trackChanges: true - }); - var TargetModel = this.TargetModel = this.dataSource.createModel('TargetModel', {}, { - trackChanges: true - }); - - var createOne = SourceModel.create.bind(SourceModel, { - name: 'baz' - }); - - async.parallel([ - createOne, - function(cb) { - SourceModel.currentCheckpoint(function(err, id) { - if(err) return cb(err); - test.startingCheckpoint = id; - cb(); - }); - } - ], process.nextTick.bind(process, done)); - }); - - describe('Model.changes(since, filter, callback)', function() { - it('Get changes since the given checkpoint', function (done) { - this.SourceModel.changes(this.startingCheckpoint, {}, function(err, changes) { - assert.equal(changes.length, 1); - done(); - }); - }); - }); - - describe.skip('Model.replicate(since, targetModel, options, callback)', function() { - it('Replicate data using the target model', function (done) { - var test = this; - var options = {}; - var sourceData; - var targetData; - - this.SourceModel.replicate(this.startingCheckpoint, this.TargetModel, - options, function(err, conflicts) { - assert(conflicts.length === 0); - async.parallel([ - function(cb) { - test.SourceModel.find(function(err, result) { - if(err) return cb(err); - sourceData = result; - cb(); - }); - }, - function(cb) { - test.TargetModel.find(function(err, result) { - if(err) return cb(err); - targetData = result; - cb(); - }); - } - ], function(err) { - if(err) return done(err); - - assert.deepEqual(sourceData, targetData); - done(); - }); - }); - }); - }); - }); - - describe('Model._getACLModel()', function() { - it('should return the subclass of ACL', function() { - var Model = require('../../').Model; - var acl = ACL.extend('acl'); - Model._ACL(null); // Reset the ACL class for the base model - var model = Model._ACL(); - assert.equal(model, acl); - }); - }); }