CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
reply-trailers.test.js435 linesDownload Raw Back to test
1'use strict'2 3const t = require('tap')4const test = t.test5const Fastify = require('..')6const { Readable } = require('node:stream')7const { createHash } = require('node:crypto')8const { sleep } = require('./helper')9 10test('send trailers when payload is empty string', t => {11  t.plan(5)12 13  const fastify = Fastify()14 15  fastify.get('/', function (request, reply) {16    reply.trailer('ETag', function (reply, payload, done) {17      done(null, 'custom-etag')18    })19    reply.send('')20  })21 22  fastify.inject({23    method: 'GET',24    url: '/'25  }, (error, res) => {26    t.error(error)27    t.equal(res.statusCode, 200)28    t.equal(res.headers.trailer, 'etag')29    t.equal(res.trailers.etag, 'custom-etag')30    t.notHas(res.headers, 'content-length')31  })32})33 34test('send trailers when payload is empty buffer', t => {35  t.plan(5)36 37  const fastify = Fastify()38 39  fastify.get('/', function (request, reply) {40    reply.trailer('ETag', function (reply, payload, done) {41      done(null, 'custom-etag')42    })43    reply.send(Buffer.alloc(0))44  })45 46  fastify.inject({47    method: 'GET',48    url: '/'49  }, (error, res) => {50    t.error(error)51    t.equal(res.statusCode, 200)52    t.equal(res.headers.trailer, 'etag')53    t.equal(res.trailers.etag, 'custom-etag')54    t.notHas(res.headers, 'content-length')55  })56})57 58test('send trailers when payload is undefined', t => {59  t.plan(5)60 61  const fastify = Fastify()62 63  fastify.get('/', function (request, reply) {64    reply.trailer('ETag', function (reply, payload, done) {65      done(null, 'custom-etag')66    })67    reply.send(undefined)68  })69 70  fastify.inject({71    method: 'GET',72    url: '/'73  }, (error, res) => {74    t.error(error)75    t.equal(res.statusCode, 200)76    t.equal(res.headers.trailer, 'etag')77    t.equal(res.trailers.etag, 'custom-etag')78    t.notHas(res.headers, 'content-length')79  })80})81 82test('send trailers when payload is json', t => {83  t.plan(7)84 85  const fastify = Fastify()86  const data = JSON.stringify({ hello: 'world' })87  const hash = createHash('md5')88  hash.update(data)89  const md5 = hash.digest('hex')90 91  fastify.get('/', function (request, reply) {92    reply.trailer('Content-MD5', function (reply, payload, done) {93      t.equal(data, payload)94      const hash = createHash('md5')95      hash.update(payload)96      done(null, hash.digest('hex'))97    })98    reply.send(data)99  })100 101  fastify.inject({102    method: 'GET',103    url: '/'104  }, (error, res) => {105    t.error(error)106    t.equal(res.statusCode, 200)107    t.equal(res.headers['transfer-encoding'], 'chunked')108    t.equal(res.headers.trailer, 'content-md5')109    t.equal(res.trailers['content-md5'], md5)110    t.notHas(res.headers, 'content-length')111  })112})113 114test('send trailers when payload is stream', t => {115  t.plan(7)116 117  const fastify = Fastify()118 119  fastify.get('/', function (request, reply) {120    reply.trailer('ETag', function (reply, payload, done) {121      t.same(payload, null)122      done(null, 'custom-etag')123    })124    const stream = Readable.from([JSON.stringify({ hello: 'world' })])125    reply.send(stream)126  })127 128  fastify.inject({129    method: 'GET',130    url: '/'131  }, (error, res) => {132    t.error(error)133    t.equal(res.statusCode, 200)134    t.equal(res.headers['transfer-encoding'], 'chunked')135    t.equal(res.headers.trailer, 'etag')136    t.equal(res.trailers.etag, 'custom-etag')137    t.notHas(res.headers, 'content-length')138  })139})140 141test('send trailers when using async-await', t => {142  t.plan(5)143 144  const fastify = Fastify()145 146  fastify.get('/', function (request, reply) {147    reply.trailer('ETag', async function (reply, payload) {148      return 'custom-etag'149    })150    reply.send('')151  })152 153  fastify.inject({154    method: 'GET',155    url: '/'156  }, (error, res) => {157    t.error(error)158    t.equal(res.statusCode, 200)159    t.equal(res.headers.trailer, 'etag')160    t.equal(res.trailers.etag, 'custom-etag')161    t.notHas(res.headers, 'content-length')162  })163})164 165test('error in trailers should be ignored', t => {166  t.plan(5)167 168  const fastify = Fastify()169 170  fastify.get('/', function (request, reply) {171    reply.trailer('ETag', function (reply, payload, done) {172      done('error')173    })174    reply.send('')175  })176 177  fastify.inject({178    method: 'GET',179    url: '/'180  }, (error, res) => {181    t.error(error)182    t.equal(res.statusCode, 200)183    t.equal(res.headers.trailer, 'etag')184    t.notHas(res.trailers, 'etag')185    t.notHas(res.headers, 'content-length')186  })187})188 189test('trailer handler counter', t => {190  t.plan(2)191 192  const data = JSON.stringify({ hello: 'world' })193  const hash = createHash('md5')194  hash.update(data)195  const md5 = hash.digest('hex')196 197  t.test('callback with timeout', t => {198    t.plan(9)199    const fastify = Fastify()200 201    fastify.get('/', function (request, reply) {202      reply.trailer('Return-Early', function (reply, payload, done) {203        t.equal(data, payload)204        done(null, 'return')205      })206      reply.trailer('Content-MD5', function (reply, payload, done) {207        t.equal(data, payload)208        const hash = createHash('md5')209        hash.update(payload)210        setTimeout(() => {211          done(null, hash.digest('hex'))212        }, 500)213      })214      reply.send(data)215    })216 217    fastify.inject({218      method: 'GET',219      url: '/'220    }, (error, res) => {221      t.error(error)222      t.equal(res.statusCode, 200)223      t.equal(res.headers['transfer-encoding'], 'chunked')224      t.equal(res.headers.trailer, 'return-early content-md5')225      t.equal(res.trailers['return-early'], 'return')226      t.equal(res.trailers['content-md5'], md5)227      t.notHas(res.headers, 'content-length')228    })229  })230 231  t.test('async-await', t => {232    t.plan(9)233    const fastify = Fastify()234 235    fastify.get('/', function (request, reply) {236      reply.trailer('Return-Early', async function (reply, payload) {237        t.equal(data, payload)238        return 'return'239      })240      reply.trailer('Content-MD5', async function (reply, payload) {241        t.equal(data, payload)242        const hash = createHash('md5')243        hash.update(payload)244        await sleep(500)245        return hash.digest('hex')246      })247      reply.send(data)248    })249 250    fastify.inject({251      method: 'GET',252      url: '/'253    }, (error, res) => {254      t.error(error)255      t.equal(res.statusCode, 200)256      t.equal(res.headers['transfer-encoding'], 'chunked')257      t.equal(res.headers.trailer, 'return-early content-md5')258      t.equal(res.trailers['return-early'], 'return')259      t.equal(res.trailers['content-md5'], md5)260      t.notHas(res.headers, 'content-length')261    })262  })263})264 265test('removeTrailer', t => {266  t.plan(6)267 268  const fastify = Fastify()269 270  fastify.get('/', function (request, reply) {271    reply.removeTrailer('ETag') // remove nothing272    reply.trailer('ETag', function (reply, payload, done) {273      done(null, 'custom-etag')274    })275    reply.trailer('Should-Not-Call', function (reply, payload, done) {276      t.fail('it should not called as this trailer is removed')277      done(null, 'should-not-call')278    })279    reply.removeTrailer('Should-Not-Call')280    reply.send(undefined)281  })282 283  fastify.inject({284    method: 'GET',285    url: '/'286  }, (error, res) => {287    t.error(error)288    t.equal(res.statusCode, 200)289    t.equal(res.headers.trailer, 'etag')290    t.equal(res.trailers.etag, 'custom-etag')291    t.notOk(res.trailers['should-not-call'])292    t.notHas(res.headers, 'content-length')293  })294})295 296test('remove all trailers', t => {297  t.plan(6)298 299  const fastify = Fastify()300 301  fastify.get('/', function (request, reply) {302    reply.trailer('ETag', function (reply, payload, done) {303      t.fail('it should not called as this trailer is removed')304      done(null, 'custom-etag')305    })306    reply.removeTrailer('ETag')307    reply.trailer('Should-Not-Call', function (reply, payload, done) {308      t.fail('it should not called as this trailer is removed')309      done(null, 'should-not-call')310    })311    reply.removeTrailer('Should-Not-Call')312    reply.send('')313  })314 315  fastify.inject({316    method: 'GET',317    url: '/'318  }, (error, res) => {319    t.error(error)320    t.equal(res.statusCode, 200)321    t.notOk(res.headers.trailer)322    t.notOk(res.trailers.etag)323    t.notOk(res.trailers['should-not-call'])324    t.notHas(res.headers, 'content-length')325  })326})327 328test('hasTrailer', t => {329  t.plan(10)330 331  const fastify = Fastify()332 333  fastify.get('/', function (request, reply) {334    t.equal(reply.hasTrailer('ETag'), false)335    reply.trailer('ETag', function (reply, payload, done) {336      done(null, 'custom-etag')337    })338    t.equal(reply.hasTrailer('ETag'), true)339    reply.trailer('Should-Not-Call', function (reply, payload, done) {340      t.fail('it should not called as this trailer is removed')341      done(null, 'should-not-call')342    })343    t.equal(reply.hasTrailer('Should-Not-Call'), true)344    reply.removeTrailer('Should-Not-Call')345    t.equal(reply.hasTrailer('Should-Not-Call'), false)346    reply.send(undefined)347  })348 349  fastify.inject({350    method: 'GET',351    url: '/'352  }, (error, res) => {353    t.error(error)354    t.equal(res.statusCode, 200)355    t.equal(res.headers.trailer, 'etag')356    t.equal(res.trailers.etag, 'custom-etag')357    t.notOk(res.trailers['should-not-call'])358    t.notHas(res.headers, 'content-length')359  })360})361 362test('throw error when trailer header name is not allowed', t => {363  const INVALID_TRAILERS = [364    'transfer-encoding',365    'content-length',366    'host',367    'cache-control',368    'max-forwards',369    'te',370    'authorization',371    'set-cookie',372    'content-encoding',373    'content-type',374    'content-range',375    'trailer'376  ]377  t.plan(INVALID_TRAILERS.length + 2)378 379  const fastify = Fastify()380 381  fastify.get('/', function (request, reply) {382    for (const key of INVALID_TRAILERS) {383      try {384        reply.trailer(key, () => { })385      } catch (err) {386        t.equal(err.message, `Called reply.trailer with an invalid header name: ${key}`)387      }388    }389    reply.send('')390  })391 392  fastify.inject({393    method: 'GET',394    url: '/'395  }, (error, res) => {396    t.error(error)397    t.equal(res.statusCode, 200)398  })399})400 401test('throw error when trailer header value is not function', t => {402  const INVALID_TRAILERS_VALUE = [403    undefined,404    null,405    true,406    false,407    'invalid',408    [],409    new Date(),410    {}411  ]412  t.plan(INVALID_TRAILERS_VALUE.length + 2)413 414  const fastify = Fastify()415 416  fastify.get('/', function (request, reply) {417    for (const value of INVALID_TRAILERS_VALUE) {418      try {419        reply.trailer('invalid', value)420      } catch (err) {421        t.equal(err.message, `Called reply.trailer('invalid', fn) with an invalid type: ${typeof value}. Expected a function.`)422      }423    }424    reply.send('')425  })426 427  fastify.inject({428    method: 'GET',429    url: '/'430  }, (error, res) => {431    t.error(error)432    t.equal(res.statusCode, 200)433  })434})435