You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
12 KiB
358 lines
12 KiB
const test = require('tape') |
|
const http = require('http') |
|
const fs = require('fs') |
|
const bl = require('bl') |
|
const xtend = require('xtend') |
|
const EE = require('events').EventEmitter |
|
const jsonist = require('./') |
|
const stringify = require('json-stringify-safe') |
|
const after = require('after') |
|
|
|
function testServer (serverResponse, statusCode) { |
|
const ee = new EE() |
|
const server = http.createServer(handler) |
|
|
|
function handler (req, res) { |
|
req.pipe(bl(function (err, data) { |
|
if (err) { return ee.emit('error', err) } |
|
|
|
ee.emit('request', req, data.toString()) |
|
|
|
setTimeout(server.close.bind(server), 20) |
|
})) |
|
|
|
res.writeHead( |
|
typeof statusCode === 'number' ? statusCode : 200 |
|
, { 'content-type': 'application/json' } |
|
) |
|
res.end(serverResponse || '') |
|
} |
|
|
|
server.listen(function () { |
|
ee.emit('ready', 'http://localhost:' + server.address().port) |
|
}) |
|
|
|
server.on('close', ee.emit.bind(ee, 'close')) |
|
|
|
return ee |
|
} |
|
|
|
'get delete'.split(' ').forEach((type) => { |
|
test(type + ' fetch json doc', (t) => { |
|
t.plan(7) |
|
|
|
let testDoc = { a: 'test', doc: true, arr: [ { of: 'things' } ] } |
|
|
|
testServer(stringify(testDoc)) |
|
.on('ready', function (url) { |
|
jsonist[type](url, function (err, data, response) { |
|
t.notOk(err, 'no error') |
|
t.deepEqual(data, testDoc, 'got correct doc') |
|
t.ok(response, 'got response object') |
|
t.equal( |
|
response && response.headers && response.headers['content-type'] |
|
, 'application/json', 'verified response object by content-type header' |
|
) |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, type.toUpperCase(), 'got ' + type + ' request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
|
|
test(type + ' fetch non-json doc', (t) => { |
|
t.plan(7) |
|
|
|
testServer('this is not json') |
|
.on('ready', function (url) { |
|
jsonist[type](url, function (err, data, response) { |
|
t.ok(err, 'got error') |
|
t.notOk(data, 'no data') |
|
t.notOk(response, 'no response obj') |
|
t.ok(/JSON/.test(err.message), 'error says something about "JSON"') |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, type.toUpperCase(), 'got ' + type + ' request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
}) |
|
|
|
'post put'.split(' ').forEach(function (type) { |
|
test(type + ' json, no response', (t) => { |
|
t.plan(9) |
|
|
|
var testDoc = { a: 'test2', doc: true, arr: [ { of: 'things' } ] } |
|
|
|
testServer('') |
|
.on('ready', function (url) { |
|
jsonist[type](url, xtend(testDoc), function (err, data, response) { |
|
t.notOk(err, 'no error') |
|
t.notOk(data, 'no data') |
|
t.ok(response, 'got response object') |
|
t.equal( |
|
response && response.headers && response.headers['content-type'] |
|
, 'application/json', 'verified response object by content-type header' |
|
) |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, type.toUpperCase(), 'got ' + type + ' request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
t.equal(req.headers['content-type'], 'application/json', 'got correct encoding') |
|
t.deepEqual(JSON.parse(data), testDoc, 'got correct ' + type) |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
|
|
test(type + ' json, with response', (t) => { |
|
t.plan(9) |
|
|
|
let sendDoc = { a: 'test2', doc: true, arr: [ { of: 'things' } ] } |
|
let recvDoc = { recv: 'this', obj: true } |
|
|
|
testServer(stringify(recvDoc)) |
|
.on('ready', function (url) { |
|
jsonist[type](url, xtend(sendDoc), function (err, data, response) { |
|
t.notOk(err, 'no error') |
|
t.deepEqual(data, recvDoc) |
|
t.ok(response, 'got response object') |
|
t.equal( |
|
response && response.headers && response.headers['content-type'] |
|
, 'application/json', 'verified response object by content-type header' |
|
) |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, type.toUpperCase(), 'got ' + type + ' request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
t.equal(req.headers['content-type'], 'application/json', 'got correct encoding') |
|
t.deepEqual(JSON.parse(data), sendDoc, 'got correct ' + type) |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
|
|
test(type + ' data with no pipe function treated as data', (t) => { |
|
t.plan(9) |
|
|
|
let sendDoc = { |
|
a: 'test2', |
|
doc: true, |
|
arr: [ { of: 'things' } ], |
|
pipe: 'this is a string and not a function' |
|
} |
|
let recvDoc = { recv: 'this', obj: true } |
|
|
|
testServer(stringify(recvDoc)) |
|
.on('ready', function (url) { |
|
jsonist[type](url, xtend(sendDoc), function (err, data, response) { |
|
t.notOk(err, 'no error') |
|
t.deepEqual(data, recvDoc) |
|
t.ok(response, 'got response object') |
|
t.equal( |
|
response && response.headers && response.headers['content-type'] |
|
, 'application/json', 'verified response object by content-type header' |
|
) |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, type.toUpperCase(), 'got ' + type + ' request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
t.equal(req.headers['content-type'], 'application/json', 'got correct encoding') |
|
t.deepEqual(JSON.parse(data), sendDoc, 'got correct ' + type) |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
|
|
test(type + ' data with pipe function will data.pipe(req)', (t) => { |
|
t.plan(10) |
|
|
|
let sendDoc = { |
|
a: 'test2', |
|
doc: true, |
|
arr: [ { of: 'things' } ] |
|
} |
|
let stream = { |
|
pipe: function (req) { |
|
t.ok(req, 'request should be set') |
|
req.end(stringify(sendDoc)) |
|
} |
|
} |
|
let recvDoc = { recv: 'this', obj: true } |
|
|
|
testServer(stringify(recvDoc)) |
|
.on('ready', function (url) { |
|
jsonist[type](url, stream, function (err, data, response) { |
|
t.notOk(err, 'no error') |
|
t.deepEqual(data, recvDoc) |
|
t.ok(response, 'got response object') |
|
t.equal( |
|
response && response.headers && response.headers['content-type'] |
|
, 'application/json', 'verified response object by content-type header' |
|
) |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, type.toUpperCase(), 'got ' + type + ' request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
t.equal(req.headers['content-type'], 'application/json', 'got correct encoding') |
|
t.deepEqual(JSON.parse(data), sendDoc, 'got correct ' + type) |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
|
|
test(type + ' data with pipe function and real stream works', (t) => { |
|
t.plan(9) |
|
|
|
let file = `${__dirname}/package.json` |
|
let content = JSON.parse(fs.readFileSync(file)) |
|
let stream = fs.createReadStream(file) |
|
let recvDoc = { recv: 'this', obj: true } |
|
|
|
testServer(stringify(recvDoc)) |
|
.on('ready', function (url) { |
|
jsonist[type](url, stream, function (err, data, response) { |
|
t.notOk(err, 'no error') |
|
t.deepEqual(data, recvDoc) |
|
t.ok(response, 'got response object') |
|
t.equal( |
|
response && response.headers && response.headers['content-type'] |
|
, 'application/json', 'verified response object by content-type header' |
|
) |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, type.toUpperCase(), 'got ' + type + ' request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
t.equal(req.headers['content-type'], 'application/json', 'got correct encoding') |
|
t.deepEqual(JSON.parse(data), content, 'got correct ' + type) |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
}) |
|
|
|
test('follow redirect', (t) => { |
|
t.plan(7) |
|
|
|
let expectedResponse = { ok: 'foobar!' } |
|
let server = http.createServer(function (req, res) { |
|
if (req.url === '/') { // 2 requests come in here |
|
t.ok('got /') |
|
res.writeHead(302, { 'location': '/foobar' }) |
|
return res.end() |
|
} |
|
// one comes in here |
|
t.equal(req.url, '/foobar', 'got /foobar') |
|
res.writeHead(200, { 'content-type': 'application/json' }) |
|
res.end(stringify(expectedResponse)) |
|
}) |
|
|
|
server.listen(function () { |
|
let port = server.address().port |
|
let done = after(2, function () { server.close() }) |
|
|
|
jsonist.get('http://localhost:' + port, function (err, data) { |
|
// don't follow redirect, don't get data |
|
t.error(err, 'no error') |
|
t.equal(data, null, 'no redirect, no data') |
|
done() |
|
}) |
|
|
|
jsonist.get('http://localhost:' + port, { followRedirects: true }, function (err, data) { |
|
t.error(err, 'no error') |
|
t.deepEqual(data, expectedResponse, 'redirect, got data') |
|
done() |
|
}) |
|
}) |
|
}) |
|
|
|
test('follow redirect limit', (t) => { |
|
t.plan(6 + 10 + 5 + 10) |
|
|
|
let expectedResponse = { ok: 'foobar!' } |
|
let server = http.createServer(function (req, res) { |
|
let m = +req.url.match(/^\/(\d+)/)[1] |
|
if (m < 20) { // 2 requests come in here |
|
t.ok('got /') |
|
res.writeHead(302, { 'location': '/' + (m + 1) }) |
|
return res.end() |
|
} |
|
// one comes in here |
|
t.equal(req.url, '/20', 'got /20') |
|
res.writeHead(200, { 'content-type': 'application/json' }) |
|
res.end(stringify(expectedResponse)) |
|
}) |
|
|
|
server.listen(function () { |
|
let port = server.address().port |
|
let done = after(3, function () { server.close() }) |
|
|
|
jsonist.get('http://localhost:' + port + '/1', { followRedirects: true }, function (err, data) { |
|
t.ok(err, 'got error') |
|
t.equal(err.message, 'Response was redirected too many times (10)') |
|
done() |
|
}) |
|
|
|
jsonist.get('http://localhost:' + port + '/1', { followRedirects: 5 }, function (err, data) { |
|
t.ok(err, 'got error') |
|
t.equal(err.message, 'Response was redirected too many times (5)') |
|
done() |
|
}) |
|
|
|
jsonist.get('http://localhost:' + port + '/11', { followRedirects: true }, function (err, data) { |
|
t.error(err, 'no error') |
|
t.deepEqual(data, expectedResponse, 'redirect, got data') |
|
done() |
|
}) |
|
}) |
|
}) |
|
|
|
test('server error, non-JSON', (t) => { |
|
t.plan(7) |
|
|
|
var responseText = 'there was an error' |
|
|
|
testServer(responseText, 501) |
|
.on('ready', function (url) { |
|
jsonist.get(url, function (err, data, response) { |
|
t.ok(err) |
|
t.ok(err instanceof jsonist.HttpError) |
|
t.equal(err.data.toString(), responseText, 'got correct response') |
|
t.equal(err.statusCode, 501, 'got correct statusCode') |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, 'GET', 'got GET request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
}) |
|
|
|
test('server error, with-JSON', (t) => { |
|
t.plan(8) |
|
|
|
var responseDoc = 'there was an error' |
|
|
|
testServer(stringify(responseDoc), 501) |
|
.on('ready', function (url) { |
|
jsonist.get(url, function (err, data, response) { |
|
t.notOk(err, 'no error') |
|
t.deepEqual(data, responseDoc, 'got correct doc') |
|
t.ok(response, 'got response object') |
|
t.equal(response.statusCode, 501, 'got correct status code') |
|
t.equal( |
|
response && response.headers && response.headers['content-type'] |
|
, 'application/json', 'verified response object by content-type header' |
|
) |
|
}) |
|
}) |
|
.on('request', function (req, data) { |
|
t.equal(req.method, 'GET', 'got GET request') |
|
t.equal(req.headers['accept'], 'application/json', 'got correct accept header') |
|
}) |
|
.on('close', t.ok.bind(t, true, 'ended')) |
|
})
|
|
|