strong-tie/inbound-calls
0
1'use strict'2 3const stream = require('node:stream')4const split = require('split2')5const { test } = require('node:test')6const Fastify = require('..')7const createError = require('@fastify/error')8 9test("HEAD route should handle stream.on('error')", (t, done) => {10 t.plan(6)11 12 const resStream = stream.Readable.from('Hello with error!')13 const logStream = split(JSON.parse)14 const expectedError = new Error('Hello!')15 const fastify = Fastify({16 logger: {17 stream: logStream,18 level: 'error'19 }20 })21 22 fastify.route({23 method: 'GET',24 path: '/more-coffee',25 exposeHeadRoute: true,26 handler: (req, reply) => {27 process.nextTick(() => resStream.emit('error', expectedError))28 return resStream29 }30 })31 32 logStream.once('data', line => {33 const { message, stack } = expectedError34 t.assert.deepStrictEqual(line.err, { type: 'Error', message, stack })35 t.assert.strictEqual(line.msg, 'Error on Stream found for HEAD route')36 t.assert.strictEqual(line.level, 50)37 })38 39 fastify.inject({40 method: 'HEAD',41 url: '/more-coffee'42 }, (error, res) => {43 t.assert.ifError(error)44 t.assert.strictEqual(res.statusCode, 200)45 t.assert.strictEqual(res.headers['content-type'], undefined)46 done()47 })48})49 50test('HEAD route should be exposed by default', async t => {51 t.plan(5)52 53 const resStream = stream.Readable.from('Hello with error!')54 const resJson = { hello: 'world' }55 const fastify = Fastify()56 57 fastify.route({58 method: 'GET',59 path: '/without-flag',60 handler: (req, reply) => {61 return resStream62 }63 })64 65 fastify.route({66 exposeHeadRoute: true,67 method: 'GET',68 path: '/with-flag',69 handler: (req, reply) => {70 return resJson71 }72 })73 74 let res = await fastify.inject({75 method: 'HEAD',76 url: '/without-flag'77 })78 t.assert.strictEqual(res.statusCode, 200)79 80 res = await fastify.inject({81 method: 'HEAD',82 url: '/with-flag'83 })84 t.assert.strictEqual(res.statusCode, 200)85 t.assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')86 t.assert.strictEqual(res.headers['content-length'], `${Buffer.byteLength(JSON.stringify(resJson))}`)87 t.assert.strictEqual(res.body, '')88})89 90test('HEAD route should be exposed if route exposeHeadRoute is set', async t => {91 t.plan(5)92 93 const resBuffer = Buffer.from('I am a coffee!')94 const resJson = { hello: 'world' }95 const fastify = Fastify({ exposeHeadRoutes: false })96 97 fastify.route({98 exposeHeadRoute: true,99 method: 'GET',100 path: '/one',101 handler: (req, reply) => {102 return resBuffer103 }104 })105 106 fastify.route({107 method: 'GET',108 path: '/two',109 handler: (req, reply) => {110 return resJson111 }112 })113 114 let res = await fastify.inject({115 method: 'HEAD',116 url: '/one'117 })118 t.assert.strictEqual(res.statusCode, 200)119 t.assert.strictEqual(res.headers['content-type'], 'application/octet-stream')120 t.assert.strictEqual(res.headers['content-length'], `${resBuffer.byteLength}`)121 t.assert.strictEqual(res.body, '')122 123 res = await fastify.inject({124 method: 'HEAD',125 url: '/two'126 })127 t.assert.strictEqual(res.statusCode, 404)128})129 130test('Set a custom HEAD route before GET one without disabling exposeHeadRoutes (global)', (t, done) => {131 t.plan(6)132 133 const resBuffer = Buffer.from('I am a coffee!')134 const fastify = Fastify({135 exposeHeadRoutes: true136 })137 138 fastify.route({139 method: 'HEAD',140 path: '/one',141 handler: (req, reply) => {142 reply.header('content-type', 'application/pdf')143 reply.header('content-length', `${resBuffer.byteLength}`)144 reply.header('x-custom-header', 'some-custom-header')145 reply.send()146 }147 })148 149 fastify.route({150 method: 'GET',151 path: '/one',152 handler: (req, reply) => {153 return resBuffer154 }155 })156 157 fastify.inject({158 method: 'HEAD',159 url: '/one'160 }, (error, res) => {161 t.assert.ifError(error)162 t.assert.strictEqual(res.statusCode, 200)163 t.assert.strictEqual(res.headers['content-type'], 'application/pdf')164 t.assert.strictEqual(res.headers['content-length'], `${resBuffer.byteLength}`)165 t.assert.strictEqual(res.headers['x-custom-header'], 'some-custom-header')166 t.assert.strictEqual(res.body, '')167 done()168 })169})170 171test('Set a custom HEAD route before GET one without disabling exposeHeadRoutes (route)', (t, done) => {172 t.plan(6)173 174 const fastify = Fastify()175 176 const resBuffer = Buffer.from('I am a coffee!')177 178 fastify.route({179 method: 'HEAD',180 path: '/one',181 handler: (req, reply) => {182 reply.header('content-type', 'application/pdf')183 reply.header('content-length', `${resBuffer.byteLength}`)184 reply.header('x-custom-header', 'some-custom-header')185 reply.send()186 }187 })188 189 fastify.route({190 method: 'GET',191 exposeHeadRoute: true,192 path: '/one',193 handler: (req, reply) => {194 return resBuffer195 }196 })197 198 fastify.inject({199 method: 'HEAD',200 url: '/one'201 }, (error, res) => {202 t.assert.ifError(error)203 t.assert.strictEqual(res.statusCode, 200)204 t.assert.strictEqual(res.headers['content-type'], 'application/pdf')205 t.assert.strictEqual(res.headers['content-length'], `${resBuffer.byteLength}`)206 t.assert.strictEqual(res.headers['x-custom-header'], 'some-custom-header')207 t.assert.strictEqual(res.body, '')208 done()209 })210})211 212test('HEAD routes properly auto created for GET routes when prefixTrailingSlash: \'no-slash\'', (t, done) => {213 t.plan(2)214 215 const fastify = Fastify()216 217 fastify.register(function routes (f, opts, next) {218 f.route({219 method: 'GET',220 url: '/',221 exposeHeadRoute: true,222 prefixTrailingSlash: 'no-slash',223 handler: (req, reply) => {224 reply.send({ hello: 'world' })225 }226 })227 228 next()229 }, { prefix: '/prefix' })230 231 fastify.inject({ url: '/prefix/prefix', method: 'HEAD' }, (err, res) => {232 t.assert.ifError(err)233 t.assert.strictEqual(res.statusCode, 404)234 done()235 })236})237 238test('HEAD routes properly auto created for GET routes when prefixTrailingSlash: \'both\'', async t => {239 t.plan(3)240 241 const fastify = Fastify()242 243 fastify.register(function routes (f, opts, next) {244 f.route({245 method: 'GET',246 url: '/',247 exposeHeadRoute: true,248 prefixTrailingSlash: 'both',249 handler: (req, reply) => {250 reply.send({ hello: 'world' })251 }252 })253 254 next()255 }, { prefix: '/prefix' })256 257 const doublePrefixReply = await fastify.inject({ url: '/prefix/prefix', method: 'HEAD' })258 const trailingSlashReply = await fastify.inject({ url: '/prefix/', method: 'HEAD' })259 const noneTrailingReply = await fastify.inject({ url: '/prefix', method: 'HEAD' })260 261 t.assert.strictEqual(doublePrefixReply.statusCode, 404)262 t.assert.strictEqual(trailingSlashReply.statusCode, 200)263 t.assert.strictEqual(noneTrailingReply.statusCode, 200)264})265 266test('GET route with body schema should throw', t => {267 t.plan(1)268 269 const fastify = Fastify()270 271 t.assert.throws(() => {272 fastify.route({273 method: 'GET',274 path: '/get',275 schema: {276 body: {}277 },278 handler: function (req, reply) {279 reply.send({ hello: 'world' })280 }281 })282 }, createError('FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED', 'Body validation schema for GET:/get route is not supported!')())283})284 285test('HEAD route with body schema should throw', t => {286 t.plan(1)287 288 const fastify = Fastify()289 290 t.assert.throws(() => {291 fastify.route({292 method: 'HEAD',293 path: '/shouldThrow',294 schema: {295 body: {}296 },297 handler: function (req, reply) {298 reply.send({ hello: 'world' })299 }300 })301 }, createError('FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED', 'Body validation schema for HEAD:/shouldThrow route is not supported!')())302})303 304test('[HEAD, GET] route with body schema should throw', t => {305 t.plan(1)306 307 const fastify = Fastify()308 309 t.assert.throws(() => {310 fastify.route({311 method: ['HEAD', 'GET'],312 path: '/shouldThrowHead',313 schema: {314 body: {}315 },316 handler: function (req, reply) {317 reply.send({ hello: 'world' })318 }319 })320 }, createError('FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED', 'Body validation schema for HEAD:/shouldThrowHead route is not supported!')())321})322 323test('GET route with body schema should throw - shorthand', t => {324 t.plan(1)325 326 const fastify = Fastify()327 328 t.assert.throws(() => {329 fastify.get('/shouldThrow', {330 schema: {331 body: {}332 }333 },334 function (req, reply) {335 reply.send({ hello: 'world' })336 }337 )338 }, createError('FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED', 'Body validation schema for GET:/shouldThrow route is not supported!')())339})340 341test('HEAD route with body schema should throw - shorthand', t => {342 t.plan(1)343 344 const fastify = Fastify()345 346 t.assert.throws(() => {347 fastify.head('/shouldThrow2', {348 schema: {349 body: {}350 }351 },352 function (req, reply) {353 reply.send({ hello: 'world' })354 }355 )356 }, createError('FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED', 'Body validation schema for HEAD:/shouldThrow2 route is not supported!')())357})358 