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

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'))
})