Use let or const to avoid scope issues and hoisting
var assert = require('node:assert')
1'use strict'23var assert = require('node:assert')4var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage5const { Buffer } = require('node:buffer');67var express = require('..')8var request = require('supertest')910describe('express.json()', function () {11 it('should parse JSON', function (done) {12 request(createApp())13 .post('/')14 .set('Content-Type', 'application/json')15 .send('{"user":"tobi"}')16 .expect(200, '{"user":"tobi"}', done)17 })1819 it('should handle Content-Length: 0', function (done) {20 request(createApp())21 .post('/')22 .set('Content-Type', 'application/json')23 .set('Content-Length', '0')24 .expect(200, '{}', done)25 })2627 it('should handle empty message-body', function (done) {28 request(createApp())29 .post('/')30 .set('Content-Type', 'application/json')31 .set('Transfer-Encoding', 'chunked')32 .expect(200, '{}', done)33 })3435 it('should handle no message-body', function (done) {36 request(createApp())37 .post('/')38 .set('Content-Type', 'application/json')39 .unset('Transfer-Encoding')40 .expect(200, '{}', done)41 })4243 // The old node error message modification in body parser is catching this44 it('should 400 when only whitespace', function (done) {45 request(createApp())46 .post('/')47 .set('Content-Type', 'application/json')48 .send(' \n')49 .expect(400, '[entity.parse.failed] ' + parseError(' \n'), done)50 })5152 it('should 400 when invalid content-length', function (done) {53 var app = express()5455 app.use(function (req, res, next) {56 req.headers['content-length'] = '20' // bad length57 next()58 })5960 app.use(express.json())6162 app.post('/', function (req, res) {63 res.json(req.body)64 })6566 request(app)67 .post('/')68 .set('Content-Type', 'application/json')69 .send('{"str":')70 .expect(400, /content length/, done)71 })7273 it('should handle duplicated middleware', function (done) {74 var app = express()7576 app.use(express.json())77 app.use(express.json())7879 app.post('/', function (req, res) {80 res.json(req.body)81 })8283 request(app)84 .post('/')85 .set('Content-Type', 'application/json')86 .send('{"user":"tobi"}')87 .expect(200, '{"user":"tobi"}', done)88 })8990 describe('when JSON is invalid', function () {91 before(function () {92 this.app = createApp()93 })9495 it('should 400 for bad token', function (done) {96 request(this.app)97 .post('/')98 .set('Content-Type', 'application/json')99 .send('{:')100 .expect(400, '[entity.parse.failed] ' + parseError('{:'), done)101 })102103 it('should 400 for incomplete', function (done) {104 request(this.app)105 .post('/')106 .set('Content-Type', 'application/json')107 .send('{"user"')108 .expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done)109 })110111 it('should include original body on error object', function (done) {112 request(this.app)113 .post('/')114 .set('Content-Type', 'application/json')115 .set('X-Error-Property', 'body')116 .send(' {"user"')117 .expect(400, ' {"user"', done)118 })119 })120121 describe('with limit option', function () {122 it('should 413 when over limit with Content-Length', function (done) {123 var buf = Buffer.alloc(1024, '.')124 request(createApp({ limit: '1kb' }))125 .post('/')126 .set('Content-Type', 'application/json')127 .set('Content-Length', '1034')128 .send(JSON.stringify({ str: buf.toString() }))129 .expect(413, '[entity.too.large] request entity too large', done)130 })131132 it('should 413 when over limit with chunked encoding', function (done) {133 var app = createApp({ limit: '1kb' })134 var buf = Buffer.alloc(1024, '.')135 var test = request(app).post('/')136 test.set('Content-Type', 'application/json')137 test.set('Transfer-Encoding', 'chunked')138 test.write('{"str":')139 test.write('"' + buf.toString() + '"}')140 test.expect(413, done)141 })142143 it('should 413 when inflated body over limit', function (done) {144 var app = createApp({ limit: '1kb' })145 var test = request(app).post('/')146 test.set('Content-Encoding', 'gzip')147 test.set('Content-Type', 'application/json')148 test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex'))149 test.expect(413, done)150 })151152 it('should accept number of bytes', function (done) {153 var buf = Buffer.alloc(1024, '.')154 request(createApp({ limit: 1024 }))155 .post('/')156 .set('Content-Type', 'application/json')157 .send(JSON.stringify({ str: buf.toString() }))158 .expect(413, done)159 })160161 it('should not change when options altered', function (done) {162 var buf = Buffer.alloc(1024, '.')163 var options = { limit: '1kb' }164 var app = createApp(options)165166 options.limit = '100kb'167168 request(app)169 .post('/')170 .set('Content-Type', 'application/json')171 .send(JSON.stringify({ str: buf.toString() }))172 .expect(413, done)173 })174175 it('should not hang response', function (done) {176 var buf = Buffer.alloc(10240, '.')177 var app = createApp({ limit: '8kb' })178 var test = request(app).post('/')179 test.set('Content-Type', 'application/json')180 test.write(buf)181 test.write(buf)182 test.write(buf)183 test.expect(413, done)184 })185186 it('should not error when inflating', function (done) {187 var app = createApp({ limit: '1kb' })188 var test = request(app).post('/')189 test.set('Content-Encoding', 'gzip')190 test.set('Content-Type', 'application/json')191 test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex'))192 test.expect(413, done)193 })194 })195196 describe('with inflate option', function () {197 describe('when false', function () {198 before(function () {199 this.app = createApp({ inflate: false })200 })201202 it('should not accept content-encoding', function (done) {203 var test = request(this.app).post('/')204 test.set('Content-Encoding', 'gzip')205 test.set('Content-Type', 'application/json')206 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))207 test.expect(415, '[encoding.unsupported] content encoding unsupported', done)208 })209 })210211 describe('when true', function () {212 before(function () {213 this.app = createApp({ inflate: true })214 })215216 it('should accept content-encoding', function (done) {217 var test = request(this.app).post('/')218 test.set('Content-Encoding', 'gzip')219 test.set('Content-Type', 'application/json')220 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))221 test.expect(200, '{"name":"论"}', done)222 })223 })224 })225226 describe('with strict option', function () {227 describe('when undefined', function () {228 before(function () {229 this.app = createApp()230 })231232 it('should 400 on primitives', function (done) {233 request(this.app)234 .post('/')235 .set('Content-Type', 'application/json')236 .send('true')237 .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)238 })239 })240241 describe('when false', function () {242 before(function () {243 this.app = createApp({ strict: false })244 })245246 it('should parse primitives', function (done) {247 request(this.app)248 .post('/')249 .set('Content-Type', 'application/json')250 .send('true')251 .expect(200, 'true', done)252 })253 })254255 describe('when true', function () {256 before(function () {257 this.app = createApp({ strict: true })258 })259260 it('should not parse primitives', function (done) {261 request(this.app)262 .post('/')263 .set('Content-Type', 'application/json')264 .send('true')265 .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace(/#/g, 't'), done)266 })267268 it('should not parse primitives with leading whitespaces', function (done) {269 request(this.app)270 .post('/')271 .set('Content-Type', 'application/json')272 .send(' true')273 .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace(/#/g, 't'), done)274 })275276 it('should allow leading whitespaces in JSON', function (done) {277 request(this.app)278 .post('/')279 .set('Content-Type', 'application/json')280 .send(' { "user": "tobi" }')281 .expect(200, '{"user":"tobi"}', done)282 })283284 it('should include correct message in stack trace', function (done) {285 request(this.app)286 .post('/')287 .set('Content-Type', 'application/json')288 .set('X-Error-Property', 'stack')289 .send('true')290 .expect(400)291 .expect(shouldContainInBody(parseError('#rue').replace(/#/g, 't')))292 .end(done)293 })294 })295 })296297 describe('with type option', function () {298 describe('when "application/vnd.api+json"', function () {299 before(function () {300 this.app = createApp({ type: 'application/vnd.api+json' })301 })302303 it('should parse JSON for custom type', function (done) {304 request(this.app)305 .post('/')306 .set('Content-Type', 'application/vnd.api+json')307 .send('{"user":"tobi"}')308 .expect(200, '{"user":"tobi"}', done)309 })310311 it('should ignore standard type', function (done) {312 request(this.app)313 .post('/')314 .set('Content-Type', 'application/json')315 .send('{"user":"tobi"}')316 .expect(200, '', done)317 })318 })319320 describe('when ["application/json", "application/vnd.api+json"]', function () {321 before(function () {322 this.app = createApp({323 type: ['application/json', 'application/vnd.api+json']324 })325 })326327 it('should parse JSON for "application/json"', function (done) {328 request(this.app)329 .post('/')330 .set('Content-Type', 'application/json')331 .send('{"user":"tobi"}')332 .expect(200, '{"user":"tobi"}', done)333 })334335 it('should parse JSON for "application/vnd.api+json"', function (done) {336 request(this.app)337 .post('/')338 .set('Content-Type', 'application/vnd.api+json')339 .send('{"user":"tobi"}')340 .expect(200, '{"user":"tobi"}', done)341 })342343 it('should ignore "application/x-json"', function (done) {344 request(this.app)345 .post('/')346 .set('Content-Type', 'application/x-json')347 .send('{"user":"tobi"}')348 .expect(200, '', done)349 })350 })351352 describe('when a function', function () {353 it('should parse when truthy value returned', function (done) {354 var app = createApp({ type: accept })355356 function accept (req) {357 return req.headers['content-type'] === 'application/vnd.api+json'358 }359360 request(app)361 .post('/')362 .set('Content-Type', 'application/vnd.api+json')363 .send('{"user":"tobi"}')364 .expect(200, '{"user":"tobi"}', done)365 })366367 it('should work without content-type', function (done) {368 var app = createApp({ type: accept })369370 function accept (req) {371 return true372 }373374 var test = request(app).post('/')375 test.write('{"user":"tobi"}')376 test.expect(200, '{"user":"tobi"}', done)377 })378379 it('should not invoke without a body', function (done) {380 var app = createApp({ type: accept })381382 function accept (req) {383 throw new Error('oops!')384 }385386 request(app)387 .get('/')388 .expect(404, done)389 })390 })391 })392393 describe('with verify option', function () {394 it('should assert value if function', function () {395 assert.throws(createApp.bind(null, { verify: 'lol' }),396 /TypeError: option verify must be function/)397 })398399 it('should error from verify', function (done) {400 var app = createApp({401 verify: function (req, res, buf) {402 if (buf[0] === 0x5b) throw new Error('no arrays')403 }404 })405406 request(app)407 .post('/')408 .set('Content-Type', 'application/json')409 .send('["tobi"]')410 .expect(403, '[entity.verify.failed] no arrays', done)411 })412413 it('should allow custom codes', function (done) {414 var app = createApp({415 verify: function (req, res, buf) {416 if (buf[0] !== 0x5b) return417 var err = new Error('no arrays')418 err.status = 400419 throw err420 }421 })422423 request(app)424 .post('/')425 .set('Content-Type', 'application/json')426 .send('["tobi"]')427 .expect(400, '[entity.verify.failed] no arrays', done)428 })429430 it('should allow custom type', function (done) {431 var app = createApp({432 verify: function (req, res, buf) {433 if (buf[0] !== 0x5b) return434 var err = new Error('no arrays')435 err.type = 'foo.bar'436 throw err437 }438 })439440 request(app)441 .post('/')442 .set('Content-Type', 'application/json')443 .send('["tobi"]')444 .expect(403, '[foo.bar] no arrays', done)445 })446447 it('should include original body on error object', function (done) {448 var app = createApp({449 verify: function (req, res, buf) {450 if (buf[0] === 0x5b) throw new Error('no arrays')451 }452 })453454 request(app)455 .post('/')456 .set('Content-Type', 'application/json')457 .set('X-Error-Property', 'body')458 .send('["tobi"]')459 .expect(403, '["tobi"]', done)460 })461462 it('should allow pass-through', function (done) {463 var app = createApp({464 verify: function (req, res, buf) {465 if (buf[0] === 0x5b) throw new Error('no arrays')466 }467 })468469 request(app)470 .post('/')471 .set('Content-Type', 'application/json')472 .send('{"user":"tobi"}')473 .expect(200, '{"user":"tobi"}', done)474 })475476 it('should work with different charsets', function (done) {477 var app = createApp({478 verify: function (req, res, buf) {479 if (buf[0] === 0x5b) throw new Error('no arrays')480 }481 })482483 var test = request(app).post('/')484 test.set('Content-Type', 'application/json; charset=utf-16')485 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))486 test.expect(200, '{"name":"论"}', done)487 })488489 it('should 415 on unknown charset prior to verify', function (done) {490 var app = createApp({491 verify: function (req, res, buf) {492 throw new Error('unexpected verify call')493 }494 })495496 var test = request(app).post('/')497 test.set('Content-Type', 'application/json; charset=x-bogus')498 test.write(Buffer.from('00000000', 'hex'))499 test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done)500 })501 })502503 describe('async local storage', function () {504 before(function () {505 var app = express()506 var store = { foo: 'bar' }507508 app.use(function (req, res, next) {509 req.asyncLocalStorage = new AsyncLocalStorage()510 req.asyncLocalStorage.run(store, next)511 })512513 app.use(express.json())514515 app.use(function (req, res, next) {516 var local = req.asyncLocalStorage.getStore()517518 if (local) {519 res.setHeader('x-store-foo', String(local.foo))520 }521522 next()523 })524525 app.use(function (err, req, res, next) {526 var local = req.asyncLocalStorage.getStore()527528 if (local) {529 res.setHeader('x-store-foo', String(local.foo))530 }531532 res.status(err.status || 500)533 res.send('[' + err.type + '] ' + err.message)534 })535536 app.post('/', function (req, res) {537 res.json(req.body)538 })539540 this.app = app541 })542543 it('should persist store', function (done) {544 request(this.app)545 .post('/')546 .set('Content-Type', 'application/json')547 .send('{"user":"tobi"}')548 .expect(200)549 .expect('x-store-foo', 'bar')550 .expect('{"user":"tobi"}')551 .end(done)552 })553554 it('should persist store when unmatched content-type', function (done) {555 request(this.app)556 .post('/')557 .set('Content-Type', 'application/fizzbuzz')558 .send('buzz')559 .expect(200)560 .expect('x-store-foo', 'bar')561 .expect('')562 .end(done)563 })564565 it('should persist store when inflated', function (done) {566 var test = request(this.app).post('/')567 test.set('Content-Encoding', 'gzip')568 test.set('Content-Type', 'application/json')569 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))570 test.expect(200)571 test.expect('x-store-foo', 'bar')572 test.expect('{"name":"论"}')573 test.end(done)574 })575576 it('should persist store when inflate error', function (done) {577 var test = request(this.app).post('/')578 test.set('Content-Encoding', 'gzip')579 test.set('Content-Type', 'application/json')580 test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))581 test.expect(400)582 test.expect('x-store-foo', 'bar')583 test.end(done)584 })585586 it('should persist store when parse error', function (done) {587 request(this.app)588 .post('/')589 .set('Content-Type', 'application/json')590 .send('{"user":')591 .expect(400)592 .expect('x-store-foo', 'bar')593 .end(done)594 })595596 it('should persist store when limit exceeded', function (done) {597 request(this.app)598 .post('/')599 .set('Content-Type', 'application/json')600 .send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}')601 .expect(413)602 .expect('x-store-foo', 'bar')603 .end(done)604 })605 })606607 describe('charset', function () {608 before(function () {609 this.app = createApp()610 })611612 it('should parse utf-8', function (done) {613 var test = request(this.app).post('/')614 test.set('Content-Type', 'application/json; charset=utf-8')615 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))616 test.expect(200, '{"name":"论"}', done)617 })618619 it('should parse utf-16', function (done) {620 var test = request(this.app).post('/')621 test.set('Content-Type', 'application/json; charset=utf-16')622 test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex'))623 test.expect(200, '{"name":"论"}', done)624 })625626 it('should parse when content-length != char length', function (done) {627 var test = request(this.app).post('/')628 test.set('Content-Type', 'application/json; charset=utf-8')629 test.set('Content-Length', '13')630 test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex'))631 test.expect(200, '{"test":"å"}', done)632 })633634 it('should default to utf-8', function (done) {635 var test = request(this.app).post('/')636 test.set('Content-Type', 'application/json')637 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))638 test.expect(200, '{"name":"论"}', done)639 })640641 it('should fail on unknown charset', function (done) {642 var test = request(this.app).post('/')643 test.set('Content-Type', 'application/json; charset=koi8-r')644 test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex'))645 test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done)646 })647 })648649 describe('encoding', function () {650 before(function () {651 this.app = createApp({ limit: '1kb' })652 })653654 it('should parse without encoding', function (done) {655 var test = request(this.app).post('/')656 test.set('Content-Type', 'application/json')657 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))658 test.expect(200, '{"name":"论"}', done)659 })660661 it('should support identity encoding', function (done) {662 var test = request(this.app).post('/')663 test.set('Content-Encoding', 'identity')664 test.set('Content-Type', 'application/json')665 test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex'))666 test.expect(200, '{"name":"论"}', done)667 })668669 it('should support gzip encoding', function (done) {670 var test = request(this.app).post('/')671 test.set('Content-Encoding', 'gzip')672 test.set('Content-Type', 'application/json')673 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))674 test.expect(200, '{"name":"论"}', done)675 })676677 it('should support deflate encoding', function (done) {678 var test = request(this.app).post('/')679 test.set('Content-Encoding', 'deflate')680 test.set('Content-Type', 'application/json')681 test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex'))682 test.expect(200, '{"name":"论"}', done)683 })684685 it('should be case-insensitive', function (done) {686 var test = request(this.app).post('/')687 test.set('Content-Encoding', 'GZIP')688 test.set('Content-Type', 'application/json')689 test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))690 test.expect(200, '{"name":"论"}', done)691 })692693 it('should 415 on unknown encoding', function (done) {694 var test = request(this.app).post('/')695 test.set('Content-Encoding', 'nulls')696 test.set('Content-Type', 'application/json')697 test.write(Buffer.from('000000000000', 'hex'))698 test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done)699 })700701 it('should 400 on malformed encoding', function (done) {702 var test = request(this.app).post('/')703 test.set('Content-Encoding', 'gzip')704 test.set('Content-Type', 'application/json')705 test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex'))706 test.expect(400, done)707 })708709 it('should 413 when inflated value exceeds limit', function (done) {710 // gzip'd data exceeds 1kb, but deflated below 1kb711 var test = request(this.app).post('/')712 test.set('Content-Encoding', 'gzip')713 test.set('Content-Type', 'application/json')714 test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex'))715 test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'))716 test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex'))717 test.expect(413, done)718 })719 })720})721722function createApp (options) {723 var app = express()724725 app.use(express.json(options))726727 app.use(function (err, req, res, next) {728 // console.log(err)729 res.status(err.status || 500)730 res.send(String(req.headers['x-error-property']731 ? err[req.headers['x-error-property']]732 : ('[' + err.type + '] ' + err.message)))733 })734735 app.post('/', function (req, res) {736 res.json(req.body)737 })738739 return app740}741742function parseError (str) {743 try {744 JSON.parse(str); throw new SyntaxError('strict violation')745 } catch (e) {746 return e.message747 }748}749750function shouldContainInBody (str) {751 return function (res) {752 assert.ok(res.text.indexOf(str) !== -1,753 'expected \'' + res.text + '\' to contain \'' + str + '\'')754 }755}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.