node-fastify 5.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/.borp.yaml +3 -0
  2. package/.markdownlint-cli2.yaml +22 -0
  3. package/.prettierignore +1 -0
  4. package/GOVERNANCE.md +4 -0
  5. package/LICENSE +21 -0
  6. package/PROJECT_CHARTER.md +126 -0
  7. package/README.md +423 -0
  8. package/SECURITY.md +220 -0
  9. package/SPONSORS.md +24 -0
  10. package/build/build-error-serializer.js +35 -0
  11. package/build/build-validation.js +169 -0
  12. package/build/sync-version.js +11 -0
  13. package/docs/Guides/Benchmarking.md +60 -0
  14. package/docs/Guides/Database.md +321 -0
  15. package/docs/Guides/Delay-Accepting-Requests.md +608 -0
  16. package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
  17. package/docs/Guides/Ecosystem.md +726 -0
  18. package/docs/Guides/Fluent-Schema.md +127 -0
  19. package/docs/Guides/Getting-Started.md +620 -0
  20. package/docs/Guides/Index.md +43 -0
  21. package/docs/Guides/Migration-Guide-V3.md +287 -0
  22. package/docs/Guides/Migration-Guide-V4.md +267 -0
  23. package/docs/Guides/Migration-Guide-V5.md +727 -0
  24. package/docs/Guides/Plugins-Guide.md +520 -0
  25. package/docs/Guides/Prototype-Poisoning.md +383 -0
  26. package/docs/Guides/Recommendations.md +378 -0
  27. package/docs/Guides/Serverless.md +604 -0
  28. package/docs/Guides/Style-Guide.md +246 -0
  29. package/docs/Guides/Testing.md +481 -0
  30. package/docs/Guides/Write-Plugin.md +103 -0
  31. package/docs/Guides/Write-Type-Provider.md +34 -0
  32. package/docs/Reference/ContentTypeParser.md +271 -0
  33. package/docs/Reference/Decorators.md +436 -0
  34. package/docs/Reference/Encapsulation.md +194 -0
  35. package/docs/Reference/Errors.md +377 -0
  36. package/docs/Reference/HTTP2.md +94 -0
  37. package/docs/Reference/Hooks.md +958 -0
  38. package/docs/Reference/Index.md +73 -0
  39. package/docs/Reference/LTS.md +86 -0
  40. package/docs/Reference/Lifecycle.md +99 -0
  41. package/docs/Reference/Logging.md +268 -0
  42. package/docs/Reference/Middleware.md +79 -0
  43. package/docs/Reference/Plugins.md +245 -0
  44. package/docs/Reference/Principles.md +73 -0
  45. package/docs/Reference/Reply.md +1001 -0
  46. package/docs/Reference/Request.md +295 -0
  47. package/docs/Reference/Routes.md +802 -0
  48. package/docs/Reference/Server.md +2389 -0
  49. package/docs/Reference/Type-Providers.md +256 -0
  50. package/docs/Reference/TypeScript.md +1729 -0
  51. package/docs/Reference/Validation-and-Serialization.md +1130 -0
  52. package/docs/Reference/Warnings.md +58 -0
  53. package/docs/index.md +24 -0
  54. package/docs/resources/encapsulation_context.drawio +1 -0
  55. package/docs/resources/encapsulation_context.svg +3 -0
  56. package/eslint.config.js +35 -0
  57. package/examples/asyncawait.js +38 -0
  58. package/examples/benchmark/body.json +3 -0
  59. package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
  60. package/examples/benchmark/hooks-benchmark.js +52 -0
  61. package/examples/benchmark/parser.js +47 -0
  62. package/examples/benchmark/simple.js +30 -0
  63. package/examples/benchmark/webstream.js +27 -0
  64. package/examples/hooks.js +91 -0
  65. package/examples/http2.js +39 -0
  66. package/examples/https.js +38 -0
  67. package/examples/parser.js +53 -0
  68. package/examples/plugin.js +12 -0
  69. package/examples/route-prefix.js +38 -0
  70. package/examples/shared-schema.js +38 -0
  71. package/examples/simple-stream.js +20 -0
  72. package/examples/simple.js +32 -0
  73. package/examples/simple.mjs +27 -0
  74. package/examples/typescript-server.ts +79 -0
  75. package/examples/use-plugin.js +29 -0
  76. package/fastify.d.ts +253 -0
  77. package/fastify.js +985 -0
  78. package/integration/server.js +29 -0
  79. package/integration/test.sh +23 -0
  80. package/lib/config-validator.js +1266 -0
  81. package/lib/content-type-parser.js +413 -0
  82. package/lib/content-type.js +160 -0
  83. package/lib/context.js +98 -0
  84. package/lib/decorate.js +152 -0
  85. package/lib/error-handler.js +173 -0
  86. package/lib/error-serializer.js +134 -0
  87. package/lib/error-status.js +14 -0
  88. package/lib/errors.js +516 -0
  89. package/lib/four-oh-four.js +190 -0
  90. package/lib/handle-request.js +195 -0
  91. package/lib/head-route.js +45 -0
  92. package/lib/hooks.js +429 -0
  93. package/lib/initial-config-validation.js +37 -0
  94. package/lib/logger-factory.js +136 -0
  95. package/lib/logger-pino.js +68 -0
  96. package/lib/noop-set.js +10 -0
  97. package/lib/plugin-override.js +90 -0
  98. package/lib/plugin-utils.js +169 -0
  99. package/lib/promise.js +23 -0
  100. package/lib/reply.js +1030 -0
  101. package/lib/req-id-gen-factory.js +52 -0
  102. package/lib/request.js +391 -0
  103. package/lib/route.js +686 -0
  104. package/lib/schema-controller.js +164 -0
  105. package/lib/schemas.js +207 -0
  106. package/lib/server.js +441 -0
  107. package/lib/symbols.js +71 -0
  108. package/lib/validation.js +280 -0
  109. package/lib/warnings.js +57 -0
  110. package/lib/wrap-thenable.js +84 -0
  111. package/package.json +225 -0
  112. package/scripts/validate-ecosystem-links.js +179 -0
  113. package/test/404s.test.js +2035 -0
  114. package/test/500s.test.js +422 -0
  115. package/test/allow-unsafe-regex.test.js +92 -0
  116. package/test/als.test.js +65 -0
  117. package/test/async-await.test.js +705 -0
  118. package/test/async-dispose.test.js +20 -0
  119. package/test/async_hooks.test.js +52 -0
  120. package/test/body-limit.test.js +224 -0
  121. package/test/buffer.test.js +74 -0
  122. package/test/build/error-serializer.test.js +36 -0
  123. package/test/build/version.test.js +14 -0
  124. package/test/build-certificate.js +109 -0
  125. package/test/bundler/README.md +29 -0
  126. package/test/bundler/esbuild/bundler-test.js +32 -0
  127. package/test/bundler/esbuild/package.json +10 -0
  128. package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
  129. package/test/bundler/esbuild/src/index.js +9 -0
  130. package/test/bundler/webpack/bundler-test.js +32 -0
  131. package/test/bundler/webpack/package.json +11 -0
  132. package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
  133. package/test/bundler/webpack/src/index.js +9 -0
  134. package/test/bundler/webpack/webpack.config.js +15 -0
  135. package/test/case-insensitive.test.js +102 -0
  136. package/test/chainable.test.js +40 -0
  137. package/test/child-logger-factory.test.js +128 -0
  138. package/test/client-timeout.test.js +38 -0
  139. package/test/close-pipelining.test.js +78 -0
  140. package/test/close.test.js +706 -0
  141. package/test/conditional-pino.test.js +47 -0
  142. package/test/connection-timeout.test.js +42 -0
  143. package/test/constrained-routes.test.js +1138 -0
  144. package/test/content-length.test.js +174 -0
  145. package/test/content-parser.test.js +739 -0
  146. package/test/content-type.test.js +181 -0
  147. package/test/context-config.test.js +164 -0
  148. package/test/custom-http-server.test.js +118 -0
  149. package/test/custom-parser-async.test.js +59 -0
  150. package/test/custom-parser.0.test.js +701 -0
  151. package/test/custom-parser.1.test.js +266 -0
  152. package/test/custom-parser.2.test.js +91 -0
  153. package/test/custom-parser.3.test.js +208 -0
  154. package/test/custom-parser.4.test.js +218 -0
  155. package/test/custom-parser.5.test.js +130 -0
  156. package/test/custom-querystring-parser.test.js +129 -0
  157. package/test/decorator.test.js +1330 -0
  158. package/test/delete.test.js +344 -0
  159. package/test/diagnostics-channel/404.test.js +49 -0
  160. package/test/diagnostics-channel/async-delay-request.test.js +65 -0
  161. package/test/diagnostics-channel/async-request.test.js +64 -0
  162. package/test/diagnostics-channel/error-before-handler.test.js +35 -0
  163. package/test/diagnostics-channel/error-request.test.js +53 -0
  164. package/test/diagnostics-channel/error-status.test.js +123 -0
  165. package/test/diagnostics-channel/init.test.js +50 -0
  166. package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
  167. package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
  168. package/test/diagnostics-channel/sync-request.test.js +54 -0
  169. package/test/encapsulated-child-logger-factory.test.js +69 -0
  170. package/test/encapsulated-error-handler.test.js +237 -0
  171. package/test/esm/errorCodes.test.mjs +10 -0
  172. package/test/esm/esm.test.mjs +13 -0
  173. package/test/esm/index.test.js +8 -0
  174. package/test/esm/named-exports.mjs +14 -0
  175. package/test/esm/other.mjs +8 -0
  176. package/test/esm/plugin.mjs +8 -0
  177. package/test/fastify-instance.test.js +300 -0
  178. package/test/find-route.test.js +152 -0
  179. package/test/fluent-schema.test.js +209 -0
  180. package/test/genReqId.test.js +426 -0
  181. package/test/handler-context.test.js +45 -0
  182. package/test/handler-timeout.test.js +367 -0
  183. package/test/has-route.test.js +88 -0
  184. package/test/header-overflow.test.js +55 -0
  185. package/test/helper.js +496 -0
  186. package/test/hooks-async.test.js +1099 -0
  187. package/test/hooks.on-listen.test.js +1162 -0
  188. package/test/hooks.on-ready.test.js +421 -0
  189. package/test/hooks.test.js +3578 -0
  190. package/test/http-methods/copy.test.js +35 -0
  191. package/test/http-methods/custom-http-methods.test.js +114 -0
  192. package/test/http-methods/get.test.js +412 -0
  193. package/test/http-methods/head.test.js +263 -0
  194. package/test/http-methods/lock.test.js +108 -0
  195. package/test/http-methods/mkcalendar.test.js +143 -0
  196. package/test/http-methods/mkcol.test.js +35 -0
  197. package/test/http-methods/move.test.js +42 -0
  198. package/test/http-methods/propfind.test.js +136 -0
  199. package/test/http-methods/proppatch.test.js +105 -0
  200. package/test/http-methods/report.test.js +142 -0
  201. package/test/http-methods/search.test.js +233 -0
  202. package/test/http-methods/trace.test.js +21 -0
  203. package/test/http-methods/unlock.test.js +38 -0
  204. package/test/http2/closing.test.js +270 -0
  205. package/test/http2/constraint.test.js +109 -0
  206. package/test/http2/head.test.js +34 -0
  207. package/test/http2/plain.test.js +68 -0
  208. package/test/http2/secure-with-fallback.test.js +113 -0
  209. package/test/http2/secure.test.js +67 -0
  210. package/test/http2/unknown-http-method.test.js +34 -0
  211. package/test/https/custom-https-server.test.js +58 -0
  212. package/test/https/https.test.js +136 -0
  213. package/test/imports.test.js +17 -0
  214. package/test/inject.test.js +502 -0
  215. package/test/input-validation.js +335 -0
  216. package/test/internals/all.test.js +38 -0
  217. package/test/internals/content-type-parser.test.js +111 -0
  218. package/test/internals/context.test.js +31 -0
  219. package/test/internals/decorator.test.js +156 -0
  220. package/test/internals/errors.test.js +982 -0
  221. package/test/internals/handle-request.test.js +270 -0
  222. package/test/internals/hook-runner.test.js +449 -0
  223. package/test/internals/hooks.test.js +96 -0
  224. package/test/internals/initial-config.test.js +383 -0
  225. package/test/internals/logger.test.js +163 -0
  226. package/test/internals/plugin.test.js +170 -0
  227. package/test/internals/promise.test.js +63 -0
  228. package/test/internals/reply-serialize.test.js +714 -0
  229. package/test/internals/reply.test.js +1920 -0
  230. package/test/internals/req-id-gen-factory.test.js +133 -0
  231. package/test/internals/request-validate.test.js +1402 -0
  232. package/test/internals/request.test.js +506 -0
  233. package/test/internals/schema-controller-perf.test.js +40 -0
  234. package/test/internals/server.test.js +91 -0
  235. package/test/internals/validation.test.js +352 -0
  236. package/test/issue-4959.test.js +118 -0
  237. package/test/keep-alive-timeout.test.js +42 -0
  238. package/test/listen.1.test.js +154 -0
  239. package/test/listen.2.test.js +113 -0
  240. package/test/listen.3.test.js +83 -0
  241. package/test/listen.4.test.js +168 -0
  242. package/test/listen.5.test.js +122 -0
  243. package/test/logger/instantiation.test.js +341 -0
  244. package/test/logger/logger-test-utils.js +47 -0
  245. package/test/logger/logging.test.js +460 -0
  246. package/test/logger/options.test.js +579 -0
  247. package/test/logger/request.test.js +292 -0
  248. package/test/logger/response.test.js +183 -0
  249. package/test/logger/tap-parallel-not-ok +0 -0
  250. package/test/max-requests-per-socket.test.js +113 -0
  251. package/test/middleware.test.js +37 -0
  252. package/test/noop-set.test.js +19 -0
  253. package/test/nullable-validation.test.js +187 -0
  254. package/test/options.error-handler.test.js +5 -0
  255. package/test/options.test.js +5 -0
  256. package/test/output-validation.test.js +140 -0
  257. package/test/patch.error-handler.test.js +5 -0
  258. package/test/patch.test.js +5 -0
  259. package/test/plugin.1.test.js +230 -0
  260. package/test/plugin.2.test.js +314 -0
  261. package/test/plugin.3.test.js +287 -0
  262. package/test/plugin.4.test.js +504 -0
  263. package/test/plugin.helper.js +8 -0
  264. package/test/plugin.name.display.js +10 -0
  265. package/test/post-empty-body.test.js +38 -0
  266. package/test/pretty-print.test.js +366 -0
  267. package/test/promises.test.js +125 -0
  268. package/test/proto-poisoning.test.js +145 -0
  269. package/test/put.error-handler.test.js +5 -0
  270. package/test/put.test.js +5 -0
  271. package/test/register.test.js +184 -0
  272. package/test/reply-code.test.js +148 -0
  273. package/test/reply-early-hints.test.js +100 -0
  274. package/test/reply-error.test.js +815 -0
  275. package/test/reply-trailers.test.js +445 -0
  276. package/test/reply-web-stream-locked.test.js +37 -0
  277. package/test/request-error.test.js +624 -0
  278. package/test/request-header-host.test.js +339 -0
  279. package/test/request-id.test.js +118 -0
  280. package/test/request-timeout.test.js +53 -0
  281. package/test/route-hooks.test.js +635 -0
  282. package/test/route-prefix.test.js +904 -0
  283. package/test/route-shorthand.test.js +48 -0
  284. package/test/route.1.test.js +259 -0
  285. package/test/route.2.test.js +100 -0
  286. package/test/route.3.test.js +213 -0
  287. package/test/route.4.test.js +127 -0
  288. package/test/route.5.test.js +211 -0
  289. package/test/route.6.test.js +306 -0
  290. package/test/route.7.test.js +406 -0
  291. package/test/route.8.test.js +225 -0
  292. package/test/router-options.test.js +1108 -0
  293. package/test/same-shape.test.js +124 -0
  294. package/test/schema-examples.test.js +661 -0
  295. package/test/schema-feature.test.js +2198 -0
  296. package/test/schema-serialization.test.js +1171 -0
  297. package/test/schema-special-usage.test.js +1348 -0
  298. package/test/schema-validation.test.js +1572 -0
  299. package/test/scripts/validate-ecosystem-links.test.js +339 -0
  300. package/test/serialize-response.test.js +186 -0
  301. package/test/server.test.js +347 -0
  302. package/test/set-error-handler.test.js +69 -0
  303. package/test/skip-reply-send.test.js +317 -0
  304. package/test/stream-serializers.test.js +40 -0
  305. package/test/stream.1.test.js +94 -0
  306. package/test/stream.2.test.js +129 -0
  307. package/test/stream.3.test.js +198 -0
  308. package/test/stream.4.test.js +176 -0
  309. package/test/stream.5.test.js +188 -0
  310. package/test/sync-routes.test.js +32 -0
  311. package/test/throw.test.js +359 -0
  312. package/test/toolkit.js +63 -0
  313. package/test/trust-proxy.test.js +162 -0
  314. package/test/type-provider.test.js +22 -0
  315. package/test/types/content-type-parser.test-d.ts +72 -0
  316. package/test/types/decorate-request-reply.test-d.ts +18 -0
  317. package/test/types/dummy-plugin.ts +9 -0
  318. package/test/types/errors.test-d.ts +90 -0
  319. package/test/types/fastify.test-d.ts +352 -0
  320. package/test/types/hooks.test-d.ts +550 -0
  321. package/test/types/import.ts +2 -0
  322. package/test/types/instance.test-d.ts +588 -0
  323. package/test/types/logger.test-d.ts +277 -0
  324. package/test/types/plugin.test-d.ts +97 -0
  325. package/test/types/register.test-d.ts +237 -0
  326. package/test/types/reply.test-d.ts +254 -0
  327. package/test/types/request.test-d.ts +188 -0
  328. package/test/types/route.test-d.ts +553 -0
  329. package/test/types/schema.test-d.ts +135 -0
  330. package/test/types/serverFactory.test-d.ts +37 -0
  331. package/test/types/type-provider.test-d.ts +1213 -0
  332. package/test/types/using.test-d.ts +17 -0
  333. package/test/upgrade.test.js +52 -0
  334. package/test/url-rewriting.test.js +122 -0
  335. package/test/use-semicolon-delimiter.test.js +168 -0
  336. package/test/validation-error-handling.test.js +900 -0
  337. package/test/versioned-routes.test.js +603 -0
  338. package/test/web-api.test.js +616 -0
  339. package/test/wrap-thenable.test.js +30 -0
  340. package/types/content-type-parser.d.ts +75 -0
  341. package/types/context.d.ts +22 -0
  342. package/types/errors.d.ts +92 -0
  343. package/types/hooks.d.ts +875 -0
  344. package/types/instance.d.ts +609 -0
  345. package/types/logger.d.ts +107 -0
  346. package/types/plugin.d.ts +44 -0
  347. package/types/register.d.ts +42 -0
  348. package/types/reply.d.ts +81 -0
  349. package/types/request.d.ts +95 -0
  350. package/types/route.d.ts +199 -0
  351. package/types/schema.d.ts +61 -0
  352. package/types/server-factory.d.ts +19 -0
  353. package/types/type-provider.d.ts +130 -0
  354. package/types/utils.d.ts +98 -0
