2013-12-18 03:15:48 +00:00
|
|
|
var path = require('path');
|
|
|
|
var SIMPLE_APP = path.join(__dirname, 'fixtures', 'simple-app');
|
2014-05-03 04:19:14 +00:00
|
|
|
var loopback = require('../');
|
2014-06-05 07:45:09 +00:00
|
|
|
var PersistedModel = loopback.PersistedModel;
|
2013-12-18 03:15:48 +00:00
|
|
|
|
2014-06-03 08:39:54 +00:00
|
|
|
var describe = require('./util/describe');
|
|
|
|
var it = require('./util/it');
|
|
|
|
|
2013-06-07 19:57:51 +00:00
|
|
|
describe('app', function() {
|
2013-06-06 00:11:21 +00:00
|
|
|
|
2013-06-07 19:57:51 +00:00
|
|
|
describe('app.model(Model)', function() {
|
2014-01-22 10:22:23 +00:00
|
|
|
var app, db;
|
|
|
|
beforeEach(function() {
|
|
|
|
app = loopback();
|
|
|
|
db = loopback.createDataSource({connector: loopback.Memory});
|
|
|
|
});
|
|
|
|
|
2013-07-16 20:46:33 +00:00
|
|
|
it("Expose a `Model` to remote clients", function() {
|
2014-06-05 07:45:09 +00:00
|
|
|
var Color = PersistedModel.extend('color', {name: String});
|
2013-06-07 19:57:51 +00:00
|
|
|
app.model(Color);
|
2014-05-03 04:19:14 +00:00
|
|
|
Color.attachTo(db);
|
2014-01-22 10:22:23 +00:00
|
|
|
|
|
|
|
expect(app.models()).to.eql([Color]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('uses singlar name as app.remoteObjects() key', function() {
|
2014-06-05 07:45:09 +00:00
|
|
|
var Color = PersistedModel.extend('color', {name: String});
|
2014-01-22 10:22:23 +00:00
|
|
|
app.model(Color);
|
2014-05-03 04:19:14 +00:00
|
|
|
Color.attachTo(db);
|
2014-01-22 10:22:23 +00:00
|
|
|
expect(app.remoteObjects()).to.eql({ color: Color });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('uses singular name as shared class name', function() {
|
2014-06-05 07:45:09 +00:00
|
|
|
var Color = PersistedModel.extend('color', {name: String});
|
2014-01-22 10:22:23 +00:00
|
|
|
app.model(Color);
|
2014-05-03 04:19:14 +00:00
|
|
|
Color.attachTo(db);
|
2014-05-19 22:56:26 +00:00
|
|
|
var classes = app.remotes().classes().map(function(c) {return c.name});
|
|
|
|
expect(classes).to.contain('color');
|
2014-01-22 10:22:23 +00:00
|
|
|
});
|
|
|
|
|
2014-06-09 23:31:33 +00:00
|
|
|
it('registers existing models to app.models', function() {
|
|
|
|
var Color = db.createModel('color', {name: String});
|
|
|
|
app.model(Color);
|
2014-06-10 06:53:01 +00:00
|
|
|
expect(Color.app).to.be.equal(app);
|
|
|
|
expect(Color.shared).to.equal(true);
|
|
|
|
expect(app.models.color).to.equal(Color);
|
|
|
|
expect(app.models.Color).to.equal(Color);
|
2014-06-09 23:31:33 +00:00
|
|
|
});
|
|
|
|
|
2014-07-25 00:00:27 +00:00
|
|
|
it("emits a `modelRemoted` event", function() {
|
|
|
|
var Color = PersistedModel.extend('color', {name: String});
|
|
|
|
Color.shared = true;
|
|
|
|
var remotedClass;
|
|
|
|
app.on('modelRemoted', function(sharedClass) {
|
|
|
|
remotedClass = sharedClass;
|
|
|
|
});
|
|
|
|
app.model(Color);
|
|
|
|
expect(remotedClass).to.exist;
|
|
|
|
expect(remotedClass).to.eql(Color.sharedClass);
|
|
|
|
});
|
|
|
|
|
2014-06-03 08:39:54 +00:00
|
|
|
it.onServer('updates REST API when a new model is added', function(done) {
|
2014-02-19 19:40:05 +00:00
|
|
|
app.use(loopback.rest());
|
|
|
|
request(app).get('/colors').expect(404, function(err, res) {
|
|
|
|
if (err) return done(err);
|
2014-06-05 07:45:09 +00:00
|
|
|
var Color = PersistedModel.extend('color', {name: String});
|
2014-02-19 19:40:05 +00:00
|
|
|
app.model(Color);
|
2014-05-03 04:19:14 +00:00
|
|
|
Color.attachTo(db);
|
2014-02-19 19:40:05 +00:00
|
|
|
request(app).get('/colors').expect(200, done);
|
|
|
|
});
|
2014-02-18 20:40:35 +00:00
|
|
|
});
|
2013-06-06 00:11:21 +00:00
|
|
|
});
|
2013-06-07 19:57:51 +00:00
|
|
|
|
2014-05-25 14:27:45 +00:00
|
|
|
describe('app.model(name, config)', function () {
|
|
|
|
var app;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
|
|
|
app = loopback();
|
2014-06-25 11:52:20 +00:00
|
|
|
app.dataSource('db', {
|
|
|
|
connector: 'memory'
|
2013-10-29 21:12:23 +00:00
|
|
|
});
|
2014-05-25 14:27:45 +00:00
|
|
|
});
|
2013-10-29 21:12:23 +00:00
|
|
|
|
2014-05-25 14:27:45 +00:00
|
|
|
it('Sugar for defining a fully built model', function () {
|
2013-10-29 21:12:23 +00:00
|
|
|
app.model('foo', {
|
|
|
|
dataSource: 'db'
|
|
|
|
});
|
|
|
|
|
|
|
|
var Foo = app.models.foo;
|
2014-05-25 14:27:45 +00:00
|
|
|
var f = new Foo();
|
2013-10-29 21:12:23 +00:00
|
|
|
|
|
|
|
assert(f instanceof loopback.Model);
|
|
|
|
});
|
2014-05-25 14:27:45 +00:00
|
|
|
|
|
|
|
it('interprets extra first-level keys as options', function() {
|
|
|
|
app.model('foo', {
|
|
|
|
dataSource: 'db',
|
|
|
|
base: 'User'
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(app.models.foo.definition.settings.base).to.equal('User');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('prefers config.options.key over config.key', function() {
|
|
|
|
app.model('foo', {
|
|
|
|
dataSource: 'db',
|
|
|
|
base: 'User',
|
|
|
|
options: {
|
|
|
|
base: 'Application'
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(app.models.foo.definition.settings.base).to.equal('Application');
|
|
|
|
});
|
2014-06-10 06:53:01 +00:00
|
|
|
|
|
|
|
it('honors config.public options', function() {
|
|
|
|
app.model('foo', {
|
|
|
|
dataSource: 'db',
|
|
|
|
public: false
|
|
|
|
});
|
|
|
|
expect(app.models.foo.app).to.equal(app);
|
|
|
|
expect(app.models.foo.shared).to.equal(false);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('defaults config.public to be true', function() {
|
|
|
|
app.model('foo', {
|
|
|
|
dataSource: 'db'
|
|
|
|
});
|
|
|
|
expect(app.models.foo.app).to.equal(app);
|
|
|
|
expect(app.models.foo.shared).to.equal(true);
|
|
|
|
});
|
|
|
|
|
2013-12-12 03:31:16 +00:00
|
|
|
});
|
2013-10-29 21:12:23 +00:00
|
|
|
|
Add createModelFromConfig and configureModel()
Add new API allowing developers to split the model definition and
configuration into two steps:
1. Build models from JSON config, export them for re-use:
```js
var Customer = loopback.createModelFromConfig({
name: 'Customer',
base: 'User',
properties: {
address: 'string'
}
});
```
2. Attach existing models to a dataSource and a loopback app,
modify certain model aspects like relations:
```js
loopback.configureModel(Customer, {
dataSource: db,
relations: { /* ... */ }
});
```
Rework `app.model` to use `loopback.configureModel` under the hood.
Here is the new usage:
```js
var Customer = require('./models').Customer;
app.model(Customer, {
dataSource: 'db',
relations: { /* ... */ }
});
```
In order to preserve backwards compatibility with loopback 1.x,
`app.model(name, config)` calls both `createModelFromConfig`
and `configureModel`.
2014-06-05 15:41:12 +00:00
|
|
|
describe('app.model(ModelCtor, config)', function() {
|
|
|
|
it('attaches the model to a datasource', function() {
|
|
|
|
app.dataSource('db', { connector: 'memory' });
|
|
|
|
var TestModel = loopback.Model.extend('TestModel');
|
|
|
|
// TestModel was most likely already defined in a different test,
|
|
|
|
// thus TestModel.dataSource may be already set
|
|
|
|
delete TestModel.dataSource;
|
|
|
|
|
|
|
|
app.model(TestModel, { dataSource: 'db' });
|
|
|
|
|
|
|
|
expect(app.models.TestModel.dataSource).to.equal(app.dataSources.db);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-02-04 19:28:19 +00:00
|
|
|
describe('app.models', function() {
|
|
|
|
it('is unique per app instance', function() {
|
2014-05-27 12:33:42 +00:00
|
|
|
app.dataSource('db', { connector: 'memory' });
|
2014-02-04 19:28:19 +00:00
|
|
|
var Color = app.model('Color', { dataSource: 'db' });
|
|
|
|
expect(app.models.Color).to.equal(Color);
|
|
|
|
var anotherApp = loopback();
|
|
|
|
expect(anotherApp.models.Color).to.equal(undefined);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-05-27 12:33:42 +00:00
|
|
|
describe('app.dataSources', function() {
|
|
|
|
it('is unique per app instance', function() {
|
|
|
|
app.dataSource('ds', { connector: 'memory' });
|
|
|
|
expect(app.datasources.ds).to.not.equal(undefined);
|
|
|
|
var anotherApp = loopback();
|
|
|
|
expect(anotherApp.datasources.ds).to.equal(undefined);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-05-28 13:02:55 +00:00
|
|
|
describe('app.dataSource', function() {
|
|
|
|
it('looks up the connector in `app.connectors`', function() {
|
|
|
|
app.connector('custom', loopback.Memory);
|
|
|
|
app.dataSource('custom', { connector: 'custom' });
|
|
|
|
expect(app.dataSources.custom.name).to.equal(loopback.Memory.name);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-06-03 08:39:54 +00:00
|
|
|
describe.onServer('listen()', function() {
|
2014-01-08 14:20:17 +00:00
|
|
|
it('starts http server', function(done) {
|
|
|
|
var app = loopback();
|
|
|
|
app.set('port', 0);
|
|
|
|
app.get('/', function(req, res) { res.send(200, 'OK'); });
|
|
|
|
|
|
|
|
var server = app.listen();
|
|
|
|
|
|
|
|
expect(server).to.be.an.instanceOf(require('http').Server);
|
|
|
|
|
|
|
|
request(server)
|
|
|
|
.get('/')
|
|
|
|
.expect(200, done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('updates port on "listening" event', function(done) {
|
|
|
|
var app = loopback();
|
|
|
|
app.set('port', 0);
|
|
|
|
|
|
|
|
app.listen(function() {
|
|
|
|
expect(app.get('port'), 'port').to.not.equal(0);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-07-01 12:27:02 +00:00
|
|
|
it('updates "url" on "listening" event', function(done) {
|
|
|
|
var app = loopback();
|
|
|
|
app.set('port', 0);
|
|
|
|
app.set('host', undefined);
|
|
|
|
|
|
|
|
app.listen(function() {
|
|
|
|
expect(app.get('url'), 'url')
|
|
|
|
.to.equal('http://127.0.0.1:' + app.get('port') + '/');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-01-08 14:20:17 +00:00
|
|
|
it('forwards to http.Server.listen on more than one arg', function(done) {
|
|
|
|
var app = loopback();
|
|
|
|
app.set('port', 1);
|
|
|
|
app.listen(0, '127.0.0.1', function() {
|
|
|
|
expect(app.get('port'), 'port').to.not.equal(0).and.not.equal(1);
|
|
|
|
expect(this.address().address).to.equal('127.0.0.1');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('forwards to http.Server.listen when the single arg is not a function',
|
|
|
|
function(done) {
|
|
|
|
var app = loopback();
|
|
|
|
app.set('port', 1);
|
|
|
|
app.listen(0).on('listening', function() {
|
|
|
|
expect(app.get('port'), 'port') .to.not.equal(0).and.not.equal(1);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
it('uses app config when no parameter is supplied', function(done) {
|
|
|
|
var app = loopback();
|
|
|
|
// Http listens on all interfaces by default
|
|
|
|
// Custom host serves as an indicator whether
|
|
|
|
// the value was used by app.listen
|
|
|
|
app.set('host', '127.0.0.1');
|
|
|
|
app.listen()
|
|
|
|
.on('listening', function() {
|
|
|
|
expect(this.address().address).to.equal('127.0.0.1');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-06-03 08:39:54 +00:00
|
|
|
describe.onServer('enableAuth', function() {
|
2014-02-05 17:46:22 +00:00
|
|
|
it('should set app.isAuthEnabled to true', function() {
|
|
|
|
expect(app.isAuthEnabled).to.not.equal(true);
|
|
|
|
app.enableAuth();
|
|
|
|
expect(app.isAuthEnabled).to.equal(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-06-03 08:39:54 +00:00
|
|
|
describe.onServer('app.get("/", loopback.status())', function () {
|
2013-11-19 20:35:29 +00:00
|
|
|
it('should return the status of the application', function (done) {
|
|
|
|
var app = loopback();
|
|
|
|
app.get('/', loopback.status());
|
|
|
|
request(app)
|
|
|
|
.get('/')
|
|
|
|
.expect(200)
|
|
|
|
.end(function(err, res) {
|
|
|
|
if(err) return done(err);
|
|
|
|
|
|
|
|
assert.equal(typeof res.body, 'object');
|
|
|
|
assert(res.body.started);
|
2014-02-19 23:14:31 +00:00
|
|
|
// The number can be 0
|
|
|
|
assert(res.body.uptime !== undefined);
|
2013-11-19 20:35:29 +00:00
|
|
|
|
|
|
|
var elapsed = Date.now() - Number(new Date(res.body.started));
|
|
|
|
|
|
|
|
// elapsed should be a positive number...
|
|
|
|
assert(elapsed > 0);
|
|
|
|
|
|
|
|
// less than 100 milliseconds
|
|
|
|
assert(elapsed < 100);
|
|
|
|
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2014-05-28 13:02:55 +00:00
|
|
|
|
|
|
|
describe('app.connectors', function() {
|
|
|
|
it('is unique per app instance', function() {
|
|
|
|
app.connectors.foo = 'bar';
|
|
|
|
var anotherApp = loopback();
|
|
|
|
expect(anotherApp.connectors.foo).to.equal(undefined);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('includes Remote connector', function() {
|
|
|
|
expect(app.connectors.remote).to.equal(loopback.Remote);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('includes Memory connector', function() {
|
|
|
|
expect(app.connectors.memory).to.equal(loopback.Memory);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('app.connector', function() {
|
|
|
|
// any connector will do
|
|
|
|
it('adds the connector to the registry', function() {
|
|
|
|
app.connector('foo-bar', loopback.Memory);
|
|
|
|
expect(app.connectors['foo-bar']).to.equal(loopback.Memory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('adds a classified alias', function() {
|
|
|
|
app.connector('foo-bar', loopback.Memory);
|
|
|
|
expect(app.connectors.FooBar).to.equal(loopback.Memory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('adds a camelized alias', function() {
|
|
|
|
app.connector('FOO-BAR', loopback.Memory);
|
|
|
|
expect(app.connectors.FOOBAR).to.equal(loopback.Memory);
|
|
|
|
});
|
|
|
|
});
|
2014-06-03 08:39:54 +00:00
|
|
|
|
|
|
|
describe('app.settings', function() {
|
|
|
|
it('can be altered via `app.set(key, value)`', function() {
|
|
|
|
app.set('write-key', 'write-value');
|
|
|
|
expect(app.settings).to.have.property('write-key', 'write-value');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('can be read via `app.get(key)`', function() {
|
|
|
|
app.settings['read-key'] = 'read-value';
|
|
|
|
expect(app.get('read-key')).to.equal('read-value');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('is unique per app instance', function() {
|
|
|
|
var app1 = loopback();
|
|
|
|
var app2 = loopback();
|
|
|
|
|
|
|
|
expect(app1.settings).to.not.equal(app2.settings);
|
|
|
|
|
|
|
|
app1.set('key', 'value');
|
|
|
|
expect(app2.get('key'), 'app2 value').to.equal(undefined);
|
|
|
|
});
|
|
|
|
});
|
2014-06-14 07:40:57 +00:00
|
|
|
|
2014-06-13 08:27:23 +00:00
|
|
|
it('exposes loopback as a property', function() {
|
|
|
|
var app = loopback();
|
|
|
|
expect(app.loopback).to.equal(loopback);
|
|
|
|
});
|
2013-10-29 21:12:23 +00:00
|
|
|
});
|