strong-tie/inbound-calls
0
1'use strict'2 3const { test } = require('node:test')4const Fastify = require('..')5 6// Because of how error handlers wrap things, following the control flow can be tricky7// In this test file numbered comments indicate the order statements are expected to execute8 9test('encapsulates an asynchronous error handler', async t => {10 t.plan(3)11 12 const fastify = Fastify()13 fastify.register(async function (fastify) {14 fastify.setErrorHandler(async function a (err) {15 // 3. the inner error handler catches the error, and throws a new error16 t.assert.strictEqual(err.message, 'from_endpoint')17 throw new Error('from_inner')18 })19 fastify.get('/encapsulated', async () => {20 // 2. the endpoint throws an error21 throw new Error('from_endpoint')22 })23 })24 25 fastify.setErrorHandler(async function b (err) {26 // 4. the outer error handler catches the error thrown by the inner error handler27 t.assert.strictEqual(err.message, 'from_inner')28 // 5. the outer error handler throws a new error29 throw new Error('from_outer')30 })31 32 // 1. the endpoint is called33 const res = await fastify.inject('/encapsulated')34 // 6. the default error handler returns the error from the outer error handler35 t.assert.strictEqual(res.json().message, 'from_outer')36})37 38// See discussion in https://github.com/fastify/fastify/pull/5222#discussion_r143257365539test('encapsulates a synchronous error handler', async t => {40 t.plan(3)41 42 const fastify = Fastify()43 fastify.register(async function (fastify) {44 fastify.setErrorHandler(function a (err) {45 // 3. the inner error handler catches the error, and throws a new error46 t.assert.strictEqual(err.message, 'from_endpoint')47 throw new Error('from_inner')48 })49 fastify.get('/encapsulated', async () => {50 // 2. the endpoint throws an error51 throw new Error('from_endpoint')52 })53 })54 55 fastify.setErrorHandler(async function b (err) {56 // 4. the outer error handler catches the error thrown by the inner error handler57 t.assert.strictEqual(err.message, 'from_inner')58 // 5. the outer error handler throws a new error59 throw new Error('from_outer')60 })61 62 // 1. the endpoint is called63 const res = await fastify.inject('/encapsulated')64 // 6. the default error handler returns the error from the outer error handler65 t.assert.strictEqual(res.json().message, 'from_outer')66})67 68test('onError hook nested', async t => {69 t.plan(4)70 71 const fastify = Fastify()72 fastify.register(async function (fastify) {73 fastify.setErrorHandler(async function a (err) {74 // 4. the inner error handler catches the error, and throws a new error75 t.assert.strictEqual(err.message, 'from_endpoint')76 throw new Error('from_inner')77 })78 fastify.get('/encapsulated', async () => {79 // 2. the endpoint throws an error80 throw new Error('from_endpoint')81 })82 })83 84 fastify.setErrorHandler(async function b (err) {85 // 5. the outer error handler catches the error thrown by the inner error handler86 t.assert.strictEqual(err.message, 'from_inner')87 // 6. the outer error handler throws a new error88 throw new Error('from_outer')89 })90 91 fastify.addHook('onError', async function (request, reply, err) {92 // 3. the hook receives the error93 t.assert.strictEqual(err.message, 'from_endpoint')94 })95 96 // 1. the endpoint is called97 const res = await fastify.inject('/encapsulated')98 // 7. the default error handler returns the error from the outer error handler99 t.assert.strictEqual(res.json().message, 'from_outer')100})101 102// See https://github.com/fastify/fastify/issues/5220103test('encapuslates an error handler, for errors thrown in hooks', async t => {104 t.plan(3)105 106 const fastify = Fastify()107 fastify.register(async function (fastify) {108 fastify.setErrorHandler(function a (err) {109 // 3. the inner error handler catches the error, and throws a new error110 t.assert.strictEqual(err.message, 'from_hook')111 throw new Error('from_inner')112 })113 fastify.addHook('onRequest', async () => {114 // 2. the hook throws an error115 throw new Error('from_hook')116 })117 fastify.get('/encapsulated', async () => {})118 })119 120 fastify.setErrorHandler(function b (err) {121 // 4. the outer error handler catches the error thrown by the inner error handler122 t.assert.strictEqual(err.message, 'from_inner')123 // 5. the outer error handler throws a new error124 throw new Error('from_outer')125 })126 127 // 1. the endpoint is called128 const res = await fastify.inject('/encapsulated')129 // 6. the default error handler returns the error from the outer error handler130 t.assert.strictEqual(res.json().message, 'from_outer')131})132 133// See https://github.com/fastify/fastify/issues/5220134test('encapuslates many synchronous error handlers that rethrow errors', async t => {135 const DEPTH = 100136 t.plan(DEPTH + 2)137 138 /**139 * This creates a very nested set of error handlers, that looks like:140 * plugin141 * - error handler142 * - plugin143 * - error handler144 * - plugin145 * ... {to DEPTH levels}146 * - plugin147 * - error handler148 * - GET /encapsulated149 */150 const createNestedRoutes = (fastify, depth) => {151 if (depth < 0) {152 throw new Error('Expected depth >= 0')153 } else if (depth === 0) {154 fastify.setErrorHandler(function a (err) {155 // 3. innermost error handler catches the error, and throws a new error156 t.assert.strictEqual(err.message, 'from_route')157 throw new Error(`from_handler_${depth}`)158 })159 fastify.get('/encapsulated', async () => {160 // 2. the endpoint throws an error161 throw new Error('from_route')162 })163 } else {164 fastify.setErrorHandler(function d (err) {165 // 4 to {DEPTH+4}. error handlers each catch errors, and then throws a new error166 t.assert.strictEqual(err.message, `from_handler_${depth - 1}`)167 throw new Error(`from_handler_${depth}`)168 })169 170 fastify.register(async function (fastify) {171 createNestedRoutes(fastify, depth - 1)172 })173 }174 }175 176 const fastify = Fastify()177 createNestedRoutes(fastify, DEPTH)178 179 // 1. the endpoint is called180 const res = await fastify.inject('/encapsulated')181 // {DEPTH+5}. the default error handler returns the error from the outermost error handler182 t.assert.strictEqual(res.json().message, `from_handler_${DEPTH}`)183})184 185// See https://github.com/fastify/fastify/issues/5220186// This was not failing previously, but we want to make sure the behavior continues to work in the same way across async and sync handlers187// Plus, the current setup is somewhat fragile to tweaks to wrapThenable as that's what retries (by calling res.send(err) again)188test('encapuslates many asynchronous error handlers that rethrow errors', async t => {189 const DEPTH = 100190 t.plan(DEPTH + 2)191 192 /**193 * This creates a very nested set of error handlers, that looks like:194 * plugin195 * - error handler196 * - plugin197 * - error handler198 * - plugin199 * ... {to DEPTH levels}200 * - plugin201 * - error handler202 * - GET /encapsulated203 */204 const createNestedRoutes = (fastify, depth) => {205 if (depth < 0) {206 throw new Error('Expected depth >= 0')207 } else if (depth === 0) {208 fastify.setErrorHandler(async function a (err) {209 // 3. innermost error handler catches the error, and throws a new error210 t.assert.strictEqual(err.message, 'from_route')211 throw new Error(`from_handler_${depth}`)212 })213 fastify.get('/encapsulated', async () => {214 // 2. the endpoint throws an error215 throw new Error('from_route')216 })217 } else {218 fastify.setErrorHandler(async function m (err) {219 // 4 to {DEPTH+4}. error handlers each catch errors, and then throws a new error220 t.assert.strictEqual(err.message, `from_handler_${depth - 1}`)221 throw new Error(`from_handler_${depth}`)222 })223 224 fastify.register(async function (fastify) {225 createNestedRoutes(fastify, depth - 1)226 })227 }228 }229 230 const fastify = Fastify()231 createNestedRoutes(fastify, DEPTH)232 233 // 1. the endpoint is called234 const res = await fastify.inject('/encapsulated')235 // {DEPTH+5}. the default error handler returns the error from the outermost error handler236 t.assert.strictEqual(res.json().message, `from_handler_${DEPTH}`)237})238 