@@ -0,0 +1,1099 @@
1
+ 'use strict'
2
+
3
+ const { Readable } = require('node:stream')
4
+ const { test, describe } = require('node:test')
5
+ const Fastify = require('../fastify')
6
+ const fs = require('node:fs')
7
+ const { sleep } = require('./helper')
8
+ const { waitForCb } = require('./toolkit')
9
+
10
+ process.removeAllListeners('warning')
11
+
12
+ test('async hooks', async t => {
13
+ t.plan(20)
14
+ const fastify = Fastify({ exposeHeadRoutes: false })
15
+ fastify.addHook('onRequest', async function (request, reply) {
16
+ await sleep(1)
17
+ request.test = 'the request is coming'
18
+ reply.test = 'the reply has come'
19
+ if (request.raw.method === 'DELETE') {
20
+ throw new Error('some error')
21
+ }
22
+ })
23
+
24
+ fastify.addHook('preHandler', async function (request, reply) {
25
+ await sleep(1)
26
+ t.assert.strictEqual(request.test, 'the request is coming')
27
+ t.assert.strictEqual(reply.test, 'the reply has come')
28
+ if (request.raw.method === 'HEAD') {
29
+ throw new Error('some error')
30
+ }
31
+ })
32
+
33
+ fastify.addHook('onSend', async function (request, reply, payload) {
34
+ await sleep(1)
35
+ t.assert.ok('onSend called')
36
+ })
37
+
38
+ const completion = waitForCb({
39
+ steps: 6
40
+ })
41
+ fastify.addHook('onResponse', async function (request, reply) {
42
+ await sleep(1)
43
+ t.assert.ok('onResponse called')
44
+ completion.stepIn()
45
+ })
46
+
47
+ fastify.get('/', function (request, reply) {
48
+ t.assert.strictEqual(request.test, 'the request is coming')
49
+ t.assert.strictEqual(reply.test, 'the reply has come')
50
+ reply.code(200).send({ hello: 'world' })
51
+ })
52
+
53
+ fastify.head('/', function (req, reply) {
54
+ reply.code(200).send({ hello: 'world' })
55
+ })
56
+
57
+ fastify.delete('/', function (req, reply) {
58
+ reply.code(200).send({ hello: 'world' })
59
+ })
60
+
61
+ const fastifyServer = await fastify.listen({ port: 0 })
62
+ t.after(() => { fastify.close() })
63
+
64
+ const response1 = await fetch(fastifyServer, {
65
+ method: 'GET'
66
+ })
67
+ t.assert.ok(response1.ok)
68
+ t.assert.strictEqual(response1.status, 200)
69
+ const body1 = await response1.text()
70
+ t.assert.strictEqual(response1.headers.get('content-length'), '' + body1.length)
71
+ t.assert.deepStrictEqual(JSON.parse(body1), { hello: 'world' })
72
+ completion.stepIn()
73
+
74
+ const response2 = await fetch(fastifyServer, {
75
+ method: 'HEAD'
76
+ })
77
+ t.assert.ok(!response2.ok)
78
+ t.assert.strictEqual(response2.status, 500)
79
+ completion.stepIn()
80
+
81
+ const response3 = await fetch(fastifyServer, {
82
+ method: 'DELETE'
83
+ })
84
+ t.assert.ok(!response3.ok)
85
+ t.assert.strictEqual(response3.status, 500)
86
+ completion.stepIn()
87
+
88
+ return completion.patience
89
+ })
90
+
91
+ test('modify payload', (t, testDone) => {
92
+ t.plan(10)
93
+ const fastify = Fastify()
94
+ const payload = { hello: 'world' }
95
+ const modifiedPayload = { hello: 'modified' }
96
+ const anotherPayload = '"winter is coming"'
97
+
98
+ fastify.addHook('onSend', async function (request, reply, thePayload) {
99
+ t.assert.ok('onSend called')
100
+ t.assert.deepStrictEqual(JSON.parse(thePayload), payload)
101
+ return thePayload.replace('world', 'modified')
102
+ })
103
+
104
+ fastify.addHook('onSend', async function (request, reply, thePayload) {
105
+ t.assert.ok('onSend called')
106
+ t.assert.deepStrictEqual(JSON.parse(thePayload), modifiedPayload)
107
+ return anotherPayload
108
+ })
109
+
110
+ fastify.addHook('onSend', async function (request, reply, thePayload) {
111
+ t.assert.ok('onSend called')
112
+ t.assert.deepStrictEqual(thePayload, anotherPayload)
113
+ })
114
+
115
+ fastify.get('/', (req, reply) => {
116
+ reply.send(payload)
117
+ })
118
+
119
+ fastify.inject({
120
+ method: 'GET',
121
+ url: '/'
122
+ }, (err, res) => {
123
+ t.assert.ifError(err)
124
+ t.assert.strictEqual(res.payload, anotherPayload)
125
+ t.assert.strictEqual(res.statusCode, 200)
126
+ t.assert.strictEqual(res.headers['content-length'], '18')
127
+ testDone()
128
+ })
129
+ })
130
+
131
+ test('onRequest hooks should be able to block a request', (t, testDone) => {
132
+ t.plan(5)
133
+ const fastify = Fastify()
134
+
135
+ fastify.addHook('onRequest', async (req, reply) => {
136
+ await reply.send('hello')
137
+ })
138
+
139
+ fastify.addHook('onRequest', async (req, reply) => {
140
+ t.assert.fail('this should not be called')
141
+ })
142
+
143
+ fastify.addHook('preHandler', async (req, reply) => {
144
+ t.assert.fail('this should not be called')
145
+ })
146
+
147
+ fastify.addHook('onSend', async (req, reply, payload) => {
148
+ t.assert.ok('called')
149
+ })
150
+
151
+ fastify.addHook('onResponse', async (request, reply) => {
152
+ t.assert.ok('called')
153
+ })
154
+
155
+ fastify.get('/', function (request, reply) {
156
+ t.assert.fail('we should not be here')
157
+ })
158
+
159
+ fastify.inject({
160
+ url: '/',
161
+ method: 'GET'
162
+ }, (err, res) => {
163
+ t.assert.ifError(err)
164
+ t.assert.strictEqual(res.statusCode, 200)
165
+ t.assert.strictEqual(res.payload, 'hello')
166
+ testDone()
167
+ })
168
+ })
169
+
170
+ test('preParsing hooks should be able to modify the payload', (t, testDone) => {
171
+ t.plan(3)
172
+ const fastify = Fastify()
173
+
174
+ fastify.addHook('preParsing', async (req, reply, payload) => {
175
+ const stream = new Readable()
176
+
177
+ stream.receivedEncodedLength = parseInt(req.headers['content-length'], 10)
178
+ stream.push(JSON.stringify({ hello: 'another world' }))
179
+ stream.push(null)
180
+
181
+ return stream
182
+ })
183
+
184
+ fastify.post('/', function (request, reply) {
185
+ reply.send(request.body)
186
+ })
187
+
188
+ fastify.inject({
189
+ method: 'POST',
190
+ url: '/',
191
+ payload: { hello: 'world' }
192
+ }, (err, res) => {
193
+ t.assert.ifError(err)
194
+ t.assert.strictEqual(res.statusCode, 200)
195
+ t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'another world' })
196
+ testDone()
197
+ })
198
+ })
199
+
200
+ test('preParsing hooks should be able to supply statusCode', (t, testDone) => {
201
+ t.plan(4)
202
+ const fastify = Fastify()
203
+
204
+ fastify.addHook('preParsing', async (req, reply, payload) => {
205
+ const stream = new Readable({
206
+ read () {
207
+ const error = new Error('kaboom')
208
+ error.statusCode = 408
209
+ this.destroy(error)
210
+ }
211
+ })
212
+ stream.receivedEncodedLength = 20
213
+ return stream
214
+ })
215
+
216
+ fastify.addHook('onError', async (req, res, err) => {
217
+ t.assert.strictEqual(err.statusCode, 408)
218
+ })
219
+
220
+ fastify.post('/', function (request, reply) {
221
+ t.assert.fail('should not be called')
222
+ })
223
+
224
+ fastify.inject({
225
+ method: 'POST',
226
+ url: '/',
227
+ payload: { hello: 'world' }
228
+ }, (err, res) => {
229
+ t.assert.ifError(err)
230
+ t.assert.strictEqual(res.statusCode, 408)
231
+ t.assert.deepStrictEqual(JSON.parse(res.payload), {
232
+ statusCode: 408,
233
+ error: 'Request Timeout',
234
+ message: 'kaboom'
235
+ })
236
+
237
+ testDone()
238
+ })
239
+ })
240
+
241
+ test('preParsing hooks should ignore statusCode 200 in stream error', (t, testDone) => {
242
+ t.plan(4)
243
+ const fastify = Fastify()
244
+
245
+ fastify.addHook('preParsing', async (req, reply, payload) => {
246
+ const stream = new Readable({
247
+ read () {
248
+ const error = new Error('kaboom')
249
+ error.statusCode = 200
250
+ this.destroy(error)
251
+ }
252
+ })
253
+ stream.receivedEncodedLength = 20
254
+ return stream
255
+ })
256
+
257
+ fastify.addHook('onError', async (req, res, err) => {
258
+ t.assert.strictEqual(err.statusCode, 400)
259
+ })
260
+
261
+ fastify.post('/', function (request, reply) {
262
+ t.assert.fail('should not be called')
263
+ })
264
+
265
+ fastify.inject({
266
+ method: 'POST',
267
+ url: '/',
268
+ payload: { hello: 'world' }
269
+ }, (err, res) => {
270
+ t.assert.ifError(err)
271
+ t.assert.strictEqual(res.statusCode, 400)
272
+ t.assert.deepStrictEqual(JSON.parse(res.payload), {
273
+ statusCode: 400,
274
+ error: 'Bad Request',
275
+ message: 'kaboom'
276
+ })
277
+ testDone()
278
+ })
279
+ })
280
+
281
+ test('preParsing hooks should ignore non-number statusCode in stream error', (t, testDone) => {
282
+ t.plan(4)
283
+ const fastify = Fastify()
284
+
285
+ fastify.addHook('preParsing', async (req, reply, payload) => {
286
+ const stream = new Readable({
287
+ read () {
288
+ const error = new Error('kaboom')
289
+ error.statusCode = '418'
290
+ this.destroy(error)
291
+ }
292
+ })
293
+ stream.receivedEncodedLength = 20
294
+ return stream
295
+ })
296
+
297
+ fastify.addHook('onError', async (req, res, err) => {
298
+ t.assert.strictEqual(err.statusCode, 400)
299
+ })
300
+
301
+ fastify.post('/', function (request, reply) {
302
+ t.assert.fail('should not be called')
303
+ })
304
+
305
+ fastify.inject({
306
+ method: 'POST',
307
+ url: '/',
308
+ payload: { hello: 'world' }
309
+ }, (err, res) => {
310
+ t.assert.ifError(err)
311
+ t.assert.strictEqual(res.statusCode, 400)
312
+ t.assert.deepStrictEqual(JSON.parse(res.payload), {
313
+ statusCode: 400,
314
+ error: 'Bad Request',
315
+ message: 'kaboom'
316
+ })
317
+ testDone()
318
+ })
319
+ })
320
+
321
+ test('preParsing hooks should default to statusCode 400 if stream error', (t, testDone) => {
322
+ t.plan(4)
323
+ const fastify = Fastify()
324
+
325
+ fastify.addHook('preParsing', async (req, reply, payload) => {
326
+ const stream = new Readable({
327
+ read () {
328
+ this.destroy(new Error('kaboom'))
329
+ }
330
+ })
331
+ stream.receivedEncodedLength = 20
332
+ return stream
333
+ })
334
+
335
+ fastify.addHook('onError', async (req, res, err) => {
336
+ t.assert.strictEqual(err.statusCode, 400)
337
+ })
338
+
339
+ fastify.post('/', function (request, reply) {
340
+ t.assert.fail('should not be called')
341
+ })
342
+
343
+ fastify.inject({
344
+ method: 'POST',
345
+ url: '/',
346
+ payload: { hello: 'world' }
347
+ }, (err, res) => {
348
+ t.assert.ifError(err)
349
+ t.assert.strictEqual(res.statusCode, 400)
350
+ t.assert.deepStrictEqual(JSON.parse(res.payload), {
351
+ statusCode: 400,
352
+ error: 'Bad Request',
353
+ message: 'kaboom'
354
+ })
355
+ testDone()
356
+ })
357
+ })
358
+
359
+ test('preParsing hooks should handle errors', (t, testDone) => {
360
+ t.plan(3)
361
+
362
+ const fastify = Fastify()
363
+ fastify.addHook('preParsing', async (req, reply, payload) => {
364
+ const e = new Error('kaboom')
365
+ e.statusCode = 501
366
+ throw e
367
+ })
368
+
369
+ fastify.post('/', function (request, reply) {
370
+ reply.send(request.body)
371
+ })
372
+
373
+ fastify.inject({
374
+ method: 'POST',
375
+ url: '/',
376
+ payload: { hello: 'world' }
377
+ }, (err, res) => {
378
+ t.assert.ifError(err)
379
+ t.assert.strictEqual(res.statusCode, 501)
380
+ t.assert.deepStrictEqual(JSON.parse(res.payload), { error: 'Not Implemented', message: 'kaboom', statusCode: 501 })
381
+ testDone()
382
+ })
383
+ })
384
+
385
+ test('preHandler hooks should be able to block a request', (t, testDone) => {
386
+ t.plan(5)
387
+ const fastify = Fastify()
388
+
389
+ fastify.addHook('preHandler', async (req, reply) => {
390
+ await reply.send('hello')
391
+ })
392
+
393
+ fastify.addHook('preHandler', async (req, reply) => {
394
+ t.assert.fail('this should not be called')
395
+ })
396
+
397
+ fastify.addHook('onSend', async (req, reply, payload) => {
398
+ t.assert.strictEqual(payload, 'hello')
399
+ })
400
+
401
+ fastify.addHook('onResponse', async (request, reply) => {
402
+ t.assert.ok('called')
403
+ })
404
+
405
+ fastify.get('/', function (request, reply) {
406
+ t.assert.fail('we should not be here')
407
+ })
408
+
409
+ fastify.inject({
410
+ url: '/',
411
+ method: 'GET'
412
+ }, (err, res) => {
413
+ t.assert.ifError(err)
414
+ t.assert.strictEqual(res.statusCode, 200)
415
+ t.assert.strictEqual(res.payload, 'hello')
416
+ testDone()
417
+ })
418
+ })
419
+
420
+ test('preValidation hooks should be able to block a request', (t, testDone) => {
421
+ t.plan(5)
422
+ const fastify = Fastify()
423
+
424
+ fastify.addHook('preValidation', async (req, reply) => {
425
+ await reply.send('hello')
426
+ })
427
+
428
+ fastify.addHook('preValidation', async (req, reply) => {
429
+ t.assert.fail('this should not be called')
430
+ })
431
+
432
+ fastify.addHook('onSend', async (req, reply, payload) => {
433
+ t.assert.strictEqual(payload, 'hello')
434
+ })
435
+
436
+ fastify.addHook('onResponse', async (request, reply) => {
437
+ t.assert.ok('called')
438
+ })
439
+
440
+ fastify.get('/', function (request, reply) {
441
+ t.assert.fail('we should not be here')
442
+ })
443
+
444
+ fastify.inject({
445
+ url: '/',
446
+ method: 'GET'
447
+ }, (err, res) => {
448
+ t.assert.ifError(err)
449
+ t.assert.strictEqual(res.statusCode, 200)
450
+ t.assert.strictEqual(res.payload, 'hello')
451
+ testDone()
452
+ })
453
+ })
454
+
455
+ test('preValidation hooks should be able to change request body before validation', (t, testDone) => {
456
+ t.plan(4)
457
+ const fastify = Fastify()
458
+
459
+ fastify.addHook('preValidation', async (req, _reply) => {
460
+ const buff = Buffer.from(req.body.message, 'base64')
461
+ req.body = JSON.parse(buff.toString('utf-8'))
462
+ t.assert.ok('has been called')
463
+ })
464
+
465
+ fastify.post(
466
+ '/',
467
+ {
468
+ schema: {
469
+ body: {
470
+ type: 'object',
471
+ properties: {
472
+ foo: {
473
+ type: 'string'
474
+ },
475
+ bar: {
476
+ type: 'number'
477
+ }
478
+ },
479
+ required: ['foo', 'bar']
480
+ }
481
+ }
482
+ },
483
+ (req, reply) => {
484
+ reply.status(200).send('hello')
485
+ }
486
+ )
487
+
488
+ fastify.inject({
489
+ url: '/',
490
+ method: 'POST',
491
+ payload: {
492
+ message: Buffer.from(JSON.stringify({ foo: 'example', bar: 1 })).toString('base64')
493
+ }
494
+ }, (err, res) => {
495
+ t.assert.ifError(err)
496
+ t.assert.strictEqual(res.statusCode, 200)
497
+ t.assert.strictEqual(res.payload, 'hello')
498
+ testDone()
499
+ })
500
+ })
501
+
502
+ test('preSerialization hooks should be able to modify the payload', (t, testDone) => {
503
+ t.plan(3)
504
+ const fastify = Fastify()
505
+
506
+ fastify.addHook('preSerialization', async (req, reply, payload) => {
507
+ return { hello: 'another world' }
508
+ })
509
+
510
+ fastify.get('/', function (request, reply) {
511
+ reply.send({ hello: 'world' })
512
+ })
513
+
514
+ fastify.inject({
515
+ url: '/',
516
+ method: 'GET'
517
+ }, (err, res) => {
518
+ t.assert.ifError(err)
519
+ t.assert.strictEqual(res.statusCode, 200)
520
+ t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'another world' })
521
+ testDone()
522
+ })
523
+ })
524
+
525
+ test('preSerialization hooks should handle errors', (t, testDone) => {
526
+ t.plan(3)
527
+ const fastify = Fastify()
528
+
529
+ fastify.addHook('preSerialization', async (req, reply, payload) => {
530
+ throw new Error('kaboom')
531
+ })
532
+
533
+ fastify.get('/', function (request, reply) {
534
+ reply.send({ hello: 'world' })
535
+ })
536
+
537
+ fastify.inject({
538
+ url: '/',
539
+ method: 'GET'
540
+ }, (err, res) => {
541
+ t.assert.ifError(err)
542
+ t.assert.strictEqual(res.statusCode, 500)
543
+ t.assert.deepStrictEqual(JSON.parse(res.payload), { error: 'Internal Server Error', message: 'kaboom', statusCode: 500 })
544
+ testDone()
545
+ })
546
+ })
547
+
548
+ test('preValidation hooks should handle throwing null', (t, testDone) => {
549
+ t.plan(4)
550
+ const fastify = Fastify()
551
+
552
+ fastify.setErrorHandler(async (error, request, reply) => {
553
+ t.assert.ok(error instanceof Error)
554
+ await reply.send(error)
555
+ })
556
+
557
+ fastify.addHook('preValidation', async () => {
558
+ // eslint-disable-next-line no-throw-literal
559
+ throw null
560
+ })
561
+
562
+ fastify.get('/', function (request, reply) { t.assert.fail('the handler must not be called') })
563
+
564
+ fastify.inject({
565
+ url: '/',
566
+ method: 'GET'
567
+ }, (err, res) => {
568
+ t.assert.ifError(err)
569
+ t.assert.strictEqual(res.statusCode, 500)
570
+ t.assert.deepStrictEqual(res.json(), {
571
+ error: 'Internal Server Error',
572
+ code: 'FST_ERR_SEND_UNDEFINED_ERR',
573
+ message: 'Undefined error has occurred',
574
+ statusCode: 500
575
+ })
576
+ testDone()
577
+ })
578
+ })
579
+
580
+ test('preValidation hooks should handle throwing a string', (t, testDone) => {
581
+ t.plan(3)
582
+ const fastify = Fastify()
583
+
584
+ fastify.addHook('preValidation', async () => {
585
+ // eslint-disable-next-line no-throw-literal
586
+ throw 'this is an error'
587
+ })
588
+
589
+ fastify.get('/', function (request, reply) { t.assert.fail('the handler must not be called') })
590
+
591
+ fastify.inject({
592
+ url: '/',
593
+ method: 'GET'
594
+ }, (err, res) => {
595
+ t.assert.ifError(err)
596
+ t.assert.strictEqual(res.statusCode, 500)
597
+ t.assert.strictEqual(res.payload, 'this is an error')
598
+ testDone()
599
+ })
600
+ })
601
+
602
+ test('onRequest hooks should be able to block a request (last hook)', (t, testDone) => {
603
+ t.plan(5)
604
+ const fastify = Fastify()
605
+
606
+ fastify.addHook('onRequest', async (req, reply) => {
607
+ await reply.send('hello')
608
+ })
609
+
610
+ fastify.addHook('preHandler', async (req, reply) => {
611
+ t.assert.fail('this should not be called')
612
+ })
613
+
614
+ fastify.addHook('onSend', async (req, reply, payload) => {
615
+ t.assert.ok('called')
616
+ })
617
+
618
+ fastify.addHook('onResponse', async (request, reply) => {
619
+ t.assert.ok('called')
620
+ })
621
+
622
+ fastify.get('/', function (request, reply) {
623
+ t.assert.fail('we should not be here')
624
+ })
625
+
626
+ fastify.inject({
627
+ url: '/',
628
+ method: 'GET'
629
+ }, (err, res) => {
630
+ t.assert.ifError(err)
631
+ t.assert.strictEqual(res.statusCode, 200)
632
+ t.assert.strictEqual(res.payload, 'hello')
633
+ testDone()
634
+ })
635
+ })
636
+
637
+ test('preHandler hooks should be able to block a request (last hook)', (t, testDone) => {
638
+ t.plan(5)
639
+ const fastify = Fastify()
640
+
641
+ fastify.addHook('preHandler', async (req, reply) => {
642
+ await reply.send('hello')
643
+ })
644
+
645
+ fastify.addHook('onSend', async (req, reply, payload) => {
646
+ t.assert.strictEqual(payload, 'hello')
647
+ })
648
+
649
+ fastify.addHook('onResponse', async (request, reply) => {
650
+ t.assert.ok('called')
651
+ })
652
+
653
+ fastify.get('/', function (request, reply) {
654
+ t.assert.fail('we should not be here')
655
+ })
656
+
657
+ fastify.inject({
658
+ url: '/',
659
+ method: 'GET'
660
+ }, (err, res) => {
661
+ t.assert.ifError(err)
662
+ t.assert.strictEqual(res.statusCode, 200)
663
+ t.assert.strictEqual(res.payload, 'hello')
664
+ testDone()
665
+ })
666
+ })
667
+
668
+ test('onRequest respond with a stream', (t, testDone) => {
669
+ t.plan(4)
670
+ const fastify = Fastify()
671
+
672
+ fastify.addHook('onRequest', async (req, reply) => {
673
+ return new Promise((resolve, reject) => {
674
+ const stream = fs.createReadStream(__filename, 'utf8')
675
+ // stream.pipe(res)
676
+ // res.once('finish', resolve)
677
+ reply.send(stream).then(() => {
678
+ reply.raw.once('finish', () => resolve())
679
+ })
680
+ })
681
+ })
682
+
683
+ fastify.addHook('onRequest', async (req, res) => {
684
+ t.assert.fail('this should not be called')
685
+ })
686
+
687
+ fastify.addHook('preHandler', async (req, reply) => {
688
+ t.assert.fail('this should not be called')
689
+ })
690
+
691
+ fastify.addHook('onSend', async (req, reply, payload) => {
692
+ t.assert.ok('called')
693
+ })
694
+
695
+ fastify.addHook('onResponse', async (request, reply) => {
696
+ t.assert.ok('called')
697
+ })
698
+
699
+ fastify.get('/', function (request, reply) {
700
+ t.assert.fail('we should not be here')
701
+ })
702
+
703
+ fastify.inject({
704
+ url: '/',
705
+ method: 'GET'
706
+ }, (err, res) => {
707
+ t.assert.ifError(err)
708
+ t.assert.strictEqual(res.statusCode, 200)
709
+ testDone()
710
+ })
711
+ })
712
+
713
+ test('preHandler respond with a stream', (t, testDone) => {
714
+ t.plan(7)
715
+ const fastify = Fastify()
716
+
717
+ fastify.addHook('onRequest', async (req, res) => {
718
+ t.assert.ok('called')
719
+ })
720
+
721
+ // we are calling `reply.send` inside the `preHandler` hook with a stream,
722
+ // this triggers the `onSend` hook event if `preHandler` has not yet finished
723
+ const order = [1, 2]
724
+
725
+ fastify.addHook('preHandler', async (req, reply) => {
726
+ const stream = fs.createReadStream(__filename, 'utf8')
727
+ reply.raw.once('finish', () => {
728
+ t.assert.strictEqual(order.shift(), 2)
729
+ })
730
+ return reply.send(stream)
731
+ })
732
+
733
+ fastify.addHook('preHandler', async (req, reply) => {
734
+ t.assert.fail('this should not be called')
735
+ })
736
+
737
+ fastify.addHook('onSend', async (req, reply, payload) => {
738
+ t.assert.strictEqual(order.shift(), 1)
739
+ t.assert.strictEqual(typeof payload.pipe, 'function')
740
+ })
741
+
742
+ fastify.addHook('onResponse', async (request, reply) => {
743
+ t.assert.ok('called')
744
+ })
745
+
746
+ fastify.get('/', function (request, reply) {
747
+ t.assert.fail('we should not be here')
748
+ })
749
+
750
+ fastify.inject({
751
+ url: '/',
752
+ method: 'GET'
753
+ }, (err, res) => {
754
+ t.assert.ifError(err)
755
+ t.assert.strictEqual(res.statusCode, 200)
756
+ testDone()
757
+ })
758
+ })
759
+
760
+ describe('Should log a warning if is an async function with `done`', () => {
761
+ test('2 arguments', t => {
762
+ const fastify = Fastify()
763
+
764
+ try {
765
+ fastify.addHook('onRequestAbort', async (req, done) => {
766
+ t.assert.fail('should have not be called')
767
+ })
768
+ } catch (e) {
769
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
770
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
771
+ }
772
+ })
773
+
774
+ test('3 arguments', t => {
775
+ const fastify = Fastify()
776
+
777
+ try {
778
+ fastify.addHook('onRequest', async (req, reply, done) => {
779
+ t.assert.fail('should have not be called')
780
+ })
781
+ } catch (e) {
782
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
783
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
784
+ }
785
+ })
786
+
787
+ test('4 arguments', t => {
788
+ const fastify = Fastify()
789
+
790
+ try {
791
+ fastify.addHook('onSend', async (req, reply, payload, done) => {
792
+ t.assert.fail('should have not be called')
793
+ })
794
+ } catch (e) {
795
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
796
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
797
+ }
798
+ try {
799
+ fastify.addHook('preSerialization', async (req, reply, payload, done) => {
800
+ t.assert.fail('should have not be called')
801
+ })
802
+ } catch (e) {
803
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
804
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
805
+ }
806
+ try {
807
+ fastify.addHook('onError', async (req, reply, payload, done) => {
808
+ t.assert.fail('should have not be called')
809
+ })
810
+ } catch (e) {
811
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
812
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
813
+ }
814
+ })
815
+ })
816
+
817
+ test('early termination, onRequest async', async t => {
818
+ const app = Fastify()
819
+
820
+ app.addHook('onRequest', async (req, reply) => {
821
+ setImmediate(() => reply.send('hello world'))
822
+ return reply
823
+ })
824
+
825
+ app.get('/', (req, reply) => {
826
+ t.assert.fail('should not happen')
827
+ })
828
+
829
+ const res = await app.inject('/')
830
+ t.assert.strictEqual(res.statusCode, 200)
831
+ t.assert.strictEqual(res.body.toString(), 'hello world')
832
+ })
833
+
834
+ test('The this should be the same of the encapsulation level', async t => {
835
+ const fastify = Fastify()
836
+
837
+ fastify.addHook('onRequest', async function (req, reply) {
838
+ if (req.raw.url === '/nested') {
839
+ t.assert.strictEqual(this.foo, 'bar')
840
+ } else {
841
+ t.assert.strictEqual(this.foo, undefined)
842
+ }
843
+ })
844
+
845
+ fastify.register(plugin)
846
+ fastify.get('/', (req, reply) => reply.send('ok'))
847
+
848
+ async function plugin (fastify, opts) {
849
+ fastify.decorate('foo', 'bar')
850
+ fastify.get('/nested', (req, reply) => reply.send('ok'))
851
+ }
852
+
853
+ await fastify.inject({ method: 'GET', path: '/' })
854
+ await fastify.inject({ method: 'GET', path: '/nested' })
855
+ await fastify.inject({ method: 'GET', path: '/' })
856
+ await fastify.inject({ method: 'GET', path: '/nested' })
857
+ })
858
+
859
+ describe('preSerializationEnd should handle errors if the serialize method throws', () => {
860
+ test('works with sync preSerialization', (t, testDone) => {
861
+ t.plan(3)
862
+ const fastify = Fastify()
863
+
864
+ fastify.addHook('preSerialization', (request, reply, payload, done) => {
865
+ t.assert.ok('called')
866
+ done(null, payload)
867
+ })
868
+
869
+ fastify.post('/', {
870
+ handler (req, reply) { reply.send({ notOk: true }) },
871
+ schema: { response: { 200: { required: ['ok'], properties: { ok: { type: 'boolean' } } } } }
872
+ })
873
+
874
+ fastify.inject({
875
+ method: 'POST',
876
+ url: '/'
877
+ }, (err, res) => {
878
+ t.assert.ifError(err)
879
+ t.assert.notEqual(res.statusCode, 200)
880
+ testDone()
881
+ })
882
+ })
883
+
884
+ test('works with async preSerialization', (t, testDone) => {
885
+ t.plan(3)
886
+ const fastify = Fastify()
887
+
888
+ fastify.addHook('preSerialization', async (request, reply, payload) => {
889
+ t.assert.ok('called')
890
+ return payload
891
+ })
892
+
893
+ fastify.post('/', {
894
+ handler (req, reply) { reply.send({ notOk: true }) },
895
+ schema: { response: { 200: { required: ['ok'], properties: { ok: { type: 'boolean' } } } } }
896
+ })
897
+
898
+ fastify.inject({
899
+ method: 'POST',
900
+ url: '/'
901
+ }, (err, res) => {
902
+ t.assert.ifError(err)
903
+ t.assert.notEqual(res.statusCode, 200)
904
+ testDone()
905
+ })
906
+ })
907
+ })
908
+
909
+ test('nested hooks to do not crash on 404', (t, testDone) => {
910
+ t.plan(3)
911
+ const fastify = Fastify()
912
+
913
+ fastify.get('/hello', (req, reply) => {
914
+ reply.send({ hello: 'world' })
915
+ })
916
+
917
+ fastify.register(async function (fastify) {
918
+ fastify.get('/something', (req, reply) => {
919
+ reply.callNotFound()
920
+ })
921
+
922
+ fastify.setNotFoundHandler(async (request, reply) => {
923
+ t.assert.ok('called')
924
+ reply.statusCode = 404
925
+ return { status: 'nested-not-found' }
926
+ })
927
+
928
+ fastify.setErrorHandler(async (error, request, reply) => {
929
+ t.assert.fail('should have not be called')
930
+ reply.statusCode = 500
931
+ return { status: 'nested-error', error }
932
+ })
933
+ }, { prefix: '/nested' })
934
+
935
+ fastify.setNotFoundHandler(async (request, reply) => {
936
+ t.assert.fail('should have not be called')
937
+ reply.statusCode = 404
938
+ return { status: 'not-found' }
939
+ })
940
+
941
+ fastify.setErrorHandler(async (error, request, reply) => {
942
+ t.assert.fail('should have not be called')
943
+ reply.statusCode = 500
944
+ return { status: 'error', error }
945
+ })
946
+
947
+ fastify.inject({
948
+ method: 'GET',
949
+ url: '/nested/something'
950
+ }, (err, res) => {
951
+ t.assert.ifError(err)
952
+ t.assert.strictEqual(res.statusCode, 404)
953
+ testDone()
954
+ })
955
+ })
956
+
957
+ test('Register an hook (preHandler) as route option should fail if mixing async and callback style', t => {
958
+ const fastify = Fastify()
959
+
960
+ try {
961
+ fastify.get(
962
+ '/',
963
+ {
964
+ preHandler: [
965
+ async (request, reply, done) => {
966
+ done()
967
+ }
968
+ ]
969
+ },
970
+ async (request, reply) => {
971
+ return { hello: 'world' }
972
+ }
973
+ )
974
+ t.assert.fail('preHandler mixing async and callback style')
975
+ } catch (e) {
976
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
977
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
978
+ }
979
+ })
980
+
981
+ test('Register an hook (onSend) as route option should fail if mixing async and callback style', t => {
982
+ const fastify = Fastify()
983
+
984
+ try {
985
+ fastify.get(
986
+ '/',
987
+ {
988
+ onSend: [
989
+ async (request, reply, payload, done) => {
990
+ done()
991
+ }
992
+ ]
993
+ },
994
+ async (request, reply) => {
995
+ return { hello: 'world' }
996
+ }
997
+ )
998
+ t.assert.fail('onSend mixing async and callback style')
999
+ } catch (e) {
1000
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
1001
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
1002
+ }
1003
+ })
1004
+
1005
+ test('Register an hook (preSerialization) as route option should fail if mixing async and callback style', t => {
1006
+ const fastify = Fastify()
1007
+
1008
+ try {
1009
+ fastify.get(
1010
+ '/',
1011
+ {
1012
+ preSerialization: [
1013
+ async (request, reply, payload, done) => {
1014
+ done()
1015
+ }
1016
+ ]
1017
+ },
1018
+ async (request, reply) => {
1019
+ return { hello: 'world' }
1020
+ }
1021
+ )
1022
+ t.assert.fail('preSerialization mixing async and callback style')
1023
+ } catch (e) {
1024
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
1025
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
1026
+ }
1027
+ })
1028
+
1029
+ test('Register an hook (onError) as route option should fail if mixing async and callback style', t => {
1030
+ const fastify = Fastify()
1031
+
1032
+ try {
1033
+ fastify.get(
1034
+ '/',
1035
+ {
1036
+ onError: [
1037
+ async (request, reply, error, done) => {
1038
+ done()
1039
+ }
1040
+ ]
1041
+ },
1042
+ async (request, reply) => {
1043
+ return { hello: 'world' }
1044
+ }
1045
+ )
1046
+ t.assert.fail('onError mixing async and callback style')
1047
+ } catch (e) {
1048
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
1049
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
1050
+ }
1051
+ })
1052
+
1053
+ test('Register an hook (preParsing) as route option should fail if mixing async and callback style', t => {
1054
+ const fastify = Fastify()
1055
+
1056
+ try {
1057
+ fastify.get(
1058
+ '/',
1059
+ {
1060
+ preParsing: [
1061
+ async (request, reply, payload, done) => {
1062
+ done()
1063
+ }
1064
+ ]
1065
+ },
1066
+ async (request, reply) => {
1067
+ return { hello: 'world' }
1068
+ }
1069
+ )
1070
+ t.assert.fail('preParsing mixing async and callback style')
1071
+ } catch (e) {
1072
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
1073
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
1074
+ }
1075
+ })
1076
+
1077
+ test('Register an hook (onRequestAbort) as route option should fail if mixing async and callback style', (t) => {
1078
+ const fastify = Fastify()
1079
+
1080
+ try {
1081
+ fastify.get(
1082
+ '/',
1083
+ {
1084
+ onRequestAbort: [
1085
+ async (request, done) => {
1086
+ done()
1087
+ }
1088
+ ]
1089
+ },
1090
+ async (request, reply) => {
1091
+ return { hello: 'world' }
1092
+ }
1093
+ )
1094
+ t.assert.fail('onRequestAbort mixing async and callback style')
1095
+ } catch (e) {
1096
+ t.assert.strictEqual(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER')
1097
+ t.assert.strictEqual(e.message, 'Async function has too many arguments. Async hooks should not use the \'done\' argument.')
1098
+ }
1099
+ })