Files
elasticsearch-js/test/unit/test_http_connector.js
Spencer Alger 18e134d0a6 Summary of Changes:
- moved es install/start/stop logic into a seperate script
  - `grunt test` now runs the integration tests once for each version of ES we support
  - grunt can now install and run elasticearch (using grunt-run, pure js solution coming later)
  - included seperate es.sh script specifically for starting or stopping elasticsearch
  - url aliases, api, yaml_suite/index.js, and yaml_tests.json, are all now duplicated for 0_90 support
  - the client now accepts an apiVersion argument (undocumented) which defaults to 'master' but can be '0.90'
  - The yaml test runner will now check the name of the ES instance it is connecting to, preventing accidental wiping of ES
2014-01-14 23:10:12 -07:00

373 lines
11 KiB
JavaScript

describe('Http Connector', function () {
var expect = require('expect.js');
var Host = require('../../src/lib/host');
var errors = require('../../src/lib/errors');
var HttpConnection = require('../../src/lib/connectors/http');
var ConnectionAbstract = require('../../src/lib/connection');
var nock = require('nock');
var sinon = require('sinon');
var util = require('util');
var KeepAliveAgent = require('agentkeepalive');
var http = require('http');
var https = require('https');
var MockRequest = require('../mocks/request');
var MockIncommingMessage = require('../mocks/incomming_message');
nock.disableNetConnect();
var stub = require('../utils/auto_release_stub').make();
function makeStubReqMethod(prep) {
return function (params, cb) {
var req = new MockRequest();
if (prep) {
prep(req, params, cb);
}
return req;
};
}
function whereReqDies(withErr) {
return function (req) {
process.nextTick(function () {
// causes the request to quit and callback
req.emit('error', withErr || void 0);
});
};
}
describe('Constructor', function () {
it('creates an object that extends ConnectionAbstract', function () {
var con = new HttpConnection(new Host());
expect(con).to.be.a(ConnectionAbstract);
});
it('sets certain defaults', function () {
var con = new HttpConnection(new Host());
expect(con.hand).to.be(require('http'));
// con.requestTimeout
// maxSockets
// maxFreeSockets
// maxKeepAliveTime
// requestTimeout
});
it('expects the host to have a protocol of http or https', function () {
expect(function () {
var con = new HttpConnection(new Host('thrifty://es.com/stuff'));
}).to.throwError(/invalid protocol/i);
});
});
describe('#makeReqParams', function () {
it('properly reads the host object', function () {
var host = new Host('john:dude@pizza.com:9200/pizza/cheese?shrooms=true');
var con = new HttpConnection(host, {});
var reqParams = con.makeReqParams();
expect(reqParams).to.eql({
method: 'GET',
protocol: 'http:',
auth: 'john:dude',
hostname: 'pizza.com',
port: 9200,
path: '/pizza/cheese?shrooms=true',
headers: host.headers,
agent: con.agent
});
});
it('merges a query object with the hosts\'', function () {
var con = new HttpConnection(new Host({
query: {
user_id: 123
}
}));
var reqParams = con.makeReqParams({
query: {
jvm: 'yes'
}
});
expect(reqParams.path).to.eql('/?user_id=123&jvm=yes');
});
it('merges the path prefex', function () {
var con = new HttpConnection(new Host('https://google.com/path/prefix/for/user/1'));
var reqParams = con.makeReqParams({
method: 'GET',
path: '/items',
query: {
q: 'pizza'
}
});
expect(reqParams).to.eql({
method: 'GET',
protocol: 'https:',
auth: null,
hostname: 'google.com',
port: 443,
path: '/path/prefix/for/user/1/items?q=pizza',
headers: undefined,
agent: con.agent
});
});
it('merges the query', function () {
var con = new HttpConnection(new Host('http://google.com/pref-x?userId=12345&token=42069'));
var reqParams = con.makeReqParams({
method: 'PUT',
path: '/stuff',
query: {
q: 'pizza'
}
});
expect(reqParams).to.eql({
method: 'PUT',
protocol: 'http:',
auth: null,
hostname: 'google.com',
port: 80,
path: '/pref-x/stuff?userId=12345&token=42069&q=pizza',
headers: undefined,
agent: con.agent
});
});
it('Works well with minimum params', function () {
var con = new HttpConnection(new Host('http://google.com'));
var reqParams = con.makeReqParams({
method: 'PUT',
path: '/stuff'
});
expect(reqParams).to.eql({
method: 'PUT',
protocol: 'http:',
auth: null,
hostname: 'google.com',
port: 80,
path: '/stuff',
headers: undefined,
agent: con.agent
});
});
});
describe('#request', function () {
beforeEach(function () {
stub(http, 'request', makeStubReqMethod(whereReqDies()));
stub(https, 'request', makeStubReqMethod(whereReqDies()));
});
it('calls http based on the host', function (done) {
var con = new HttpConnection(new Host('http://google.com'));
con.request({}, function () {
expect(http.request.callCount).to.be(1);
expect(https.request.callCount).to.be(0);
expect(http.request.lastCall.args[0].agent).to.be.a(KeepAliveAgent);
done();
});
});
it('calls https based on the host', function (done) {
var con = new HttpConnection(new Host('https://google.com'));
con.request({}, function () {
expect(http.request.callCount).to.be(0);
expect(https.request.callCount).to.be(1);
expect(https.request.lastCall.args[0].agent).to.be.a(KeepAliveAgent.HttpsAgent);
done();
});
});
it('does not log error events', function (done) {
var con = new HttpConnection(new Host('http://google.com'));
stub(con.log, 'error');
stub(con.log, 'trace');
stub(con.log, 'info');
stub(con.log, 'warning');
stub(con.log, 'debug');
http.request.restore();
stub(http, 'request', makeStubReqMethod(whereReqDies(new Error('actual error'))));
con.request({}, function (err) {
// error should have been sent to the
expect(err.message).to.eql('actual error');
// logged the error and the trace log
expect(con.log.trace.callCount).to.eql(1);
expect(con.log.error.callCount).to.eql(0);
expect(con.log.info.callCount).to.eql(0);
expect(con.log.warning.callCount).to.eql(0);
expect(con.log.debug.callCount).to.eql(0);
done();
});
});
it('logs error events', function (done) {
var con = new HttpConnection(new Host('http://google.com'));
stub(con.log, 'error');
http.request.func = makeStubReqMethod(whereReqDies(new Error('actual error')));
con.request({}, function (err) {
// error should have been sent to the
expect(err.message).to.eql('actual error');
// logged the error
expect(con.log.error.callCount).to.eql(0);
done();
});
});
});
describe('#request with incomming message error', function () {
function makeStubReqWithMsgWhichErrorsMidBody(err) {
return makeStubReqMethod(function (req, params, cb) {
process.nextTick(function () {
var incom = new MockIncommingMessage();
incom.statusCode = 200;
setTimeout(function () {
incom.emit('data', '{ "not json"');
incom.emit('error', err || new Error('Socket is dead now...'));
}, 20);
cb(incom);
});
});
}
it('does not log errors', function (done) {
var con = new HttpConnection(new Host('https://google.com'));
stub(con.log, 'error');
stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody());
con.request({}, function (err, resp, status) {
expect(con.log.error.callCount).to.eql(0);
done();
});
});
it('passes the original error on', function (done) {
var con = new HttpConnection(new Host('https://google.com'));
stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody(new Error('no more message :(')));
con.request({}, function (err, resp, status) {
expect(err).to.be.an(Error);
expect(err.message).to.eql('no more message :(');
done();
});
});
it('does not pass the partial body along', function (done) {
var con = new HttpConnection(new Host('https://google.com'));
stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody());
con.request({}, function (err, resp, status) {
expect(resp).to.be(undefined);
done();
});
});
it('does not pass the status code along', function (done) {
var con = new HttpConnection(new Host('https://google.com'));
stub(https, 'request', makeStubReqWithMsgWhichErrorsMidBody());
con.request({}, function (err, resp, status) {
expect(status).to.be(undefined);
done();
});
});
});
describe('#request\'s responder', function () {
it('collects the whole request body', function (done) {
var server = nock('http://esjs.com:9200');
var con = new HttpConnection(new Host('http://esjs.com:9200'));
var body = '{ "USER": "doc" }';
server
.get('/users/1')
.reply(200, body);
con.request({
method: 'GET',
path: '/users/1'
}, function (err, resp, status) {
expect(err).to.be(undefined);
expect(resp).to.eql(body);
expect(status).to.eql(200);
server.done();
done();
});
});
it('Ignores serialization errors', function (done) {
var server = nock('http://esjs.com:9200');
var con = new HttpConnection(new Host('http://esjs.com:9200'));
var body = '{ "USER":';
// partial body
server
.get('/users/1')
.reply(200, body);
con.request({
method: 'GET',
path: '/users/1'
}, function (err, resp, status) {
expect(err).to.be(undefined);
expect(resp).to.eql(body);
expect(status).to.eql(200);
done();
});
});
});
describe('HTTP specifics', function () {
it('uses TCP no delay', function (done) {
var con = new HttpConnection(new Host('localhost'));
stub(http.ClientRequest.prototype, 'setNoDelay');
var server = nock('http://localhost').get('/').reply(200);
con.request({}, function (err, resp, status) {
expect(http.ClientRequest.prototype.setNoDelay.callCount).to.eql(1);
expect(http.ClientRequest.prototype.setNoDelay.lastCall.args[0]).to.eql(true);
server.done();
done();
});
});
it('sets the Content-Length header properly', function (done) {
var con = new HttpConnection(new Host('localhost'));
stub(http.ClientRequest.prototype, 'setHeader');
var server = nock('http://localhost').get('/').reply(200);
var body = 'pasta and 𝄞';
expect(body.length).to.eql(12); // nope
expect(Buffer.byteLength(body, 'utf8')).to.eql(14); // yep
con.request({
body: body
}, function (err, resp, status) {
expect(http.ClientRequest.prototype.setHeader.lastCall.args).to.eql(['Content-Length', 14]);
server.done();
done();
});
});
});
});