341 lines
9.8 KiB
JavaScript
341 lines
9.8 KiB
JavaScript
describe('loopback.rest', function() {
|
|
var MyModel;
|
|
beforeEach(function() {
|
|
var ds = app.dataSource('db', { connector: loopback.Memory });
|
|
MyModel = ds.createModel('MyModel', {name: String});
|
|
loopback.autoAttach();
|
|
});
|
|
|
|
it('works out-of-the-box', function(done) {
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
request(app).get('/mymodels')
|
|
.expect(200)
|
|
.end(done);
|
|
});
|
|
|
|
it('should report 404 for GET /:id not found', function(done) {
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
request(app).get('/mymodels/1')
|
|
.expect(404)
|
|
.end(function(err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
var errorResponse = res.body.error;
|
|
assert(errorResponse);
|
|
assert.equal(errorResponse.code, 'MODEL_NOT_FOUND');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should report 404 for HEAD /:id not found', function(done) {
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
request(app).head('/mymodels/1')
|
|
.expect(404)
|
|
.end(done);
|
|
});
|
|
|
|
it('should report 200 for GET /:id/exists not found', function(done) {
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
request(app).get('/mymodels/1/exists')
|
|
.expect(200)
|
|
.end(function(err, res) {
|
|
if (err) return done(err);
|
|
expect(res.body).to.eql({exists: false});
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should report 200 for GET /:id found', function(done) {
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
MyModel.create({name: 'm1'}, function(err, inst) {
|
|
request(app).get('/mymodels/' + inst.id)
|
|
.expect(200)
|
|
.end(done);
|
|
});
|
|
});
|
|
|
|
it('should report 200 for HEAD /:id found', function(done) {
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
MyModel.create({name: 'm2'}, function(err, inst) {
|
|
request(app).head('/mymodels/' + inst.id)
|
|
.expect(200)
|
|
.end(done);
|
|
});
|
|
});
|
|
|
|
it('should report 200 for GET /:id/exists found', function(done) {
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
MyModel.create({name: 'm2'}, function(err, inst) {
|
|
request(app).get('/mymodels/' + inst.id + '/exists')
|
|
.expect(200)
|
|
.end(function(err, res) {
|
|
if (err) return done(err);
|
|
expect(res.body).to.eql({exists: true});
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should honour `remoting.rest.supportedTypes`', function(done) {
|
|
var app = loopback();
|
|
|
|
// NOTE it is crucial to set `remoting` before creating any models
|
|
var supportedTypes = ['json', 'application/javascript', 'text/javascript'];
|
|
app.set('remoting', { rest: { supportedTypes: supportedTypes } });
|
|
|
|
app.model(MyModel);
|
|
app.use(loopback.rest());
|
|
|
|
request(app).get('/mymodels')
|
|
.set('Accept', 'text/html,application/xml;q= 0.9,*/*;q= 0.8')
|
|
.expect('Content-Type', 'application/json; charset=utf-8')
|
|
.expect(200, done);
|
|
});
|
|
|
|
it('allows models to provide a custom HTTP path', function(done) {
|
|
var ds = app.dataSource('db', { connector: loopback.Memory });
|
|
var CustomModel = ds.createModel('CustomModel',
|
|
{ name: String },
|
|
{ http: { 'path': 'domain1/CustomModelPath' }
|
|
});
|
|
|
|
app.model(CustomModel);
|
|
app.use(loopback.rest());
|
|
|
|
request(app).get('/domain1/CustomModelPath').expect(200).end(done);
|
|
});
|
|
|
|
it('should report 200 for url-encoded HTTP path', function(done) {
|
|
var ds = app.dataSource('db', { connector: loopback.Memory });
|
|
var CustomModel = ds.createModel('CustomModel',
|
|
{ name: String },
|
|
{ http: { path: 'domain%20one/CustomModelPath' }
|
|
});
|
|
|
|
app.model(CustomModel);
|
|
app.use(loopback.rest());
|
|
|
|
request(app).get('/domain%20one/CustomModelPath').expect(200).end(done);
|
|
});
|
|
|
|
it('includes loopback.token when necessary', function(done) {
|
|
givenUserModelWithAuth();
|
|
app.enableAuth();
|
|
app.use(loopback.rest());
|
|
|
|
givenLoggedInUser(function(err, token) {
|
|
if (err) return done(err);
|
|
expect(token).instanceOf(app.models.accessToken);
|
|
request(app).get('/users/' + token.userId)
|
|
.set('Authorization', token.id)
|
|
.expect(200)
|
|
.end(done);
|
|
}, done);
|
|
});
|
|
|
|
it('does not include loopback.token when auth not enabled', function(done) {
|
|
var User = givenUserModelWithAuth();
|
|
User.getToken = function(req, cb) {
|
|
cb(null, req.accessToken ? req.accessToken.id : null);
|
|
};
|
|
loopback.remoteMethod(User.getToken, {
|
|
accepts: [{ type: 'object', http: { source: 'req' } }],
|
|
returns: [{ type: 'object', name: 'id' }]
|
|
});
|
|
|
|
app.use(loopback.rest());
|
|
givenLoggedInUser(function(err, token) {
|
|
if (err) return done(err);
|
|
request(app).get('/users/getToken')
|
|
.set('Authorization', token.id)
|
|
.expect(200)
|
|
.end(function(err, res) {
|
|
if (err) return done(err);
|
|
expect(res.body.id).to.equal(null);
|
|
done();
|
|
});
|
|
}, done);
|
|
});
|
|
|
|
it('should report 200 for legacy explorer route /routes', function(done) {
|
|
app.use(loopback.rest());
|
|
request(app).get('/routes')
|
|
.expect(200)
|
|
.end(function(err, res) {
|
|
if (err) return done(err);
|
|
expect(res.body).to.eql([]);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should report 200 for legacy explorer route /models', function(done) {
|
|
app.use(loopback.rest());
|
|
request(app).get('/models')
|
|
.expect(200)
|
|
.end(function(err, res) {
|
|
if (err) return done(err);
|
|
expect(res.body).to.eql({});
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should report 404 for disabled legacy explorer route /routes', function(done) {
|
|
app.set('legacyExplorer', false);
|
|
app.use(loopback.rest());
|
|
request(app).get('/routes')
|
|
.expect(404)
|
|
.end(done);
|
|
});
|
|
|
|
it('should report 404 for disabled legacy explorer route /models', function(done) {
|
|
app.set('legacyExplorer', false);
|
|
app.use(loopback.rest());
|
|
request(app).get('/models')
|
|
.expect(404)
|
|
.end(done);
|
|
});
|
|
|
|
describe('context propagation', function() {
|
|
var User;
|
|
|
|
beforeEach(function() {
|
|
User = givenUserModelWithAuth();
|
|
User.getToken = function(cb) {
|
|
var context = loopback.getCurrentContext();
|
|
var req = context.get('http').req;
|
|
expect(req).to.have.property('accessToken');
|
|
|
|
var juggler = require('loopback-datasource-juggler');
|
|
expect(juggler.getCurrentContext().get('http').req)
|
|
.to.have.property('accessToken');
|
|
|
|
var remoting = require('strong-remoting');
|
|
expect(remoting.getCurrentContext().get('http').req)
|
|
.to.have.property('accessToken');
|
|
|
|
cb(null, req && req.accessToken ? req.accessToken.id : null);
|
|
};
|
|
// Set up the ACL
|
|
User.settings.acls.push({principalType: 'ROLE',
|
|
principalId: '$authenticated', permission: 'ALLOW',
|
|
property: 'getToken'});
|
|
|
|
loopback.remoteMethod(User.getToken, {
|
|
accepts: [],
|
|
returns: [
|
|
{ type: 'object', name: 'id' }
|
|
]
|
|
});
|
|
});
|
|
|
|
function invokeGetToken(done) {
|
|
givenLoggedInUser(function(err, token) {
|
|
if (err) return done(err);
|
|
request(app).get('/users/getToken')
|
|
.set('Authorization', token.id)
|
|
.expect(200)
|
|
.end(function(err, res) {
|
|
if (err) return done(err);
|
|
expect(res.body.id).to.equal(token.id);
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
|
|
it('should enable context using loopback.context', function(done) {
|
|
app.use(loopback.context({ enableHttpContext: true }));
|
|
app.enableAuth();
|
|
app.use(loopback.rest());
|
|
|
|
invokeGetToken(done);
|
|
});
|
|
|
|
it('should enable context with loopback.rest', function(done) {
|
|
app.enableAuth();
|
|
app.set('remoting', { context: { enableHttpContext: true } });
|
|
app.use(loopback.rest());
|
|
|
|
invokeGetToken(done);
|
|
});
|
|
|
|
it('should support explicit context', function(done) {
|
|
app.enableAuth();
|
|
app.use(loopback.context());
|
|
app.use(loopback.token(
|
|
{ model: loopback.getModelByType(loopback.AccessToken) }));
|
|
app.use(function(req, res, next) {
|
|
loopback.getCurrentContext().set('accessToken', req.accessToken);
|
|
next();
|
|
});
|
|
app.use(loopback.rest());
|
|
|
|
User.getToken = function(cb) {
|
|
var context = loopback.getCurrentContext();
|
|
var accessToken = context.get('accessToken');
|
|
expect(context.get('accessToken')).to.have.property('id');
|
|
|
|
var juggler = require('loopback-datasource-juggler');
|
|
context = juggler.getCurrentContext();
|
|
expect(context.get('accessToken')).to.have.property('id');
|
|
|
|
var remoting = require('strong-remoting');
|
|
context = remoting.getCurrentContext();
|
|
expect(context.get('accessToken')).to.have.property('id');
|
|
|
|
cb(null, accessToken ? accessToken.id : null);
|
|
};
|
|
|
|
loopback.remoteMethod(User.getToken, {
|
|
accepts: [],
|
|
returns: [
|
|
{ type: 'object', name: 'id' }
|
|
]
|
|
});
|
|
|
|
invokeGetToken(done);
|
|
});
|
|
});
|
|
|
|
function givenUserModelWithAuth() {
|
|
// NOTE(bajtos) It is important to create a custom AccessToken model here,
|
|
// in order to overwrite the entry created by previous tests in
|
|
// the global model registry
|
|
app.model('accessToken', {
|
|
options: {
|
|
base: 'AccessToken'
|
|
},
|
|
dataSource: 'db'
|
|
});
|
|
return app.model('user', {
|
|
options: {
|
|
base: 'User',
|
|
relations: {
|
|
accessTokens: {
|
|
model: 'accessToken',
|
|
type: 'hasMany',
|
|
foreignKey: 'userId'
|
|
}
|
|
}
|
|
},
|
|
dataSource: 'db'
|
|
});
|
|
}
|
|
function givenLoggedInUser(cb, done) {
|
|
var credentials = { email: 'user@example.com', password: 'pwd' };
|
|
var User = app.models.user;
|
|
User.create(credentials,
|
|
function(err, user) {
|
|
if (err) return done(err);
|
|
User.login(credentials, cb);
|
|
});
|
|
}
|
|
});
|