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,270 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const handleRequest = require('../../lib/handle-request')
5
+ const internals = require('../../lib/handle-request')[Symbol.for('internals')]
6
+ const Request = require('../../lib/request')
7
+ const Reply = require('../../lib/reply')
8
+ const { kRouteContext } = require('../../lib/symbols')
9
+ const buildSchema = require('../../lib/validation').compileSchemasForValidation
10
+
11
+ const Ajv = require('ajv')
12
+ const ajv = new Ajv({ coerceTypes: true })
13
+
14
+ function schemaValidator ({ schema, method, url, httpPart }) {
15
+ const validateFunction = ajv.compile(schema)
16
+ const fn = function (body) {
17
+ const isOk = validateFunction(body)
18
+ if (isOk) return
19
+ return false
20
+ }
21
+ fn.errors = []
22
+ return fn
23
+ }
24
+
25
+ test('handleRequest function - sent reply', t => {
26
+ t.plan(1)
27
+ const request = {}
28
+ const reply = { sent: true }
29
+ const res = handleRequest(null, request, reply)
30
+ t.assert.strictEqual(res, undefined)
31
+ })
32
+
33
+ test('handleRequest function - invoke with error', t => {
34
+ t.plan(1)
35
+ const request = {}
36
+ const reply = {}
37
+ reply.send = (err) => t.assert.strictEqual(err.message, 'Kaboom')
38
+ handleRequest(new Error('Kaboom'), request, reply)
39
+ })
40
+
41
+ test('handler function - invalid schema', t => {
42
+ t.plan(1)
43
+ const res = {}
44
+ res.log = { error: () => {}, info: () => {} }
45
+ const context = {
46
+ config: {
47
+ method: 'GET',
48
+ url: '/an-url'
49
+ },
50
+ schema: {
51
+ body: {
52
+ type: 'object',
53
+ properties: {
54
+ hello: { type: 'number' }
55
+ }
56
+ }
57
+ },
58
+ errorHandler: { func: () => { t.assert.ok('errorHandler called') } },
59
+ handler: () => {},
60
+ Reply,
61
+ Request,
62
+ preValidation: [],
63
+ preHandler: [],
64
+ onSend: [],
65
+ onError: [],
66
+ attachValidation: false,
67
+ schemaErrorFormatter: () => new Error()
68
+ }
69
+ buildSchema(context, schemaValidator)
70
+ const request = {
71
+ body: { hello: 'world' },
72
+ [kRouteContext]: context
73
+ }
74
+ internals.handler(request, new Reply(res, request))
75
+ })
76
+
77
+ test('handler function - reply', t => {
78
+ t.plan(3)
79
+ const res = {}
80
+ res.end = () => {
81
+ t.assert.strictEqual(res.statusCode, 204)
82
+ t.assert.ok(true)
83
+ }
84
+ res.writeHead = () => {}
85
+ const context = {
86
+ handler: (req, reply) => {
87
+ t.assert.strictEqual(typeof reply, 'object')
88
+ reply.code(204)
89
+ reply.send(undefined)
90
+ },
91
+ Reply,
92
+ Request,
93
+ preValidation: [],
94
+ preHandler: [],
95
+ onSend: [],
96
+ onError: [],
97
+ config: {
98
+ url: '',
99
+ method: ''
100
+ }
101
+ }
102
+ buildSchema(context, schemaValidator)
103
+ internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
104
+ })
105
+
106
+ test('handler function - preValidationCallback with finished response', t => {
107
+ t.plan(0)
108
+ const res = {}
109
+ // Be sure to check only `writableEnded` where is available
110
+ res.writableEnded = true
111
+ res.end = () => {
112
+ t.assert.fail()
113
+ }
114
+ res.writeHead = () => {}
115
+ const context = {
116
+ handler: (req, reply) => {
117
+ t.assert.fail()
118
+ reply.send(undefined)
119
+ },
120
+ Reply,
121
+ Request,
122
+ preValidation: null,
123
+ preHandler: [],
124
+ onSend: [],
125
+ onError: []
126
+ }
127
+ buildSchema(context, schemaValidator)
128
+ internals.handler({ [kRouteContext]: context }, new Reply(res, { [kRouteContext]: context }))
129
+ })
130
+
131
+ test('request should be defined in onSend Hook on post request with content type application/json', async t => {
132
+ t.plan(6)
133
+ const fastify = require('../..')()
134
+
135
+ t.after(() => {
136
+ fastify.close()
137
+ })
138
+
139
+ fastify.addHook('onSend', (request, reply, payload, done) => {
140
+ t.assert.ok(request)
141
+ t.assert.ok(request.raw)
142
+ t.assert.ok(request.id)
143
+ t.assert.ok(request.params)
144
+ t.assert.ok(request.query)
145
+ done()
146
+ })
147
+ fastify.post('/', (request, reply) => {
148
+ reply.send(200)
149
+ })
150
+
151
+ const fastifyServer = await fastify.listen({ port: 0 })
152
+ const result = await fetch(fastifyServer, {
153
+ method: 'POST',
154
+ headers: {
155
+ 'content-type': 'application/json'
156
+ }
157
+ })
158
+
159
+ t.assert.strictEqual(result.status, 400)
160
+ })
161
+
162
+ test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', async t => {
163
+ t.plan(5)
164
+ const fastify = require('../..')()
165
+
166
+ t.after(() => {
167
+ fastify.close()
168
+ })
169
+
170
+ fastify.addHook('onSend', (request, reply, payload, done) => {
171
+ t.assert.ok(request)
172
+ t.assert.ok(request.raw)
173
+ t.assert.ok(request.params)
174
+ t.assert.ok(request.query)
175
+ done()
176
+ })
177
+ fastify.post('/', (request, reply) => {
178
+ reply.send(200)
179
+ })
180
+
181
+ const fastifyServer = await fastify.listen({ port: 0 })
182
+ const result = await fetch(fastifyServer, {
183
+ method: 'POST',
184
+ headers: {
185
+ 'content-type': 'application/x-www-form-urlencoded'
186
+ }
187
+ })
188
+
189
+ // a 415 error is expected because of missing content type parser
190
+ t.assert.strictEqual(result.status, 415)
191
+ })
192
+
193
+ test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', async t => {
194
+ t.plan(15)
195
+ const fastify = require('../..')()
196
+
197
+ t.after(() => {
198
+ fastify.close()
199
+ })
200
+
201
+ fastify.addHook('onSend', (request, reply, payload, done) => {
202
+ t.assert.ok(request)
203
+ t.assert.ok(request.raw)
204
+ t.assert.ok(request.params)
205
+ t.assert.ok(request.query)
206
+ done()
207
+ })
208
+ fastify.options('/', (request, reply) => {
209
+ reply.send(200)
210
+ })
211
+
212
+ // Test 1: OPTIONS with body and content-type header
213
+ const result1 = await fastify.inject({
214
+ method: 'OPTIONS',
215
+ url: '/',
216
+ body: 'first-name=OPTIONS&last-name=METHOD',
217
+ headers: {
218
+ 'content-type': 'application/x-www-form-urlencoded'
219
+ }
220
+ })
221
+
222
+ // Content-Type is not supported
223
+ t.assert.strictEqual(result1.statusCode, 415)
224
+
225
+ // Test 2: OPTIONS with content-type header only (no body)
226
+ const result2 = await fastify.inject({
227
+ method: 'OPTIONS',
228
+ url: '/',
229
+ headers: {
230
+ 'content-type': 'application/x-www-form-urlencoded'
231
+ }
232
+ })
233
+
234
+ // Content-Type is not supported
235
+ t.assert.strictEqual(result2.statusCode, 415)
236
+
237
+ // Test 3: OPTIONS with body but no content-type header
238
+ const result3 = await fastify.inject({
239
+ method: 'OPTIONS',
240
+ url: '/',
241
+ body: 'first-name=OPTIONS&last-name=METHOD'
242
+ })
243
+
244
+ // No content-type with payload
245
+ t.assert.strictEqual(result3.statusCode, 415)
246
+ })
247
+
248
+ test('request should respond with an error if an unserialized payload is sent inside an async handler', async t => {
249
+ t.plan(2)
250
+
251
+ const fastify = require('../..')()
252
+
253
+ fastify.get('/', (request, reply) => {
254
+ reply.type('text/html')
255
+ return Promise.resolve(request.headers)
256
+ })
257
+
258
+ const res = await fastify.inject({
259
+ method: 'GET',
260
+ url: '/'
261
+ })
262
+
263
+ t.assert.strictEqual(res.statusCode, 500)
264
+ t.assert.deepStrictEqual(JSON.parse(res.payload), {
265
+ error: 'Internal Server Error',
266
+ code: 'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
267
+ message: 'Attempted to send payload of invalid type \'object\'. Expected a string or Buffer.',
268
+ statusCode: 500
269
+ })
270
+ })
@@ -0,0 +1,449 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const { hookRunnerGenerator, onSendHookRunner } = require('../../lib/hooks')
5
+
6
+ test('hookRunner - Basic', t => {
7
+ t.plan(9)
8
+
9
+ const hookRunner = hookRunnerGenerator(iterator)
10
+
11
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
12
+
13
+ function iterator (fn, a, b, done) {
14
+ return fn(a, b, done)
15
+ }
16
+
17
+ function fn1 (a, b, done) {
18
+ t.assert.strictEqual(a, 'a')
19
+ t.assert.strictEqual(b, 'b')
20
+ done()
21
+ }
22
+
23
+ function fn2 (a, b, done) {
24
+ t.assert.strictEqual(a, 'a')
25
+ t.assert.strictEqual(b, 'b')
26
+ done()
27
+ }
28
+
29
+ function fn3 (a, b, done) {
30
+ t.assert.strictEqual(a, 'a')
31
+ t.assert.strictEqual(b, 'b')
32
+ done()
33
+ }
34
+
35
+ function done (err, a, b) {
36
+ t.assert.ifError(err)
37
+ t.assert.strictEqual(a, 'a')
38
+ t.assert.strictEqual(b, 'b')
39
+ }
40
+ })
41
+
42
+ test('hookRunner - In case of error should skip to done', t => {
43
+ t.plan(7)
44
+
45
+ const hookRunner = hookRunnerGenerator(iterator)
46
+
47
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
48
+
49
+ function iterator (fn, a, b, done) {
50
+ return fn(a, b, done)
51
+ }
52
+
53
+ function fn1 (a, b, done) {
54
+ t.assert.strictEqual(a, 'a')
55
+ t.assert.strictEqual(b, 'b')
56
+ done()
57
+ }
58
+
59
+ function fn2 (a, b, done) {
60
+ t.assert.strictEqual(a, 'a')
61
+ t.assert.strictEqual(b, 'b')
62
+ done(new Error('kaboom'))
63
+ }
64
+
65
+ function fn3 () {
66
+ t.assert.fail('We should not be here')
67
+ }
68
+
69
+ function done (err, a, b) {
70
+ t.assert.strictEqual(err.message, 'kaboom')
71
+ t.assert.strictEqual(a, 'a')
72
+ t.assert.strictEqual(b, 'b')
73
+ }
74
+ })
75
+
76
+ test('hookRunner - Should handle throw', t => {
77
+ t.plan(7)
78
+
79
+ const hookRunner = hookRunnerGenerator(iterator)
80
+
81
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
82
+
83
+ function iterator (fn, a, b, done) {
84
+ return fn(a, b, done)
85
+ }
86
+
87
+ function fn1 (a, b, done) {
88
+ t.assert.strictEqual(a, 'a')
89
+ t.assert.strictEqual(b, 'b')
90
+ done()
91
+ }
92
+
93
+ function fn2 (a, b, done) {
94
+ t.assert.strictEqual(a, 'a')
95
+ t.assert.strictEqual(b, 'b')
96
+ throw new Error('kaboom')
97
+ }
98
+
99
+ function fn3 () {
100
+ t.assert.fail('We should not be here')
101
+ }
102
+
103
+ function done (err, a, b) {
104
+ t.assert.strictEqual(err.message, 'kaboom')
105
+ t.assert.strictEqual(a, 'a')
106
+ t.assert.strictEqual(b, 'b')
107
+ }
108
+ })
109
+
110
+ test('hookRunner - Should handle promises', t => {
111
+ t.plan(9)
112
+
113
+ const hookRunner = hookRunnerGenerator(iterator)
114
+
115
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
116
+
117
+ function iterator (fn, a, b, done) {
118
+ return fn(a, b, done)
119
+ }
120
+
121
+ function fn1 (a, b) {
122
+ t.assert.strictEqual(a, 'a')
123
+ t.assert.strictEqual(b, 'b')
124
+ return Promise.resolve()
125
+ }
126
+
127
+ function fn2 (a, b) {
128
+ t.assert.strictEqual(a, 'a')
129
+ t.assert.strictEqual(b, 'b')
130
+ return Promise.resolve()
131
+ }
132
+
133
+ function fn3 (a, b) {
134
+ t.assert.strictEqual(a, 'a')
135
+ t.assert.strictEqual(b, 'b')
136
+ return Promise.resolve()
137
+ }
138
+
139
+ function done (err, a, b) {
140
+ t.assert.ifError(err)
141
+ t.assert.strictEqual(a, 'a')
142
+ t.assert.strictEqual(b, 'b')
143
+ }
144
+ })
145
+
146
+ test('hookRunner - In case of error should skip to done (with promises)', t => {
147
+ t.plan(7)
148
+
149
+ const hookRunner = hookRunnerGenerator(iterator)
150
+
151
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
152
+
153
+ function iterator (fn, a, b, done) {
154
+ return fn(a, b, done)
155
+ }
156
+
157
+ function fn1 (a, b) {
158
+ t.assert.strictEqual(a, 'a')
159
+ t.assert.strictEqual(b, 'b')
160
+ return Promise.resolve()
161
+ }
162
+
163
+ function fn2 (a, b) {
164
+ t.assert.strictEqual(a, 'a')
165
+ t.assert.strictEqual(b, 'b')
166
+ return Promise.reject(new Error('kaboom'))
167
+ }
168
+
169
+ function fn3 () {
170
+ t.assert.fail('We should not be here')
171
+ }
172
+
173
+ function done (err, a, b) {
174
+ t.assert.strictEqual(err.message, 'kaboom')
175
+ t.assert.strictEqual(a, 'a')
176
+ t.assert.strictEqual(b, 'b')
177
+ }
178
+ })
179
+
180
+ test('hookRunner - Be able to exit before its natural end', t => {
181
+ t.plan(4)
182
+
183
+ const hookRunner = hookRunnerGenerator(iterator)
184
+
185
+ let shouldStop = false
186
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
187
+
188
+ function iterator (fn, a, b, done) {
189
+ if (shouldStop) {
190
+ return undefined
191
+ }
192
+ return fn(a, b, done)
193
+ }
194
+
195
+ function fn1 (a, b, done) {
196
+ t.assert.strictEqual(a, 'a')
197
+ t.assert.strictEqual(b, 'b')
198
+ done()
199
+ }
200
+
201
+ function fn2 (a, b) {
202
+ t.assert.strictEqual(a, 'a')
203
+ t.assert.strictEqual(b, 'b')
204
+ shouldStop = true
205
+ return Promise.resolve()
206
+ }
207
+
208
+ function fn3 () {
209
+ t.assert.fail('this should not be called')
210
+ }
211
+
212
+ function done () {
213
+ t.assert.fail('this should not be called')
214
+ }
215
+ })
216
+
217
+ test('hookRunner - Promises that resolve to a value do not change the state', t => {
218
+ t.plan(5)
219
+
220
+ const originalState = { a: 'a', b: 'b' }
221
+
222
+ const hookRunner = hookRunnerGenerator(iterator)
223
+
224
+ hookRunner([fn1, fn2, fn3], originalState, 'b', done)
225
+
226
+ function iterator (fn, state, b, done) {
227
+ return fn(state, b, done)
228
+ }
229
+
230
+ function fn1 (state, b, done) {
231
+ t.assert.strictEqual(state, originalState)
232
+ return Promise.resolve(null)
233
+ }
234
+
235
+ function fn2 (state, b, done) {
236
+ t.assert.strictEqual(state, originalState)
237
+ return Promise.resolve('string')
238
+ }
239
+
240
+ function fn3 (state, b, done) {
241
+ t.assert.strictEqual(state, originalState)
242
+ return Promise.resolve({ object: true })
243
+ }
244
+
245
+ function done (err, state, b) {
246
+ t.assert.ifError(err)
247
+ t.assert.strictEqual(state, originalState)
248
+ }
249
+ })
250
+
251
+ test('onSendHookRunner - Basic', t => {
252
+ t.plan(13)
253
+
254
+ const originalRequest = { body: null }
255
+ const originalReply = { request: originalRequest }
256
+ const originalPayload = 'payload'
257
+
258
+ onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, originalPayload, done)
259
+
260
+ function fn1 (request, reply, payload, done) {
261
+ t.assert.deepStrictEqual(request, originalRequest)
262
+ t.assert.deepStrictEqual(reply, originalReply)
263
+ t.assert.strictEqual(payload, originalPayload)
264
+ done()
265
+ }
266
+
267
+ function fn2 (request, reply, payload, done) {
268
+ t.assert.deepStrictEqual(request, originalRequest)
269
+ t.assert.deepStrictEqual(reply, originalReply)
270
+ t.assert.strictEqual(payload, originalPayload)
271
+ done()
272
+ }
273
+
274
+ function fn3 (request, reply, payload, done) {
275
+ t.assert.deepStrictEqual(request, originalRequest)
276
+ t.assert.deepStrictEqual(reply, originalReply)
277
+ t.assert.strictEqual(payload, originalPayload)
278
+ done()
279
+ }
280
+
281
+ function done (err, request, reply, payload) {
282
+ t.assert.ifError(err)
283
+ t.assert.deepStrictEqual(request, originalRequest)
284
+ t.assert.deepStrictEqual(reply, originalReply)
285
+ t.assert.strictEqual(payload, originalPayload)
286
+ }
287
+ })
288
+
289
+ test('onSendHookRunner - Can change the payload', t => {
290
+ t.plan(7)
291
+
292
+ const originalRequest = { body: null }
293
+ const originalReply = { request: originalRequest }
294
+ const v1 = { hello: 'world' }
295
+ const v2 = { ciao: 'mondo' }
296
+ const v3 = { winter: 'is coming' }
297
+ const v4 = { winter: 'has come' }
298
+
299
+ onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
300
+
301
+ function fn1 (request, reply, payload, done) {
302
+ t.assert.deepStrictEqual(payload, v1)
303
+ done(null, v2)
304
+ }
305
+
306
+ function fn2 (request, reply, payload, done) {
307
+ t.assert.deepStrictEqual(payload, v2)
308
+ done(null, v3)
309
+ }
310
+
311
+ function fn3 (request, reply, payload, done) {
312
+ t.assert.deepStrictEqual(payload, v3)
313
+ done(null, v4)
314
+ }
315
+
316
+ function done (err, request, reply, payload) {
317
+ t.assert.ifError(err)
318
+ t.assert.deepStrictEqual(request, originalRequest)
319
+ t.assert.deepStrictEqual(reply, originalReply)
320
+ t.assert.deepStrictEqual(payload, v4)
321
+ }
322
+ })
323
+
324
+ test('onSendHookRunner - In case of error should skip to done', t => {
325
+ t.plan(6)
326
+
327
+ const originalRequest = { body: null }
328
+ const originalReply = { request: originalRequest }
329
+ const v1 = { hello: 'world' }
330
+ const v2 = { ciao: 'mondo' }
331
+
332
+ onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
333
+
334
+ function fn1 (request, reply, payload, done) {
335
+ t.assert.deepStrictEqual(payload, v1)
336
+ done(null, v2)
337
+ }
338
+
339
+ function fn2 (request, reply, payload, done) {
340
+ t.assert.deepStrictEqual(payload, v2)
341
+ done(new Error('kaboom'))
342
+ }
343
+
344
+ function fn3 () {
345
+ t.assert.fail('We should not be here')
346
+ }
347
+
348
+ function done (err, request, reply, payload) {
349
+ t.assert.strictEqual(err.message, 'kaboom')
350
+ t.assert.deepStrictEqual(request, originalRequest)
351
+ t.assert.deepStrictEqual(reply, originalReply)
352
+ t.assert.deepStrictEqual(payload, v2)
353
+ }
354
+ })
355
+
356
+ test('onSendHookRunner - Should handle promises', t => {
357
+ t.plan(7)
358
+
359
+ const originalRequest = { body: null }
360
+ const originalReply = { request: originalRequest }
361
+ const v1 = { hello: 'world' }
362
+ const v2 = { ciao: 'mondo' }
363
+ const v3 = { winter: 'is coming' }
364
+ const v4 = { winter: 'has come' }
365
+
366
+ onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
367
+
368
+ function fn1 (request, reply, payload) {
369
+ t.assert.deepStrictEqual(payload, v1)
370
+ return Promise.resolve(v2)
371
+ }
372
+
373
+ function fn2 (request, reply, payload) {
374
+ t.assert.deepStrictEqual(payload, v2)
375
+ return Promise.resolve(v3)
376
+ }
377
+
378
+ function fn3 (request, reply, payload) {
379
+ t.assert.deepStrictEqual(payload, v3)
380
+ return Promise.resolve(v4)
381
+ }
382
+
383
+ function done (err, request, reply, payload) {
384
+ t.assert.ifError(err)
385
+ t.assert.deepStrictEqual(request, originalRequest)
386
+ t.assert.deepStrictEqual(reply, originalReply)
387
+ t.assert.deepStrictEqual(payload, v4)
388
+ }
389
+ })
390
+
391
+ test('onSendHookRunner - In case of error should skip to done (with promises)', t => {
392
+ t.plan(6)
393
+
394
+ const originalRequest = { body: null }
395
+ const originalReply = { request: originalRequest }
396
+ const v1 = { hello: 'world' }
397
+ const v2 = { ciao: 'mondo' }
398
+
399
+ onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
400
+
401
+ function fn1 (request, reply, payload) {
402
+ t.assert.deepStrictEqual(payload, v1)
403
+ return Promise.resolve(v2)
404
+ }
405
+
406
+ function fn2 (request, reply, payload) {
407
+ t.assert.deepStrictEqual(payload, v2)
408
+ return Promise.reject(new Error('kaboom'))
409
+ }
410
+
411
+ function fn3 () {
412
+ t.assert.fail('We should not be here')
413
+ }
414
+
415
+ function done (err, request, reply, payload) {
416
+ t.assert.strictEqual(err.message, 'kaboom')
417
+ t.assert.deepStrictEqual(request, originalRequest)
418
+ t.assert.deepStrictEqual(reply, originalReply)
419
+ t.assert.deepStrictEqual(payload, v2)
420
+ }
421
+ })
422
+
423
+ test('onSendHookRunner - Be able to exit before its natural end', t => {
424
+ t.plan(2)
425
+
426
+ const originalRequest = { body: null }
427
+ const originalReply = { request: originalRequest }
428
+ const v1 = { hello: 'world' }
429
+ const v2 = { ciao: 'mondo' }
430
+
431
+ onSendHookRunner([fn1, fn2, fn3], originalRequest, originalReply, v1, done)
432
+
433
+ function fn1 (request, reply, payload, done) {
434
+ t.assert.deepStrictEqual(payload, v1)
435
+ done(null, v2)
436
+ }
437
+
438
+ function fn2 (request, reply, payload) {
439
+ t.assert.deepStrictEqual(payload, v2)
440
+ }
441
+
442
+ function fn3 () {
443
+ t.assert.fail('this should not be called')
444
+ }
445
+
446
+ function done () {
447
+ t.assert.fail('this should not be called')
448
+ }
449
+ })