| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- import {expect} from 'chai';
- import HttpErrors from 'http-errors';
- import {HttpMethod} from '../route.js';
- import {format} from '@e22m4u/js-format';
- import {RouterOptions} from '../router-options.js';
- import {createRequestMock} from '../utils/index.js';
- import {
- BodyParser,
- METHODS_WITH_BODY,
- UNPARSABLE_MEDIA_TYPES,
- } from './body-parser.js';
- describe('BodyParser', function () {
- describe('defineParser', function () {
- it('requires the parameter "mediaType" to be a non-empty String', function () {
- const parser = new BodyParser();
- const throwable = v => () => parser.defineParser(v, () => undefined);
- const error = v =>
- format(
- 'The parameter "mediaType" of BodyParser.defineParser ' +
- 'should be a non-empty String, but %s was given.',
- v,
- );
- expect(throwable('')).to.throw(error('""'));
- expect(throwable(10)).to.throw(error('10'));
- expect(throwable(0)).to.throw(error('0'));
- expect(throwable(true)).to.throw(error('true'));
- expect(throwable(false)).to.throw(error('false'));
- expect(throwable(null)).to.throw(error('null'));
- expect(throwable({})).to.throw(error('Object'));
- expect(throwable([])).to.throw(error('Array'));
- expect(throwable(undefined)).to.throw(error('undefined'));
- expect(throwable(() => undefined)).to.throw(error('Function'));
- throwable('text/plain')();
- });
- it('requires the parameter "parser" to be a Function', function () {
- const parser = new BodyParser();
- const throwable = v => () => parser.defineParser('str', v);
- const error = v =>
- format(
- 'The parameter "parser" of BodyParser.defineParser ' +
- 'should be a Function, but %s was given.',
- v,
- );
- expect(throwable('str')).to.throw(error('"str"'));
- expect(throwable('')).to.throw(error('""'));
- expect(throwable(10)).to.throw(error('10'));
- expect(throwable(0)).to.throw(error('0'));
- expect(throwable(true)).to.throw(error('true'));
- expect(throwable(false)).to.throw(error('false'));
- expect(throwable(null)).to.throw(error('null'));
- expect(throwable({})).to.throw(error('Object'));
- expect(throwable([])).to.throw(error('Array'));
- expect(throwable(undefined)).to.throw(error('undefined'));
- throwable(() => undefined)();
- });
- it('overrides existing parser', function () {
- const parser = new BodyParser();
- const fn = v => v;
- parser.defineParser('text/plain', fn);
- expect(parser['_parsers']['text/plain']).to.be.eq(fn);
- });
- it('sets a new parser', function () {
- const parser = new BodyParser();
- const fn = v => v;
- parser.defineParser('my/type', fn);
- expect(parser['_parsers']['my/type']).to.be.eq(fn);
- });
- });
- describe('hasParser', function () {
- it('requires the parameter "mediaType" to be a non-empty String', function () {
- const parser = new BodyParser();
- const throwable = v => () => parser.hasParser(v);
- const error = v =>
- format(
- 'The parameter "mediaType" of BodyParser.hasParser ' +
- 'should be a non-empty String, but %s was given.',
- v,
- );
- expect(throwable('')).to.throw(error('""'));
- expect(throwable(10)).to.throw(error('10'));
- expect(throwable(0)).to.throw(error('0'));
- expect(throwable(true)).to.throw(error('true'));
- expect(throwable(false)).to.throw(error('false'));
- expect(throwable(null)).to.throw(error('null'));
- expect(throwable({})).to.throw(error('Object'));
- expect(throwable([])).to.throw(error('Array'));
- expect(throwable(undefined)).to.throw(error('undefined'));
- expect(throwable(() => undefined)).to.throw(error('Function'));
- throwable('text/plain')();
- });
- it('returns true if the parser is exist', function () {
- const parser = new BodyParser();
- parser.defineParser('type/media', v => v);
- expect(parser.hasParser('type/media')).to.be.true;
- });
- it('returns false if the parser is not exist', function () {
- const parser = new BodyParser();
- expect(parser.hasParser('text/unknown')).to.be.false;
- });
- });
- describe('deleteParser', function () {
- it('requires the parameter "mediaType" to be a non-empty String', function () {
- const parser = new BodyParser();
- const throwable = v => () => parser.deleteParser(v);
- const error = v =>
- format(
- 'The parameter "mediaType" of BodyParser.deleteParser ' +
- 'should be a non-empty String, but %s was given.',
- v,
- );
- expect(throwable('')).to.throw(error('""'));
- expect(throwable(10)).to.throw(error('10'));
- expect(throwable(0)).to.throw(error('0'));
- expect(throwable(true)).to.throw(error('true'));
- expect(throwable(false)).to.throw(error('false'));
- expect(throwable(null)).to.throw(error('null'));
- expect(throwable({})).to.throw(error('Object'));
- expect(throwable([])).to.throw(error('Array'));
- expect(throwable(undefined)).to.throw(error('undefined'));
- expect(throwable(() => undefined)).to.throw(error('Function'));
- throwable('text/plain')();
- });
- it('remove existing parser', function () {
- const parser = new BodyParser();
- const fn = v => v;
- parser.defineParser('my/type', fn);
- expect(parser['_parsers']['my/type']).to.be.eq(fn);
- parser.deleteParser('my/type');
- expect(parser['_parsers']['my/type']).to.be.undefined;
- });
- it('throws an error if the media type does not exist', function () {
- const parser = new BodyParser();
- const throwable = () => parser.deleteParser('unknown');
- expect(throwable).to.throw('The parser of "unknown" is not found.');
- });
- });
- describe('parse', function () {
- it('returns undefined if the request method is not supported', async function () {
- const parser = new BodyParser();
- const req = createRequestMock({
- method: 'unsupported',
- body: 'Lorem Ipsum is simply dummy text.',
- });
- const result = await parser.parse(req);
- expect(result).to.be.undefined;
- });
- it('returns undefined if the request method is not supported even the header "content-type" is specified', async function () {
- const parser = new BodyParser();
- const req = createRequestMock({
- method: 'unsupported',
- headers: {'content-type': 'text/plain'},
- body: 'Lorem Ipsum is simply dummy text.',
- });
- const result = await parser.parse(req);
- expect(result).to.be.undefined;
- });
- it('returns undefined if no "content-type" header', async function () {
- const parser = new BodyParser();
- const req = createRequestMock({method: HttpMethod.POST});
- const result = await parser.parse(req);
- expect(result).to.be.undefined;
- });
- it('returns undefined if the media type is excluded', async function () {
- const parser = new BodyParser();
- for await (const mediaType of UNPARSABLE_MEDIA_TYPES) {
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': mediaType},
- body: 'Lorem Ipsum is simply dummy text.',
- });
- const result = await parser.parse(req);
- expect(result).to.be.undefined;
- }
- });
- it('parses the request body for available methods', async function () {
- const parser = new BodyParser();
- const body = 'Lorem Ipsum is simply dummy text.';
- const headers = {'content-type': 'text/plain'};
- for await (const method of Object.values(METHODS_WITH_BODY)) {
- const req = createRequestMock({method, body, headers});
- const result = await parser.parse(req);
- expect(result).to.be.eq(body);
- }
- });
- it('throws an error for unsupported media type', function () {
- const parser = new BodyParser();
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': 'media/unknown'},
- });
- const throwable = () => parser.parse(req);
- expect(throwable).to.throw(
- 'Media type "media/unknown" is not supported.',
- );
- });
- it('uses the option "bodyBytesLimit" from the RouterOptions', async function () {
- const parser = new BodyParser();
- parser.getService(RouterOptions).setRequestBodyBytesLimit(1);
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {
- 'content-type': 'text/plain',
- 'content-length': '2',
- },
- });
- const promise = parser.parse(req);
- await expect(promise).to.be.rejectedWith(HttpErrors.PayloadTooLarge);
- });
- describe('text/plain', function () {
- it('returns undefined if no request body', async function () {
- const parser = new BodyParser();
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': 'text/plain'},
- });
- const result = await parser.parse(req);
- expect(result).to.be.undefined;
- });
- it('returns a string from the string body', async function () {
- const body = 'Lorem Ipsum is simply dummy text.';
- const parser = new BodyParser();
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': 'text/plain'},
- body,
- });
- const result = await parser.parse(req);
- expect(result).to.be.eq(body);
- });
- it('returns a string from the Buffer body', async function () {
- const body = 'Lorem Ipsum is simply dummy text.';
- const parser = new BodyParser();
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': 'text/plain'},
- body: Buffer.from(body, 'utf-8'),
- });
- const result = await parser.parse(req);
- expect(result).to.be.eq(body);
- });
- });
- describe('application/json', function () {
- it('returns undefined if no request body', async function () {
- const parser = new BodyParser();
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': 'application/json'},
- });
- const result = await parser.parse(req);
- expect(result).to.be.undefined;
- });
- it('returns parsed JSON from the string body', async function () {
- const body = {foo: 'bar'};
- const parser = new BodyParser();
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': 'application/json'},
- body: JSON.stringify(body),
- });
- const result = await parser.parse(req);
- expect(result).to.be.eql(body);
- });
- it('returns parsed JSON from the Buffer body', async function () {
- const body = {foo: 'bar'};
- const parser = new BodyParser();
- const req = createRequestMock({
- method: HttpMethod.POST,
- headers: {'content-type': 'application/json'},
- body: Buffer.from(JSON.stringify(body)),
- });
- const result = await parser.parse(req);
- expect(result).to.be.eql(body);
- });
- });
- });
- });
|