Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dependencies": {
"@sentry/node": "latest || *",
"@types/node": "^18.19.1",
"fastify": "5.3.2",
"fastify": "^5.7.0",
"typescript": "5.6.3",
"ts-node": "10.9.2"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getPlaywrightConfig } from '@sentry-internal/test-utils';

const config = getPlaywrightConfig({
startCommand: `pnpm start`,
startCommand: `pnpm start:override`,
});

export default config;
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ Sentry.setupFastifyErrorHandler(app, {
return false;
}

// @ts-ignore // Fastify V5 is not typed correctly
if (_request.routeOptions?.url?.includes('/test-error-ignored') && _reply.statusCode === 500) {
return false;
}

return true;
},
});
Expand Down Expand Up @@ -130,10 +135,30 @@ app.get('/test-outgoing-http-external-disallowed', async function (req, res) {
res.send(data);
});

// Regression test for https://github.com/fastify/fastify/issues/6409
// The error diagnostic channel was always sending 200 unless explicitly changed.
// This was fixed in Fastify 5.7.0
app.register((childApp: F.FastifyInstance, _options: F.FastifyPluginOptions, next: (err?: Error) => void) => {
childApp.setErrorHandler((error: Error, _request: F.FastifyRequest, reply: F.FastifyReply) => {
reply.send({ ok: false });
});

childApp.get('/test-error-ignored', async function () {
throw new Error('This is an error that will not be captured');
});

next();
});

app.post('/test-post', function (req, res) {
res.send({ status: 'ok', body: req.body });
});

app.get('/flush', async function (_req, res) {
await Sentry.flush();
res.send({ ok: true });
});

app.listen({ port: port });

// A second app so we can test header propagation between external URLs
Expand Down
25 changes: 25 additions & 0 deletions dev-packages/e2e-tests/test-applications/node-fastify-5/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ Sentry.init({
return false;
}

// @ts-ignore // Fastify V5 is not typed correctly
if (_request.routeOptions?.url?.includes('/test-error-ignored') && _reply.statusCode === 500) {
return false;
}

return true;
},
}),
Expand Down Expand Up @@ -90,6 +95,21 @@ app.get('/test-error', async function (req, res) {
res.send({ exceptionId });
});

// Regression test for https://github.com/fastify/fastify/issues/6409
// The error diagnostic channel was always sending 200 unless explicitly changed.
// This was fixed in Fastify 5.7.0
app.register((childApp: F.FastifyInstance, _options: F.FastifyPluginOptions, next: (err?: Error) => void) => {
childApp.setErrorHandler((error: Error, _request: F.FastifyRequest, reply: F.FastifyReply) => {
reply.send({ ok: false });
});

childApp.get('/test-error-ignored', async function () {
throw new Error('This is an error that will not be captured');
});

next();
});

app.get('/test-error-not-captured', async function () {
// This error will not be captured by Sentry
throw new Error('This is an error that will not be captured');
Expand Down Expand Up @@ -127,6 +147,11 @@ app.post('/test-post', function (req, res) {
res.send({ status: 'ok', body: req.body });
});

app.get('/flush', async function (_req, res) {
await Sentry.flush();
res.send({ ok: true });
});

app.listen({ port: port });

// A second app so we can test header propagation between external URLs
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { waitForError } from '@sentry-internal/test-utils';
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';

test('Sends correct error event', async ({ baseURL }) => {
const errorEventPromise = waitForError('node-fastify-5', event => {
Expand Down Expand Up @@ -35,16 +35,54 @@ test('Sends correct error event', async ({ baseURL }) => {
});

test('Does not send error when shouldHandleError returns false', async ({ baseURL }) => {
const errorEventPromise = waitForError('node-fastify-5', event => {
return !event.type && event.exception?.values?.[0]?.value === 'This is an error that will not be captured';
let errorEventOccurred = false;

waitForError('node-fastify-5', event => {
if (!event.type && event.exception?.values?.[0]?.value === 'This is an error that will not be captured') {
errorEventOccurred = true;
}
return event?.transaction === 'GET /test-error-not-captured';
});

const transactionEventPromise = waitForTransaction('node-fastify-5', transactionEvent => {
return transactionEvent?.transaction === 'GET /test-error-not-captured';
});

const response = await fetch(`${baseURL}/test-error-not-captured`);

await transactionEventPromise;

const flushResponse = await fetch(`${baseURL}/flush`);

expect(response.status).toBe(500);
expect(flushResponse.status).toBe(200);
expect(errorEventOccurred).toBe(false);
});

// Regression test for https://github.com/fastify/fastify/issues/6409
// The error diagnostic channel was always sending 200 unless explicitly changed.
// This was fixed in Fastify 5.7.0
test('Error in child plugin with rethrown error handler reports correct 500 status', async ({ baseURL }) => {
let errorEventOccurred = false;

waitForError('node-fastify-5', event => {
if (!event.type && event.exception?.values?.[0]?.value === 'This is an error that will not be captured') {
errorEventOccurred = true;
}
return event?.transaction === 'GET /test-error-ignored';
});

errorEventPromise.then(() => {
throw new Error('This error should not be captured');
const transactionEventPromise = waitForTransaction('node-fastify-5', transactionEvent => {
return transactionEvent?.transaction === 'GET /test-error-ignored';
});

await fetch(`${baseURL}/test-error-not-captured`);
const response = await fetch(`${baseURL}/test-error-ignored`);

await transactionEventPromise;

const flushResponse = await fetch(`${baseURL}/flush`);

// wait for a short time to ensure the error is not captured
await new Promise(resolve => setTimeout(resolve, 1000));
expect(response.status).toBe(500);
expect(flushResponse.status).toBe(200);
expect(errorEventOccurred).toBe(false);
});
Loading