Strongloop Loopback remote hooks not triggered with supertest? - javascript

We are testing our loopback API code using spec.js files like this:
Require libs:
var app = rewire('../..');
var request = require('supertest');
var assert = require('chai').assert;
json helper method to standardize headers and content type:
function json(verb, url) {
return request(app)[verb](url)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect('Content-Type', /json/);
}
A test of a custom remote method that requires auth:
describe("Order remote methods", function() {
var accessTokenId, userId;
// authenticate before each test and save token
before(function(done) {
json('post', '/api/People/login')
.send({ email: 'user#email.com', password: 'password' })
.expect(200)
.end(function(err, res) {
accessTokenId = res.body.id;
userId = res.body.userId;
assert(res.body.id);
assert(res.body.userId);
done();
});
});
it("should fetch user orders", function(done) {
json('get', '/api/Orders/specialOrders')
.set('Authorization', accessTokenId)
.send({id: userId})
.expect(200)
.end(function(err, res) {
var orders = res.body.orders;
assert(Array.isArray(orders), "Orders should be an array");
// more asserts for explicit data values
done();
});
});
});
/api/Orders/specialOrders is a custom remote method that does a custom query on the Order model, which works as expected. But when I add a beforeRemote hook for this model, it does not get triggered by running the test. Is this expected or is my test setup not complete?
Remote hook:
Order.beforeRemote('specialOrders', function(ctx, unused, next) {
console.log('[userOrders]');
console.log('ctx req token: ', ctx.req.accessToken.userId);
console.log('ctx args: ', ctx.req.params.id);
// prevent remote method from being called
// even without a next(), remote is executed!
next(new Error('testing error'));
});
Running the same custom method via the Explorer UI, the beforeRemote hook is triggered as expected, and reports the custom error (or hangs when the next() is not present).
Is it possible to get supertest to trigger remote hooks in tests like this or am I missing some app setup in the spec file?

Related

How to test req.query using supertest node.js?

I have this code on supertest framework for tests:
it('GET normal pending transfer with receiverId', (done) => {
supertest(app)
.get('/transfers/pending')
.set('Accept', 'application/json')
.expect(200)
.query({
senderId: clientSenderId,
})
.expect('Content-Type', 'application/json; charset=utf-8')
.then((res) => {
console.log('res.body', res.body);
done();
})
.catch(done);
});
This endpoint - /transfers/pending takes query this way:
const { receiverId, senderId } = req.query;
As you can see, this is req.query and I want to send this query in my test's code.
I was trying to use this:
.get('/transfers/pending?senderId=${clientSenderId}')
And this:
.query({
senderId: clientSenderId,
})
And nothing of this isn't working. I mean, I got 500 and, the most important, I get an error in message, that belongs to other endpoint. It looks like my code triggers other endpoint, not that /transfers/pending.
My question is, how can I send queries in tests. With params and bodies everything works just fine, but not with queries.

nodeJs basic authentication issue

I'm getting no proper response while make an API request to external API using basic authentication (username and password) in nodejs (javascript)
I used the below code and the response is "undefined", not sure what is missing here.
But I was able to make a request using postman tool without any issues.
const request = require('request')
const user = '*****';
const pass = '*****!';
const url = 'https://servicenow.com/api/table'
var options = {
url: url,
auth: {
username: user,
password: pass
}
};
request.get(options, (err, res, body) => {
if (err) {
return console.log(err);
}
console.log(body.url);
console.log(body.explanation);
});
Response:
undefined
undefined
if your api right with postman you can do like this based on photo
send a request
click on code
select nodejs- Request
copy

Hapi.js - adding mechanism to check every route

