UNPKG

ravel

Version:

Ravel Rapid Application Development Framework

231 lines (208 loc) 8.34 kB
const httpCodes = require('../../lib/util/http_codes'); describe('util/rest', () => { let koaApp, ravelApp, rest; beforeEach(async () => { ravelApp = new (require('../../lib/ravel'))(); ravelApp.set('keygrip keys', ['abc']); ravelApp.set('log level', ravelApp.$log.NONE); await ravelApp.init(); // we'll test the rest middleware in isolation, using koa directly const Koa = require('koa'); koaApp = new Koa(); rest = new (require('../../lib/util/rest'))(ravelApp); }); describe('#respond()', () => { it('should produce a response with HTTP 204 NO CONTENT if no body is supplied', async () => { koaApp.use(rest.respond()); koaApp.use(async function (ctx) { ctx.response.body = ''; }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(204); }); it('should produce a response with HTTP 200 OK containing a string body if a json payload is supplied', async () => { const result = {}; koaApp.use(rest.respond()); koaApp.use(async function (ctx) { ctx.body = result; }); const res = await request(koaApp.callback()).get('/'); expect(res.headers['content-type']).toBe('application/json; charset=utf-8'); expect(res.status).toBe(200); }); it('should produce a response with HTTP 201 CREATED and an appropriate location header if a json body containing a property \'id\' is supplied along with an okCode of CREATED', async () => { const result = { id: 1 }; koaApp.use(rest.respond()); koaApp.use(async function (ctx) { ctx.body = result; }); const res = await request(koaApp.callback()).post('/entity').set('origin', 'http://localhost:8080/'); expect(res.headers['content-type']).toBe('application/json; charset=utf-8'); expect(res.headers.location).toBe('http://localhost:8080/entity/1'); expect(res.status).toBe(201); }); it('should allow the user to override the default success status code', async () => { koaApp.use(rest.respond()); koaApp.use(async function (ctx) { ctx.respondOptions = { okCode: 201 }; }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(201); }); it('should allow ctx.status to be used as an alias for respondOptions.okCode', async () => { const result = { id: 1 }; koaApp.use(rest.respond()); koaApp.use(async (ctx) => { ctx.body = result; ctx.status = 201; }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(201); expect(res.body).toEqual(result); }); it('should allow the second status should override first', async () => { const result = { id: 1 }; koaApp.use(rest.respond()); koaApp.use(async (ctx) => { ctx.body = result; ctx.status = 201; ctx.status = 200; }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(200); expect(res.body).toEqual(result); }); it('should use error codes in ctx.response.status if present (likely set by another library)', async () => { koaApp.use(rest.respond()); koaApp.use(async (ctx) => { ctx.response.status = 501; }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(501); expect(res.text).toBe('Not Implemented'); }); it('should produce a response with HTTP 206 PARTIAL CONTENT if it is supplied as an okCode along with options.start, options.end and options.count', async () => { const result = []; const options = { okCode: httpCodes.PARTIAL_CONTENT, start: 0, end: 5, count: 10 }; koaApp.use(rest.respond()); koaApp.use(async function (ctx) { ctx.body = result; ctx.respondOptions = options; }); const res = await request(koaApp.callback()).get('/').set('origin', 'http://localhost:8080/'); expect(res.headers['content-type']).toBe('application/json; charset=utf-8'); expect(res.headers['content-range']).toBe(`items ${options.start}-${options.end}/${options.count}`); expect(res.status).toBe(206); expect(res.body).toEqual(result); }); }); describe('#errorHandler()', () => { it('should respond with HTTP 404 NOT FOUND when $err.NotFound is passed as err', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw new ravelApp.$err.NotFound(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(404); expect(res.text).toBe(message); }); it('should respond with HTTP 403 Forbidden when $err.Access is passed as err', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw new ravelApp.$err.Access(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(403); expect(res.text).toBe(message); }); it('should respond with HTTP 405 METHOD NOT ALLOWED when $err.NotAllowed is passed as err', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw new ravelApp.$err.NotAllowed(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(405); expect(res.text).toBe(message); }); it('should respond with HTTP 501 NOT IMPLEMENTED when $err.NotImplemented is passed as err', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw new ravelApp.$err.NotImplemented(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(501); expect(res.text).toBe(message); }); it('should respond with HTTP 409 CONFLICT when $err.DuplicateEntry is passed as err', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw new ravelApp.$err.DuplicateEntry(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(409); expect(res.text).toBe(message); }); it('should respond with HTTP 416 REQUESTED_RANGE_NOT_SATISFIABLE when $err.RangeOutOfBounds is passed as err', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw new ravelApp.$err.RangeOutOfBounds(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(416); expect(res.text).toBe(message); }); it('should respond with HTTP 400 BAD REQUEST when $err.IllegalValue is passed as err', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw new ravelApp.$err.IllegalValue(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(400); expect(res.text).toBe(message); }); it('should respond with HTTP 500 INTERNAL SERVER ERROR when an unknown Error type is passed as err', async () => { const err = new Error('a message'); koaApp.use(rest.errorHandler()); koaApp.use(async () => { throw err; }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(500); expect(res.text).toBe(err.stack); }); }); describe('#errorHandler() and #respond()', () => { it('should override ctx.status with the error status code when an Error is thrown', async () => { const message = 'a message'; koaApp.use(rest.errorHandler()); koaApp.use(rest.respond()); koaApp.use(async (ctx) => { ctx.status = 200; throw new ravelApp.$err.IllegalValue(message); }); const res = await request(koaApp.callback()).get('/'); expect(res.status).toBe(400); expect(res.text).toBe(message); }); }); });