node-bigcommerce-client
Version:
A node module for authentication and use with the BigCommerce API
208 lines (177 loc) • 6.2 kB
JavaScript
;
const Request = require('../lib/request'),
should = require('chai').should(),
nock = require('nock'),
HttpsAgent = require('agentkeepalive').HttpsAgent,
zlib = require('zlib');
describe('Request', () => {
const self = { };
const request = new Request('api.bigcommerce.com', { headers: { 'Content-Type': 'application/json' } });
afterEach(() => nock.cleanAll());
context('given a missing hostname', () => {
it('should return an error if hostname is missing', () => {
/* eslint no-new: off */
should.Throw(() => {
new Request();
}, Error);
});
});
context('given a 429 status code', () => {
beforeEach(() => {
self.ordersCall = nock('https://api.bigcommerce.com')
.post('/orders')
.reply(429, { }, { 'X-Retry-After': 0.1 })
.post('/orders')
.reply(200, { });
});
it('should retry the request', () => {
return request.run('post', '/orders')
.then(() => self.ordersCall.isDone().should.equal(true));
});
context('given a failOnLimitReached option', () => {
const failRequest = new Request('api.bigcommerce.com', {
headers: { 'Content-Type': 'application/json' },
failOnLimitReached: true
});
it('should return an error', () => {
return failRequest.run('post', '/orders')
.then(() => should.fail('You shall not pass!'))
.catch(e => {
e.message.should.match(/rate limit/);
e.retryAfter.should.equal(0.1);
});
});
});
});
context('given a bad request or internal error is returned', () => {
beforeEach(() => {
nock('https://api.bigcommerce.com')
.post('/orders')
.reply(400, {});
});
it('should return an error', () => {
return request.run('post', '/orders', { })
.then(() => should.fail('You shall not pass!'))
.catch(e => e.message.should.match(/Request returned error code/));
});
});
context('if "error" are found in the response JSON', () => {
beforeEach(() => {
nock('https://api.bigcommerce.com')
.post('/orders')
.reply(200, { error: 'An error has occurred.' });
});
it('should return an error', () => {
return request.run('post', '/orders', { })
.then(() => should.fail('You shall not pass!'))
.catch(e => e.message.should.match(/An error has occurred/));
});
});
context('if "errors" are found in the response JSON', () => {
beforeEach(() => {
nock('https://api.bigcommerce.com')
.post('/orders')
.reply(200, { errors: ['An error has occurred.'] });
});
it('should return an error', () => {
return request.run('post', '/orders', { })
.then(() => should.fail('You shall not pass!'))
.catch(e => e.message.should.match(/An error has occurred/));
});
});
context('given a malformed request JSON', () => {
beforeEach(() => {
nock('https://api.bigcommerce.com')
.defaultReplyHeaders({ 'Content-Type': 'application/json' })
.post('/orders')
.reply(200, '<malformed>');
});
it('should return an error', () => {
return request.run('post', '/orders', { })
.then(() => should.fail('You shall not pass!'))
.catch(e => e.message.should.match(/Unexpected token/));
});
});
context('if json is not returned', () => {
beforeEach(() => {
nock('https://api.bigcommerce.com')
.defaultReplyHeaders({ 'Content-Type': 'application/xml' })
.post('/orders')
.reply(200, '<xml></xml>');
nock('https://api.bigcommerce.com')
.defaultReplyHeaders({ 'Content-Type': 'application/json' })
.post('/customers')
.reply(200, '<html></html>');
});
it('should return the raw response', () => {
return request.run('post', '/orders', { })
.then(res => res.should.equal('<xml></xml>'));
});
it('should attach the response if the JSON cannot be parsed', () => {
return request.run('post', '/customers', { })
.catch(err => err.should.have.property('responseBody'));
});
});
context('timeout', () => {
beforeEach(() => {
nock('https://api.bigcommerce.com')
.post('/orders')
.replyWithError('ECONNRESET');
});
it('should return an error', () => {
return request.run('post', '/orders', { })
.then(() => should.fail('You shall not pass!'))
.catch(e => e.message.should.match(/ECONNRESET/));
});
});
it('should attach a keep-alive HTTPS agent', () => {
nock('https://api.bigcommerce.com')
.post('/orders')
.reply(200, { order: true });
const request = new Request('api.bigcommerce.com', {
headers: { 'Content-Type': 'application/json' },
agent: new HttpsAgent({
maxSockets: 30,
maxFreeSockets: 30,
timeout: 60000,
keepAliveTimeout: 30000
})
});
return request.run('post', '/orders')
.then(res => res.should.be.a('object'));
});
it('should return a JSON object on success', () => {
nock('https://api.bigcommerce.com')
.post('/orders')
.reply(200, { order: true });
return request.run('post', '/orders')
.then(res => {
res.should.be.a('object');
res.order.should.equal(true);
});
});
it('should accept and parse a GZIP JSON response', () => {
const data = JSON.stringify({ order: true });
const buffer = Buffer.from(data);
const zipped = zlib.gzipSync(buffer);
nock('https://api.bigcommerce.com')
.post('/orders')
.reply(200, zipped, {
'X-Transfer-Length': String(zipped.length),
'Content-Length': undefined,
'Content-Encoding': 'gzip',
'Content-Type': 'application/json'
});
const request = new Request('api.bigcommerce.com', {
headers: {
'Content-Type': 'application/json',
'Accept-Encoding': 'gzip, deflate'
}
});
return request.run('post', '/orders')
.then(res => {
should.exist(res);
res.should.have.property('order', true);
});
});
});