I am trying to implement a mechanism that will be run before any route is hit. In that mechanism I want to take a value from the header and check for authentication.
I have come up with this:
server.js:
// Create a server with a host and port
'use strict';
var Hapi = require('hapi');
var mongojs = require('mongojs');
var plugins = [
require('./routes/entities')
];
var server = new Hapi.Server();
server.connection({
port: 3000
});
//Connect to db
server.app.db = mongojs('hapi-rest-mongo', ['entities']);
server.app.checkHeader = function (request) {
var header = request.headers['x-authorization'];
if(header === "letmein"){
return true
}
return false
};
//Load plugins and start server
server.register(plugins, function (err) {
if (err) {
throw err;
}
// Start the server
server.start(function (err) {
console.log('Server running at:', server.info.uri);
});
});
and in routes.entities:
'use strict';
var Boom = require('boom');
var uuid = require('node-uuid');
var Joi = require('joi');
exports.register = function (server, options, next) {
var db = server.app.db;
server.route({
method: 'GET',
path: '/entities',
handler: function handler(request, reply) {
if(!server.app.checkHeader(request))
{
return reply(Boom.unauthorized());
};
//request.server.myFunc();
db.entities.find(function (err, docs) {
if (err) {
return reply(Boom.wrap(err, 'Internal MongoDB error'));
}
reply(docs);
});
}
});
So in short while starting the server I have registered my function server.app.checkHeader
And in the routes I am calling it and sending a request object to it. Request object contains information about the headers.
While this works, I am having a feeling I am not following the best practices with the Hapi.
How could I do it more elegantly?
There are a few options.
You can, of course, tap into the request lifecycle - note the events that occur in the pipeline prior to the route handler.
Although, I'd urge you to consider implementing an auth strategy that can be set as the default for all routes or selectively on appropriate routes.
The best way to require authentication for all or selected route is to use hapi’s integrated functionality.
You should set a default authentication strategy that is applied to each route handler. The sample below uses basic auth. You’d want to create a custom authentication strategy for hapi to check your x-authentication header.
const Hapi = require('hapi')
const BasicAuth = require('hapi-auth-basic')
const server = new Hapi.Server()
server.register(BasicAuth, function (err) {
if (err) {
console.log('error', 'failed to install plugins')
throw err
}
// TODO: add authentication strategy & set as default
server.auth.strategy('simple', 'basic', true, { validateFunc: basicValidationFn })
// or set strategy separately as default auth strategy
server.auth.strategy('simple', 'basic', { validateFunc: basicValidationFn })
server.auth.default('simple')
// TODO: add routes
server.start(function (err) {
})
})
You can also inject hapi’s request lifecycle and extend it at given points. Extending the request lifecycle should be done by using plugins:
register: function (server, options, next) {
// do some processing before 'onPreAuth'
// or pick another extension point
server.ext('onPreAuth', (request, reply) => {
// your functionality
})
}
Hope that helps!

Call a hapi route from another route

I am pretty new to HapiJS. I am building a service where I have two routes /route1 and /route2 both are using the plugin architecture. I have registered both as plugins on my manifest file.
I want to call /route1 from /route2 so /route2 depends on the payload reply from /route1. I've been looking at putting the logic of /route2 on /route1 on the pre-handler but I want to keep them separately.
Don't know how to call a registered plugin from another the thing is that both plugins (routes) are making networks requests. Thanks for reading.
Thanks.
As you specify that you don't want to use a shared handler/route prerequisite (which would be my first choice), you could make an actual request using a http client (Wreck, request, http or the like).
Another, more efficient way that doesn't involve actually making a network request is to use hapi's built-in server.inject() method provided by Shot. This will inject a request into your server and get the response, which you can use. Here's an example:
var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection({ port: 4000 });
var plugin1 = function (server, options, next) {
server.route({
method: 'GET',
path: '/route1',
handler: function (request, reply) {
reply('Hello');
}
});
next();
};
plugin1.attributes = { name: 'plugin1' };
var plugin2 = function (server, options, next) {
server.route({
method: 'GET',
path: '/route2',
handler: function (request, reply) {
server.inject('/route1', function (res) {
reply(res.payload + ' World!');
});
}
});
next();
};
plugin2.attributes = { name: 'plugin2' };
server.register([plugin1, plugin2], function (err) {
if (err) throw err;
server.start(function (err) {
if (err) throw err;
console.log('Started');
});
});
Note that the fact the routes are in plugins here is irrelevant. I've merely included it so it's close to your situation.
Shot and server.inject() are primarily used for testing but there are legitimate runtime uses like this too.
If you make a request to /route2, this will invoke /route1 handler and get the payload:
$ curl localhost:4000/route2
Hello World!

Setting Basic Auth in Mocha and SuperTest

I'm trying to set us a test to verify the username and password of a path blocked by the basic auth of a username and password.
it('should receive a status code of 200 with login', function(done) {
request(url)
.get("/staging")
.expect(200)
.set('Authorization', 'Basic username:password')
.end(function(err, res) {
if (err) {
throw err;
}
done();
});
});
Using the auth method
SuperTest is based on SuperAgent which provides the auth method to facilitate Basic Authentication:
it('should receive a status code of 200 with login', function(done) {
request(url)
.get('/staging')
.auth('the-username', 'the-password')
.expect(200, done);
});
Source: http://visionmedia.github.io/superagent/#basic-authentication
PS: You can pass done straight to any of the .expect() calls
The username:password part must be base64 encoded
You can use something like
.set("Authorization", "basic " + new Buffer("username:password").toString("base64"))

Categories